1414
1515package com .google .firebase .firestore .core ;
1616
17+ import static com .google .firebase .firestore .model .Values .max ;
18+ import static com .google .firebase .firestore .model .Values .min ;
19+
1720import androidx .annotation .Nullable ;
1821import com .google .firebase .firestore .core .OrderBy .Direction ;
1922import com .google .firebase .firestore .model .DocumentKey ;
@@ -121,25 +124,29 @@ public boolean hasLimit() {
121124 */
122125 public Bound getLowerBound (FieldIndex fieldIndex ) {
123126 List <Value > values = new ArrayList <>();
124- boolean before = true ;
127+ boolean inclusive = true ;
125128
126129 // Go through all filters to find a value for the current field segment
127130 for (FieldIndex .Segment segment : fieldIndex ) {
128- Value lowestValue = Values .NULL_VALUE ;
131+ Value segmentValue = Values .NULL_VALUE ;
132+ boolean segmentInclusive = true ;
133+
129134 for (Filter filter : filters ) {
130135 if (filter .getField ().equals (segment .getFieldPath ())) {
131136 FieldFilter fieldFilter = (FieldFilter ) filter ;
137+ Value filterValue = null ;
138+ boolean filterInclusive = true ;
139+
132140 switch (fieldFilter .getOperator ()) {
133141 case LESS_THAN :
134142 case LESS_THAN_OR_EQUAL :
135- // TODO(indexing): Implement type clamping. Only field values with the same type
136- // should match the query.
143+ filterValue = Values .getLowerBound (fieldFilter .getValue ().getValueTypeCase ());
137144 break ;
138145 case NOT_EQUAL :
139- // These filters cannot be used as a lower bound. Skip.
146+ filterValue = Values . NULL_VALUE ;
140147 break ;
141148 case NOT_IN :
142- lowestValue =
149+ filterValue =
143150 Value .newBuilder ()
144151 .setArrayValue (ArrayValue .newBuilder ().addValues (Values .NULL_VALUE ))
145152 .build ();
@@ -149,13 +156,18 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
149156 case ARRAY_CONTAINS_ANY :
150157 case ARRAY_CONTAINS :
151158 case GREATER_THAN_OR_EQUAL :
152- lowestValue = fieldFilter .getValue ();
159+ filterValue = fieldFilter .getValue ();
153160 break ;
154161 case GREATER_THAN :
155- lowestValue = fieldFilter .getValue ();
156- before = false ;
162+ filterValue = fieldFilter .getValue ();
163+ filterInclusive = false ;
157164 break ;
158165 }
166+
167+ if (max (segmentValue , filterValue ) == filterValue ) {
168+ segmentValue = filterValue ;
169+ segmentInclusive = filterInclusive ;
170+ }
159171 }
160172 }
161173
@@ -166,20 +178,20 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
166178 OrderBy orderBy = this .orderBys .get (i );
167179 if (orderBy .getField ().equals (segment .getFieldPath ())) {
168180 Value cursorValue = startAt .getPosition ().get (i );
169- if (Values .compare (lowestValue , cursorValue ) <= 0 ) {
170- lowestValue = cursorValue ;
171- // `before` is shared by all cursor values. If any cursor value is used, we set before
172- // to the cursor's value.
173- before = startAt .isBefore ();
181+ if (max (segmentValue , cursorValue ) == cursorValue ) {
182+ segmentValue = cursorValue ;
183+ segmentInclusive = startAt .isBefore ();
174184 }
175185 break ;
176186 }
177187 }
178188 }
179- values .add (lowestValue );
189+
190+ values .add (segmentValue );
191+ inclusive &= segmentInclusive ;
180192 }
181193
182- return new Bound (values , before );
194+ return new Bound (values , inclusive );
183195 }
184196
185197 /**
@@ -192,38 +204,46 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
192204 */
193205 public @ Nullable Bound getUpperBound (FieldIndex fieldIndex ) {
194206 List <Value > values = new ArrayList <>();
195- boolean before = false ;
207+ boolean inclusive = true ;
196208
197209 for (FieldIndex .Segment segment : fieldIndex ) {
198- @ Nullable Value largestValue = null ;
210+ @ Nullable Value segmentValue = null ;
211+ boolean segmentInclusive = true ;
199212
200213 // Go through all filters to find a value for the current field segment
201214 for (Filter filter : filters ) {
202215 if (filter .getField ().equals (segment .getFieldPath ())) {
203216 FieldFilter fieldFilter = (FieldFilter ) filter ;
217+ Value filterValue = null ;
218+ boolean filterInclusive = true ;
219+
204220 switch (fieldFilter .getOperator ()) {
205221 case NOT_IN :
206222 case NOT_EQUAL :
207223 // These filters cannot be used as an upper bound. Skip.
208224 break ;
209225 case GREATER_THAN_OR_EQUAL :
210226 case GREATER_THAN :
211- // TODO(indexing): Implement type clamping. Only field values with the same type
212- // should match the query.
227+ filterValue = Values . getUpperBound ( fieldFilter . getValue (). getValueTypeCase ());
228+ filterInclusive = false ;
213229 break ;
214230 case EQUAL :
215231 case IN :
216232 case ARRAY_CONTAINS_ANY :
217233 case ARRAY_CONTAINS :
218234 case LESS_THAN_OR_EQUAL :
219- largestValue = fieldFilter .getValue ();
220- before = false ;
235+ filterValue = fieldFilter .getValue ();
221236 break ;
222237 case LESS_THAN :
223- largestValue = fieldFilter .getValue ();
224- before = true ;
238+ filterValue = fieldFilter .getValue ();
239+ filterInclusive = false ;
225240 break ;
226241 }
242+
243+ if (min (segmentValue , filterValue ) == filterValue ) {
244+ segmentValue = filterValue ;
245+ segmentInclusive = filterInclusive ;
246+ }
227247 }
228248 }
229249
@@ -234,28 +254,29 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
234254 OrderBy orderBy = this .orderBys .get (i );
235255 if (orderBy .getField ().equals (segment .getFieldPath ())) {
236256 Value cursorValue = endAt .getPosition ().get (i );
237- if (largestValue == null || Values . compare ( largestValue , cursorValue ) > 0 ) {
238- largestValue = cursorValue ;
239- before = endAt .isBefore ();
257+ if (min ( segmentValue , cursorValue ) == cursorValue ) {
258+ segmentValue = cursorValue ;
259+ segmentInclusive = ! endAt .isBefore ();
240260 }
241261 break ;
242262 }
243263 }
244264 }
245265
246- if (largestValue == null ) {
266+ if (segmentValue == null ) {
247267 // No upper bound exists
248268 return null ;
249269 }
250270
251- values .add (largestValue );
271+ values .add (segmentValue );
272+ inclusive &= segmentInclusive ;
252273 }
253274
254275 if (values .isEmpty ()) {
255276 return null ;
256277 }
257278
258- return new Bound (values , before );
279+ return new Bound (values , ! inclusive );
259280 }
260281
261282 public List <OrderBy > getOrderBy () {
0 commit comments