@@ -710,9 +710,6 @@ abstract class FilterInternal {
710
710
/** Returns a list of all filters that are contained within this filter */
711
711
abstract getFilters ( ) : FilterInternal [ ] ;
712
712
713
- /** Returns the field of the first filter that's an inequality, or null if none. */
714
- abstract getFirstInequalityField ( ) : FieldPath | null ;
715
-
716
713
/** Returns the proto representation of this filter */
717
714
abstract toProto ( ) : Filter ;
718
715
@@ -735,13 +732,6 @@ class CompositeFilterInternal extends FilterInternal {
735
732
return this . filters ;
736
733
}
737
734
738
- public getFirstInequalityField ( ) : FieldPath | null {
739
- return (
740
- this . getFlattenedFilters ( ) . find ( filter => filter . isInequalityFilter ( ) )
741
- ?. field ?? null
742
- ) ;
743
- }
744
-
745
735
public isConjunction ( ) : boolean {
746
736
return this . operator === 'AND' ;
747
737
}
@@ -807,14 +797,6 @@ class FieldFilterInternal extends FilterInternal {
807
797
return [ this ] ;
808
798
}
809
799
810
- getFirstInequalityField ( ) : FieldPath | null {
811
- if ( this . isInequalityFilter ( ) ) {
812
- return this . field ;
813
- } else {
814
- return null ;
815
- }
816
- }
817
-
818
800
/**
819
801
* @param serializer The Firestore serializer
820
802
* @param field The path of the property value to compare.
@@ -843,6 +825,8 @@ class FieldFilterInternal extends FilterInternal {
843
825
case 'GREATER_THAN_OR_EQUAL' :
844
826
case 'LESS_THAN' :
845
827
case 'LESS_THAN_OR_EQUAL' :
828
+ case 'NOT_EQUAL' :
829
+ case 'NOT_IN' :
846
830
return true ;
847
831
default :
848
832
return false ;
@@ -1821,6 +1805,25 @@ export class Query<T = firestore.DocumentData> implements firestore.Query<T> {
1821
1805
) ;
1822
1806
}
1823
1807
1808
+ /**
1809
+ * Returns the sorted array of inequality filter fields used in this query.
1810
+ *
1811
+ * @return An array of inequality filter fields sorted lexicographically by FieldPath.
1812
+ */
1813
+ private getInequalityFilterFields ( ) : FieldPath [ ] {
1814
+ const inequalityFields : FieldPath [ ] = [ ] ;
1815
+
1816
+ for ( const filter of this . _queryOptions . filters ) {
1817
+ for ( const subFilter of filter . getFlattenedFilters ( ) ) {
1818
+ if ( subFilter . isInequalityFilter ( ) ) {
1819
+ inequalityFields . push ( subFilter . field ) ;
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ return inequalityFields . sort ( ( a , b ) => a . compareTo ( b ) ) ;
1825
+ }
1826
+
1824
1827
/**
1825
1828
* Computes the backend ordering semantics for DocumentSnapshot cursors.
1826
1829
*
@@ -1855,29 +1858,36 @@ export class Query<T = firestore.DocumentData> implements firestore.Query<T> {
1855
1858
}
1856
1859
1857
1860
const fieldOrders = this . _queryOptions . fieldOrders . slice ( ) ;
1858
-
1859
- // If no explicit ordering is specified, use the first inequality to
1860
- // define an implicit order.
1861
- if ( fieldOrders . length === 0 ) {
1862
- for ( const filter of this . _queryOptions . filters ) {
1863
- const fieldReference = filter . getFirstInequalityField ( ) ;
1864
- if ( fieldReference !== null ) {
1865
- fieldOrders . push ( new FieldOrder ( fieldReference ) ) ;
1866
- break ;
1867
- }
1861
+ const fieldsNormalized = new Set ( [
1862
+ ...fieldOrders . map ( item => item . field . toString ( ) ) ,
1863
+ ] ) ;
1864
+
1865
+ /** The order of the implicit ordering always matches the last explicit order by. */
1866
+ const lastDirection =
1867
+ fieldOrders . length === 0
1868
+ ? directionOperators . ASC
1869
+ : fieldOrders [ fieldOrders . length - 1 ] . direction ;
1870
+
1871
+ /**
1872
+ * Any inequality fields not explicitly ordered should be implicitly ordered in a
1873
+ * lexicographical order. When there are multiple inequality filters on the same field, the
1874
+ * field should be added only once.
1875
+ * Note: getInequalityFilterFields function sorts the key field before
1876
+ * other fields. However, we want the key field to be sorted last.
1877
+ */
1878
+ const inequalityFields = this . getInequalityFilterFields ( ) ;
1879
+ for ( const field of inequalityFields ) {
1880
+ if (
1881
+ ! fieldsNormalized . has ( field . toString ( ) ) &&
1882
+ ! field . isEqual ( FieldPath . documentId ( ) )
1883
+ ) {
1884
+ fieldOrders . push ( new FieldOrder ( field , lastDirection ) ) ;
1885
+ fieldsNormalized . add ( field . toString ( ) ) ;
1868
1886
}
1869
1887
}
1870
1888
1871
- const hasDocumentId = ! ! fieldOrders . find ( fieldOrder =>
1872
- FieldPath . documentId ( ) . isEqual ( fieldOrder . field )
1873
- ) ;
1874
- if ( ! hasDocumentId ) {
1875
- // Add implicit sorting by name, using the last specified direction.
1876
- const lastDirection =
1877
- fieldOrders . length === 0
1878
- ? directionOperators . ASC
1879
- : fieldOrders [ fieldOrders . length - 1 ] . direction ;
1880
-
1889
+ // Add the document key field to the last if it is not explicitly ordered.
1890
+ if ( ! fieldsNormalized . has ( FieldPath . documentId ( ) . toString ( ) ) ) {
1881
1891
fieldOrders . push ( new FieldOrder ( FieldPath . documentId ( ) , lastDirection ) ) ;
1882
1892
}
1883
1893
0 commit comments