@@ -19,13 +19,14 @@ import { compareDocumentsByField, Document } from '../model/document';
19
19
import { DocumentKey } from '../model/document_key' ;
20
20
import { FieldPath , ResourcePath } from '../model/path' ;
21
21
import { debugAssert , debugCast , fail } from '../util/assert' ;
22
+ import { SortedSet } from '../util/sorted_set' ;
22
23
23
24
import {
24
25
Bound ,
25
26
boundSortsAfterDocument ,
26
27
boundSortsBeforeDocument
27
28
} from './bound' ;
28
- import { Filter } from './filter' ;
29
+ import { FieldFilter , Filter } from './filter' ;
29
30
import { Direction , OrderBy } from './order_by' ;
30
31
import {
31
32
canonifyTarget ,
@@ -172,21 +173,18 @@ export function queryMatchesAllDocuments(query: Query): boolean {
172
173
) ;
173
174
}
174
175
175
- export function getFirstOrderByField ( query : Query ) : FieldPath | null {
176
- return query . explicitOrderBy . length > 0
177
- ? query . explicitOrderBy [ 0 ] . field
178
- : null ;
179
- }
180
-
181
- export function getInequalityFilterField ( query : Query ) : FieldPath | null {
182
- for ( const filter of query . filters ) {
183
- const result = filter . getFirstInequalityField ( ) ;
184
- if ( result !== null ) {
185
- return result ;
186
- }
187
- }
188
-
189
- return null ;
176
+ // Returns the sorted set of inequality filter fields used in this query.
177
+ export function getInequalityFilterFields ( query : Query ) : SortedSet < FieldPath > {
178
+ let result = new SortedSet < FieldPath > ( FieldPath . comparator ) ;
179
+ query . filters . forEach ( ( filter : Filter ) => {
180
+ const subFilters = filter . getFlattenedFilters ( ) ;
181
+ subFilters . forEach ( ( filter : FieldFilter ) => {
182
+ if ( filter . isInequality ( ) ) {
183
+ result = result . add ( filter . field ) ;
184
+ }
185
+ } ) ;
186
+ } ) ;
187
+ return result ;
190
188
}
191
189
192
190
/**
@@ -228,45 +226,43 @@ export function queryNormalizedOrderBy(query: Query): OrderBy[] {
228
226
const queryImpl = debugCast ( query , QueryImpl ) ;
229
227
if ( queryImpl . memoizedNormalizedOrderBy === null ) {
230
228
queryImpl . memoizedNormalizedOrderBy = [ ] ;
229
+ const fieldsNormalized = new Set < string > ( ) ;
231
230
232
- const inequalityField = getInequalityFilterField ( queryImpl ) ;
233
- const firstOrderByField = getFirstOrderByField ( queryImpl ) ;
234
- if ( inequalityField !== null && firstOrderByField === null ) {
235
- // In order to implicitly add key ordering, we must also add the
236
- // inequality filter field for it to be a valid query.
237
- // Note that the default inequality field and key ordering is ascending.
238
- if ( ! inequalityField . isKeyField ( ) ) {
239
- queryImpl . memoizedNormalizedOrderBy . push ( new OrderBy ( inequalityField ) ) ;
231
+ // Any explicit order by fields should be added as is.
232
+ for ( const orderBy of queryImpl . explicitOrderBy ) {
233
+ queryImpl . memoizedNormalizedOrderBy . push ( orderBy ) ;
234
+ fieldsNormalized . add ( orderBy . field . canonicalString ( ) ) ;
235
+ }
236
+
237
+ // The order of the implicit ordering always matches the last explicit order by.
238
+ const lastDirection =
239
+ queryImpl . explicitOrderBy . length > 0
240
+ ? queryImpl . explicitOrderBy [ queryImpl . explicitOrderBy . length - 1 ] . dir
241
+ : Direction . ASCENDING ;
242
+
243
+ // Any inequality fields not explicitly ordered should be implicitly ordered in a lexicographical
244
+ // order. When there are multiple inequality filters on the same field, the field should be added
245
+ // only once.
246
+ // Note: `SortedSet<FieldPath>` sorts the key field before other fields. However, we want the key
247
+ // field to be sorted last.
248
+ const inequalityFields : SortedSet < FieldPath > =
249
+ getInequalityFilterFields ( queryImpl ) ;
250
+ inequalityFields . forEach ( field => {
251
+ if (
252
+ ! fieldsNormalized . has ( field . canonicalString ( ) ) &&
253
+ ! field . isKeyField ( )
254
+ ) {
255
+ queryImpl . memoizedNormalizedOrderBy ! . push (
256
+ new OrderBy ( field , lastDirection )
257
+ ) ;
240
258
}
259
+ } ) ;
260
+
261
+ // Add the document key field to the last if it is not explicitly ordered.
262
+ if ( ! fieldsNormalized . has ( FieldPath . keyField ( ) . canonicalString ( ) ) ) {
241
263
queryImpl . memoizedNormalizedOrderBy . push (
242
- new OrderBy ( FieldPath . keyField ( ) , Direction . ASCENDING )
243
- ) ;
244
- } else {
245
- debugAssert (
246
- inequalityField === null ||
247
- ( firstOrderByField !== null &&
248
- inequalityField . isEqual ( firstOrderByField ) ) ,
249
- 'First orderBy should match inequality field.'
264
+ new OrderBy ( FieldPath . keyField ( ) , lastDirection )
250
265
) ;
251
- let foundKeyOrdering = false ;
252
- for ( const orderBy of queryImpl . explicitOrderBy ) {
253
- queryImpl . memoizedNormalizedOrderBy . push ( orderBy ) ;
254
- if ( orderBy . field . isKeyField ( ) ) {
255
- foundKeyOrdering = true ;
256
- }
257
- }
258
- if ( ! foundKeyOrdering ) {
259
- // The order of the implicit key ordering always matches the last
260
- // explicit order-by
261
- const lastDirection =
262
- queryImpl . explicitOrderBy . length > 0
263
- ? queryImpl . explicitOrderBy [ queryImpl . explicitOrderBy . length - 1 ]
264
- . dir
265
- : Direction . ASCENDING ;
266
- queryImpl . memoizedNormalizedOrderBy . push (
267
- new OrderBy ( FieldPath . keyField ( ) , lastDirection )
268
- ) ;
269
- }
270
266
}
271
267
}
272
268
return queryImpl . memoizedNormalizedOrderBy ;
@@ -350,16 +346,6 @@ function _queryToTarget(queryImpl: QueryImpl, orderBys: OrderBy[]): Target {
350
346
}
351
347
352
348
export function queryWithAddedFilter ( query : Query , filter : Filter ) : Query {
353
- const newInequalityField = filter . getFirstInequalityField ( ) ;
354
- const queryInequalityField = getInequalityFilterField ( query ) ;
355
-
356
- debugAssert (
357
- queryInequalityField == null ||
358
- newInequalityField == null ||
359
- newInequalityField . isEqual ( queryInequalityField ) ,
360
- 'Query must only have one inequality field.'
361
- ) ;
362
-
363
349
debugAssert (
364
350
! isDocumentQuery ( query ) ,
365
351
'No filtering allowed for document query'
0 commit comments