diff --git a/.changeset/sweet-rats-compete.md b/.changeset/sweet-rats-compete.md new file mode 100644 index 00000000000..0c10ba8bac5 --- /dev/null +++ b/.changeset/sweet-rats-compete.md @@ -0,0 +1,6 @@ +--- +"@firebase/firestore": minor +'firebase': minor +--- + +OR Query public API diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 153aadf3040..35802afacb7 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -46,6 +46,9 @@ export type AggregateSpecData = { [P in keyof T]: T[P] extends AggregateField ? U : never; }; +// @public +export function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; + // @public export function arrayRemove(...elements: unknown[]): FieldValue; @@ -234,6 +237,9 @@ export type NestedUpdateFields> = UnionToInter [K in keyof T & string]: ChildUpdateFields; }[keyof T & string]>; +// @public +export function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; + // @public export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDirection): QueryOrderByConstraint; @@ -258,9 +264,17 @@ export class Query { withConverter(converter: FirestoreDataConverter): Query; } +// @public +export function query(query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query; + // @public export function query(query: Query, ...queryConstraints: QueryConstraint[]): Query; +// @public +export class QueryCompositeFilterConstraint { + readonly type: 'or' | 'and'; +} + // @public export abstract class QueryConstraint { abstract readonly type: QueryConstraintType; @@ -288,6 +302,9 @@ export class QueryFieldFilterConstraint extends QueryConstraint { readonly type = "where"; } +// @public +export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; + // @public export class QueryLimitConstraint extends QueryConstraint { readonly type: 'limit' | 'limitToLast'; diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 35f887736f7..077e0e4426f 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -46,6 +46,9 @@ export type AggregateSpecData = { [P in keyof T]: T[P] extends AggregateField ? U : never; }; +// @public +export function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; + // @public export function arrayRemove(...elements: unknown[]): FieldValue; @@ -382,6 +385,9 @@ export function onSnapshotsInSync(firestore: Firestore, observer: { // @public export function onSnapshotsInSync(firestore: Firestore, onSync: () => void): Unsubscribe; +// @public +export function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; + // @public export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDirection): QueryOrderByConstraint; @@ -411,9 +417,17 @@ export class Query { withConverter(converter: FirestoreDataConverter): Query; } +// @public +export function query(query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query; + // @public export function query(query: Query, ...queryConstraints: QueryConstraint[]): Query; +// @public +export class QueryCompositeFilterConstraint { + readonly type: 'or' | 'and'; +} + // @public export abstract class QueryConstraint { abstract readonly type: QueryConstraintType; @@ -441,6 +455,9 @@ export class QueryFieldFilterConstraint extends QueryConstraint { readonly type = "where"; } +// @public +export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; + // @public export class QueryLimitConstraint extends QueryConstraint { readonly type: 'limit' | 'limitToLast'; diff --git a/docs-devsite/firestore_.md b/docs-devsite/firestore_.md index 058cdd7b281..9351f162119 100644 --- a/docs-devsite/firestore_.md +++ b/docs-devsite/firestore_.md @@ -75,7 +75,11 @@ https://github.com/firebase/firebase-js-sdk | [onSnapshot(query, options, observer)](./firestore_.md#onsnapshot) | Attaches a listener for QuerySnapshot events. You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | | [onSnapshot(query, onNext, onError, onCompletion)](./firestore_.md#onsnapshot) | Attaches a listener for QuerySnapshot events. You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | | [onSnapshot(query, options, onNext, onError, onCompletion)](./firestore_.md#onsnapshot) | Attaches a listener for QuerySnapshot events. You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [query(query, compositeFilter, queryConstraints)](./firestore_.md#query) | Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. | | [query(query, queryConstraints)](./firestore_.md#query) | Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. | +| function(queryConstraints...) | +| [and(queryConstraints)](./firestore_.md#and) | Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a conjunction of the given filter constraints. A conjunction filter includes a document if it satisfies all of the given filters. | +| [or(queryConstraints)](./firestore_.md#or) | Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a disjunction of the given filter constraints. A disjunction filter includes a document if it satisfies any of the given filters. | | function(reference...) | | [addDoc(reference, data)](./firestore_.md#adddoc) | Add a new document to specified CollectionReference with the given data, assigning it a document ID automatically. | | [collection(reference, path, pathSegments)](./firestore_.md#collection) | Gets a CollectionReference instance that refers to a subcollection of reference at the the specified relative path. | @@ -117,7 +121,8 @@ https://github.com/firebase/firebase-js-sdk | [GeoPoint](./firestore_.geopoint.md#geopoint_class) | An immutable object representing a geographic location in Firestore. The location is represented as latitude/longitude pair.Latitude values are in the range of \[-90, 90\]. Longitude values are in the range of \[-180, 180\]. | | [LoadBundleTask](./firestore_.loadbundletask.md#loadbundletask_class) | Represents the task of loading a Firestore bundle. It provides progress of bundle loading, as well as task completion and error events.The API is compatible with Promise<LoadBundleTaskProgress>. | | [Query](./firestore_.query.md#query_class) | A Query refers to a query which you can read or listen to. You can also construct refined Query objects by adding filters and ordering. | -| [QueryConstraint](./firestore_.queryconstraint.md#queryconstraint_class) | A QueryConstraint is used to narrow the set of documents returned by a Firestore query. QueryConstraints are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryConstraint. | +| [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) | A QueryCompositeFilterConstraint is used to narrow the set of documents returned by a Firestore query by performing the logical OR or AND of multiple [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class)s or [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class)s. QueryCompositeFilterConstraints are created by invoking [or()](./firestore_.md#or) or [and()](./firestore_.md#and) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryCompositeFilterConstraint. | +| [QueryConstraint](./firestore_.queryconstraint.md#queryconstraint_class) | A QueryConstraint is used to narrow the set of documents returned by a Firestore query. QueryConstraints are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryConstraint. | | [QueryDocumentSnapshot](./firestore_.querydocumentsnapshot.md#querydocumentsnapshot_class) | A QueryDocumentSnapshot contains data read from a document in your Firestore database as part of a query. The document is guaranteed to exist and its data can be extracted with .data() or .get(<field>) to get a specific field.A QueryDocumentSnapshot offers the same API surface as a DocumentSnapshot. Since query results contain only existing documents, the exists property will always be true and data() will never return 'undefined'. | | [QueryEndAtConstraint](./firestore_.queryendatconstraint.md#queryendatconstraint_class) | A QueryEndAtConstraint is used to exclude documents from the end of a result set returned by a Firestore query. QueryEndAtConstraints are created by invoking [endAt()](./firestore_.md#endat) or [endBefore()](./firestore_.md#endbefore) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryEndAtConstraint. | | [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) | A QueryFieldFilterConstraint is used to narrow the set of documents returned by a Firestore query by filtering on one or more document fields. QueryFieldFilterConstraints are created by invoking [where()](./firestore_.md#where) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryFieldFilterConstraint. | @@ -170,7 +175,8 @@ https://github.com/firebase/firebase-js-sdk | [PartialWithFieldValue](./firestore_.md#partialwithfieldvalue) | Similar to Typescript's Partial<T>, but allows nested fields to be omitted and FieldValues to be passed in as property values. | | [Primitive](./firestore_.md#primitive) | Primitive types. | | [QueryConstraintType](./firestore_.md#queryconstrainttype) | Describes the different query constraints available in this SDK. | -| [QueryNonFilterConstraint](./firestore_.md#querynonfilterconstraint) | QueryNonFilterConstraint is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. QueryNonFilterConstraints are created by invoking [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryConstraint. | +| [QueryFilterConstraint](./firestore_.md#queryfilterconstraint) | QueryFilterConstraint is a helper union type that represents [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) and [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). | +| [QueryNonFilterConstraint](./firestore_.md#querynonfilterconstraint) | QueryNonFilterConstraint is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. QueryNonFilterConstraints are created by invoking [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryConstraint. | | [SetOptions](./firestore_.md#setoptions) | An options object that configures the behavior of [setDoc()](./firestore_lite.md#setdoc), and calls. These calls can be configured to perform granular merges instead of overwriting the target documents in their entirety by providing a SetOptions with merge: true. | | [TaskState](./firestore_.md#taskstate) | Represents the state of bundle loading tasks.Both 'Error' and 'Success' are sinking state: task will abort or complete and there will be no more updates after they are reported. | | [UnionToIntersection](./firestore_.md#uniontointersection) | Given a union type U = T1 | T2 | ..., returns an intersected type (T1 & T2 & ...).Uses distributive conditional types and inference from conditional types. This works because multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred. https://www.typescriptlang.org/docs/handbook/advanced-types.html\#type-inference-in-conditional-types https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type | @@ -1374,6 +1380,32 @@ Creates a new immutable instance of [Query](./firestore_.query.md#query_class) t Signature: +```typescript +export declare function query(query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| query | [Query](./firestore_.query.md#query_class)<T> | The [Query](./firestore_.query.md#query_class) instance to use as a base for the new constraints. | +| compositeFilter | [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) | The [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) to apply. Create [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) using [and()](./firestore_.md#and) or [or()](./firestore_.md#or). | +| queryConstraints | [QueryNonFilterConstraint](./firestore_.md#querynonfilterconstraint)\[\] | Additional [QueryNonFilterConstraint](./firestore_.md#querynonfilterconstraint)s to apply (e.g. [orderBy()](./firestore_.md#orderby), [limit()](./firestore_.md#limit)). | + +Returns: + +[Query](./firestore_.query.md#query_class)<T> + +## Exceptions + +if any of the provided query constraints cannot be combined with the existing or new constraints. + +## query() + +Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. + +Signature: + ```typescript export declare function query(query: Query, ...queryConstraints: QueryConstraint[]): Query; ``` @@ -1393,6 +1425,50 @@ export declare function query(query: Query, ...queryConstraints: QueryCons if any of the provided query constraints cannot be combined with the existing or new constraints. +## and() + +Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a conjunction of the given filter constraints. A conjunction filter includes a document if it satisfies all of the given filters. + +Signature: + +```typescript +export declare function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryConstraints | [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)\[\] | Optional. The list of [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)s to perform a conjunction for. These must be created with calls to [where()](./firestore_.md#where), [or()](./firestore_.md#or), or [and()](./firestore_.md#and). | + +Returns: + +[QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) + +The newly created [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + +## or() + +Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a disjunction of the given filter constraints. A disjunction filter includes a document if it satisfies any of the given filters. + +Signature: + +```typescript +export declare function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryConstraints | [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)\[\] | Optional. The list of [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)s to perform a disjunction for. These must be created with calls to [where()](./firestore_.md#where), [or()](./firestore_.md#or), or [and()](./firestore_.md#and). | + +Returns: + +[QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) + +The newly created [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + ## addDoc() Add a new document to specified `CollectionReference` with the given data, assigning it a document ID automatically. @@ -2051,9 +2127,19 @@ Describes the different query constraints available in this SDK. export declare type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore'; ``` +## QueryFilterConstraint + +`QueryFilterConstraint` is a helper union type that represents [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) and [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + +Signature: + +```typescript +export declare type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; +``` + ## QueryNonFilterConstraint -`QueryNonFilterConstraint` is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. `QueryNonFilterConstraint`s are created by invoking [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryConstraint`. +`QueryNonFilterConstraint` is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. `QueryNonFilterConstraint`s are created by invoking [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryConstraint`. Signature: diff --git a/docs-devsite/firestore_.querycompositefilterconstraint.md b/docs-devsite/firestore_.querycompositefilterconstraint.md new file mode 100644 index 00000000000..7c82f21e25f --- /dev/null +++ b/docs-devsite/firestore_.querycompositefilterconstraint.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# QueryCompositeFilterConstraint class +A `QueryCompositeFilterConstraint` is used to narrow the set of documents returned by a Firestore query by performing the logical OR or AND of multiple [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class)s or [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class)s. `QueryCompositeFilterConstraint`s are created by invoking [or()](./firestore_.md#or) or [and()](./firestore_.md#and) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryCompositeFilterConstraint`. + +Signature: + +```typescript +export declare class QueryCompositeFilterConstraint +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [type](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstrainttype) | | 'or' \| 'and' | The type of this query constraint | + +## QueryCompositeFilterConstraint.type + +The type of this query constraint + +Signature: + +```typescript +readonly type: 'or' | 'and'; +``` diff --git a/docs-devsite/firestore_.queryconstraint.md b/docs-devsite/firestore_.queryconstraint.md index 77ebddf1c65..c51850d8dea 100644 --- a/docs-devsite/firestore_.queryconstraint.md +++ b/docs-devsite/firestore_.queryconstraint.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # QueryConstraint class -A `QueryConstraint` is used to narrow the set of documents returned by a Firestore query. `QueryConstraint`s are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this `QueryConstraint`. +A `QueryConstraint` is used to narrow the set of documents returned by a Firestore query. `QueryConstraint`s are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this `QueryConstraint`. Signature: diff --git a/docs-devsite/firestore_lite.md b/docs-devsite/firestore_lite.md index 3dc3dfe2388..b0ec302e722 100644 --- a/docs-devsite/firestore_lite.md +++ b/docs-devsite/firestore_lite.md @@ -57,7 +57,11 @@ https://github.com/firebase/firebase-js-sdk | function(query...) | | [getCount(query)](./firestore_lite.md#getcount) | Calculates the number of documents in the result set of the given query, without actually downloading the documents.Using this function to count the documents is efficient because only the final count, not the documents' data, is downloaded. This function can even count the documents if the result set would be prohibitively large to download entirely (e.g. thousands of documents). | | [getDocs(query)](./firestore_lite.md#getdocs) | Executes the query and returns the results as a [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class).All queries are executed directly by the server, even if the the query was previously executed. Recent modifications are only reflected in the retrieved results if they have already been applied by the backend. If the client is offline, the operation fails. To see previously cached result and local modifications, use the full Firestore SDK. | +| [query(query, compositeFilter, queryConstraints)](./firestore_lite.md#query) | Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. | | [query(query, queryConstraints)](./firestore_lite.md#query) | Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. | +| function(queryConstraints...) | +| [and(queryConstraints)](./firestore_lite.md#and) | Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a conjunction of the given filter constraints. A conjunction filter includes a document if it satisfies all of the given filters. | +| [or(queryConstraints)](./firestore_lite.md#or) | Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a disjunction of the given filter constraints. A disjunction filter includes a document if it satisfies any of the given filters. | | function(reference...) | | [addDoc(reference, data)](./firestore_lite.md#adddoc) | Add a new document to specified CollectionReference with the given data, assigning it a document ID automatically.The result of this write will only be reflected in document reads that occur after the returned promise resolves. If the client is offline, the write fails. If you would like to see local modifications or buffer writes until the client is online, use the full Firestore SDK. | | [collection(reference, path, pathSegments)](./firestore_lite.md#collection) | Gets a CollectionReference instance that refers to a subcollection of reference at the the specified relative path. | @@ -92,7 +96,8 @@ https://github.com/firebase/firebase-js-sdk | [FirestoreError](./firestore_lite.firestoreerror.md#firestoreerror_class) | An error returned by a Firestore operation. | | [GeoPoint](./firestore_lite.geopoint.md#geopoint_class) | An immutable object representing a geographic location in Firestore. The location is represented as latitude/longitude pair.Latitude values are in the range of \[-90, 90\]. Longitude values are in the range of \[-180, 180\]. | | [Query](./firestore_lite.query.md#query_class) | A Query refers to a query which you can read or listen to. You can also construct refined Query objects by adding filters and ordering. | -| [QueryConstraint](./firestore_lite.queryconstraint.md#queryconstraint_class) | A QueryConstraint is used to narrow the set of documents returned by a Firestore query. QueryConstraints are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryConstraint. | +| [QueryCompositeFilterConstraint](./firestore_lite.querycompositefilterconstraint.md#querycompositefilterconstraint_class) | A QueryCompositeFilterConstraint is used to narrow the set of documents returned by a Firestore query by performing the logical OR or AND of multiple [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class)s or [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class)s. QueryCompositeFilterConstraints are created by invoking [or()](./firestore_.md#or) or [and()](./firestore_.md#and) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryCompositeFilterConstraint. | +| [QueryConstraint](./firestore_lite.queryconstraint.md#queryconstraint_class) | A QueryConstraint is used to narrow the set of documents returned by a Firestore query. QueryConstraints are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryConstraint. | | [QueryDocumentSnapshot](./firestore_lite.querydocumentsnapshot.md#querydocumentsnapshot_class) | A QueryDocumentSnapshot contains data read from a document in your Firestore database as part of a query. The document is guaranteed to exist and its data can be extracted with .data() or .get(<field>) to get a specific field.A QueryDocumentSnapshot offers the same API surface as a DocumentSnapshot. Since query results contain only existing documents, the exists property will always be true and data() will never return 'undefined'. | | [QueryEndAtConstraint](./firestore_lite.queryendatconstraint.md#queryendatconstraint_class) | A QueryEndAtConstraint is used to exclude documents from the end of a result set returned by a Firestore query. QueryEndAtConstraints are created by invoking [endAt()](./firestore_.md#endat) or [endBefore()](./firestore_.md#endbefore) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryEndAtConstraint. | | [QueryFieldFilterConstraint](./firestore_lite.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) | A QueryFieldFilterConstraint is used to narrow the set of documents returned by a Firestore query by filtering on one or more document fields. QueryFieldFilterConstraints are created by invoking [where()](./firestore_.md#where) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this QueryFieldFilterConstraint. | @@ -128,7 +133,8 @@ https://github.com/firebase/firebase-js-sdk | [PartialWithFieldValue](./firestore_lite.md#partialwithfieldvalue) | Similar to Typescript's Partial<T>, but allows nested fields to be omitted and FieldValues to be passed in as property values. | | [Primitive](./firestore_lite.md#primitive) | Primitive types. | | [QueryConstraintType](./firestore_lite.md#queryconstrainttype) | Describes the different query constraints available in this SDK. | -| [QueryNonFilterConstraint](./firestore_lite.md#querynonfilterconstraint) | QueryNonFilterConstraint is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. QueryNonFilterConstraints are created by invoking [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryConstraint. | +| [QueryFilterConstraint](./firestore_lite.md#queryfilterconstraint) | QueryFilterConstraint is a helper union type that represents [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) and [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). | +| [QueryNonFilterConstraint](./firestore_lite.md#querynonfilterconstraint) | QueryNonFilterConstraint is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. QueryNonFilterConstraints are created by invoking [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the QueryConstraint. | | [SetOptions](./firestore_lite.md#setoptions) | An options object that configures the behavior of [setDoc()](./firestore_lite.md#setdoc), and calls. These calls can be configured to perform granular merges instead of overwriting the target documents in their entirety by providing a SetOptions with merge: true. | | [UnionToIntersection](./firestore_lite.md#uniontointersection) | Given a union type U = T1 | T2 | ..., returns an intersected type (T1 & T2 & ...).Uses distributive conditional types and inference from conditional types. This works because multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred. https://www.typescriptlang.org/docs/handbook/advanced-types.html\#type-inference-in-conditional-types https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type | | [UpdateData](./firestore_lite.md#updatedata) | Update data (for use with [updateDoc()](./firestore_.md#updatedoc)) that consists of field paths (e.g. 'foo' or 'foo.baz') mapped to values. Fields that contain dots reference nested fields within the document. FieldValues can be passed in as property values. | @@ -841,6 +847,32 @@ Creates a new immutable instance of [Query](./firestore_.query.md#query_class) t Signature: +```typescript +export declare function query(query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| query | [Query](./firestore_lite.query.md#query_class)<T> | The [Query](./firestore_.query.md#query_class) instance to use as a base for the new constraints. | +| compositeFilter | [QueryCompositeFilterConstraint](./firestore_lite.querycompositefilterconstraint.md#querycompositefilterconstraint_class) | The [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) to apply. Create [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) using [and()](./firestore_.md#and) or [or()](./firestore_.md#or). | +| queryConstraints | [QueryNonFilterConstraint](./firestore_lite.md#querynonfilterconstraint)\[\] | Additional [QueryNonFilterConstraint](./firestore_.md#querynonfilterconstraint)s to apply (e.g. [orderBy()](./firestore_.md#orderby), [limit()](./firestore_.md#limit)). | + +Returns: + +[Query](./firestore_lite.query.md#query_class)<T> + +## Exceptions + +if any of the provided query constraints cannot be combined with the existing or new constraints. + +## query() + +Creates a new immutable instance of [Query](./firestore_.query.md#query_class) that is extended to also include additional query constraints. + +Signature: + ```typescript export declare function query(query: Query, ...queryConstraints: QueryConstraint[]): Query; ``` @@ -860,6 +892,50 @@ export declare function query(query: Query, ...queryConstraints: QueryCons if any of the provided query constraints cannot be combined with the existing or new constraints. +## and() + +Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a conjunction of the given filter constraints. A conjunction filter includes a document if it satisfies all of the given filters. + +Signature: + +```typescript +export declare function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryConstraints | [QueryFilterConstraint](./firestore_lite.md#queryfilterconstraint)\[\] | Optional. The list of [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)s to perform a conjunction for. These must be created with calls to [where()](./firestore_.md#where), [or()](./firestore_.md#or), or [and()](./firestore_.md#and). | + +Returns: + +[QueryCompositeFilterConstraint](./firestore_lite.querycompositefilterconstraint.md#querycompositefilterconstraint_class) + +The newly created [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + +## or() + +Creates a new [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class) that is a disjunction of the given filter constraints. A disjunction filter includes a document if it satisfies any of the given filters. + +Signature: + +```typescript +export declare function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryConstraints | [QueryFilterConstraint](./firestore_lite.md#queryfilterconstraint)\[\] | Optional. The list of [QueryFilterConstraint](./firestore_.md#queryfilterconstraint)s to perform a disjunction for. These must be created with calls to [where()](./firestore_.md#where), [or()](./firestore_.md#or), or [and()](./firestore_.md#and). | + +Returns: + +[QueryCompositeFilterConstraint](./firestore_lite.querycompositefilterconstraint.md#querycompositefilterconstraint_class) + +The newly created [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + ## addDoc() Add a new document to specified `CollectionReference` with the given data, assigning it a document ID automatically. @@ -1372,9 +1448,19 @@ Describes the different query constraints available in this SDK. export declare type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore'; ``` +## QueryFilterConstraint + +`QueryFilterConstraint` is a helper union type that represents [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class) and [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class). + +Signature: + +```typescript +export declare type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; +``` + ## QueryNonFilterConstraint -`QueryNonFilterConstraint` is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. `QueryNonFilterConstraint`s are created by invoking [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryConstraint`. +`QueryNonFilterConstraint` is a helper union type that represents QueryConstraints which are used to narrow or order the set of documents, but that do not explicitly filter on a document field. `QueryNonFilterConstraint`s are created by invoking [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit) or [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryConstraint`. Signature: diff --git a/docs-devsite/firestore_lite.querycompositefilterconstraint.md b/docs-devsite/firestore_lite.querycompositefilterconstraint.md new file mode 100644 index 00000000000..b0bd12f96ec --- /dev/null +++ b/docs-devsite/firestore_lite.querycompositefilterconstraint.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# QueryCompositeFilterConstraint class +A `QueryCompositeFilterConstraint` is used to narrow the set of documents returned by a Firestore query by performing the logical OR or AND of multiple [QueryFieldFilterConstraint](./firestore_.queryfieldfilterconstraint.md#queryfieldfilterconstraint_class)s or [QueryCompositeFilterConstraint](./firestore_.querycompositefilterconstraint.md#querycompositefilterconstraint_class)s. `QueryCompositeFilterConstraint`s are created by invoking [or()](./firestore_.md#or) or [and()](./firestore_.md#and) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains the `QueryCompositeFilterConstraint`. + +Signature: + +```typescript +export declare class QueryCompositeFilterConstraint +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [type](./firestore_lite.querycompositefilterconstraint.md#querycompositefilterconstrainttype) | | 'or' \| 'and' | The type of this query constraint | + +## QueryCompositeFilterConstraint.type + +The type of this query constraint + +Signature: + +```typescript +readonly type: 'or' | 'and'; +``` diff --git a/docs-devsite/firestore_lite.queryconstraint.md b/docs-devsite/firestore_lite.queryconstraint.md index dae8d5f0069..f9d2d6ddb0a 100644 --- a/docs-devsite/firestore_lite.queryconstraint.md +++ b/docs-devsite/firestore_lite.queryconstraint.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # QueryConstraint class -A `QueryConstraint` is used to narrow the set of documents returned by a Firestore query. `QueryConstraint`s are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), , , , , [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this `QueryConstraint`. +A `QueryConstraint` is used to narrow the set of documents returned by a Firestore query. `QueryConstraint`s are created by invoking [where()](./firestore_.md#where), [orderBy()](./firestore_.md#orderby), [startAt()](./firestore_.md#startat), [startAfter()](./firestore_.md#startafter), [endBefore()](./firestore_.md#endbefore), [endAt()](./firestore_.md#endat), [limit()](./firestore_.md#limit), [limitToLast()](./firestore_.md#limittolast) and can then be passed to [query()](./firestore_.md#query) to create a new query instance that also contains this `QueryConstraint`. Signature: diff --git a/packages/firestore/src/lite-api/query.ts b/packages/firestore/src/lite-api/query.ts index 30e88927742..3afa282048f 100644 --- a/packages/firestore/src/lite-api/query.ts +++ b/packages/firestore/src/lite-api/query.ts @@ -101,9 +101,9 @@ export abstract class AppliableConstraint { /** * A `QueryConstraint` is used to narrow the set of documents returned by a * Firestore query. `QueryConstraint`s are created by invoking {@link where}, - * {@link orderBy}, {@link startAt}, {@link startAfter}, {@link - * endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and - * can then be passed to {@link query} to create a new query instance that + * {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link + * (endBefore:1)}, {@link (endAt:1)}, {@link limit}, {@link limitToLast} and + * can then be passed to {@link (query:1)} to create a new query instance that * also contains this `QueryConstraint`. */ export abstract class QueryConstraint extends AppliableConstraint { @@ -130,7 +130,6 @@ export abstract class QueryConstraint extends AppliableConstraint { * apply (e.g. {@link orderBy}, {@link limit}). * @throws if any of the provided query constraints cannot be combined with the * existing or new constraints. - * @internal TODO remove this internal tag with OR Query support in the server */ export function query( query: Query, @@ -180,7 +179,7 @@ export function query( * A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by * a Firestore query by filtering on one or more document fields. * `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then - * be passed to {@link query} to create a new query instance that also contains + * be passed to {@link (query:1)} to create a new query instance that also contains * this `QueryFieldFilterConstraint`. */ export class QueryFieldFilterConstraint extends QueryConstraint { @@ -274,9 +273,8 @@ export function where( * returned by a Firestore query by performing the logical OR or AND of multiple * {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s. * `QueryCompositeFilterConstraint`s are created by invoking {@link or} or - * {@link and} and can then be passed to {@link query} to create a new query + * {@link and} and can then be passed to {@link (query:1)} to create a new query * instance that also contains the `QueryCompositeFilterConstraint`. - * @internal TODO remove this internal tag with OR Query support in the server */ export class QueryCompositeFilterConstraint extends AppliableConstraint { /** @@ -341,8 +339,8 @@ export class QueryCompositeFilterConstraint extends AppliableConstraint { * QueryConstraints which are used to narrow or order the set of documents, * but that do not explicitly filter on a document field. * `QueryNonFilterConstraint`s are created by invoking {@link orderBy}, - * {@link startAt}, {@link startAfter}, {@link endBefore}, {@link endAt}, - * {@link limit} or {@link limitToLast} and can then be passed to {@link query} + * {@link (startAt:1)}, {@link (startAfter:1)}, {@link (endBefore:1)}, {@link (endAt:1)}, + * {@link limit} or {@link limitToLast} and can then be passed to {@link (query:1)} * to create a new query instance that also contains the `QueryConstraint`. */ export type QueryNonFilterConstraint = @@ -354,10 +352,6 @@ export type QueryNonFilterConstraint = /** * `QueryFilterConstraint` is a helper union type that represents * {@link QueryFieldFilterConstraint} and {@link QueryCompositeFilterConstraint}. - * `QueryFilterConstraint`s are created by invoking {@link or} or {@link and} - * and can then be passed to {@link query} to create a new query instance that - * also contains the `QueryConstraint`. - * @internal TODO remove this internal tag with OR Query support in the server */ export type QueryFilterConstraint = | QueryFieldFilterConstraint @@ -372,7 +366,6 @@ export type QueryFilterConstraint = * {@link QueryFilterConstraint}s to perform a disjunction for. These must be * created with calls to {@link where}, {@link or}, or {@link and}. * @returns The newly created {@link QueryCompositeFilterConstraint}. - * @internal TODO remove this internal tag with OR Query support in the server */ export function or( ...queryConstraints: QueryFilterConstraint[] @@ -397,7 +390,6 @@ export function or( * {@link QueryFilterConstraint}s to perform a conjunction for. These must be * created with calls to {@link where}, {@link or}, or {@link and}. * @returns The newly created {@link QueryCompositeFilterConstraint}. - * @internal TODO remove this internal tag with OR Query support in the server */ export function and( ...queryConstraints: QueryFilterConstraint[] @@ -416,7 +408,7 @@ export function and( /** * A `QueryOrderByConstraint` is used to sort the set of documents returned by a * Firestore query. `QueryOrderByConstraint`s are created by invoking - * {@link orderBy} and can then be passed to {@link query} to create a new query + * {@link orderBy} and can then be passed to {@link (query:1)} to create a new query * instance that also contains this `QueryOrderByConstraint`. * * Note: Documents that do not contain the orderBy field will not be present in @@ -484,7 +476,7 @@ export function orderBy( * A `QueryLimitConstraint` is used to limit the number of documents returned by * a Firestore query. * `QueryLimitConstraint`s are created by invoking {@link limit} or - * {@link limitToLast} and can then be passed to {@link query} to create a new + * {@link limitToLast} and can then be passed to {@link (query:1)} to create a new * query instance that also contains this `QueryLimitConstraint`. */ export class QueryLimitConstraint extends QueryConstraint { @@ -548,7 +540,7 @@ export function limitToLast(limit: number): QueryLimitConstraint { * A `QueryStartAtConstraint` is used to exclude documents from the start of a * result set returned by a Firestore query. * `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or - * {@link (startAfter:1)} and can then be passed to {@link query} to create a + * {@link (startAfter:1)} and can then be passed to {@link (query:1)} to create a * new query instance that also contains this `QueryStartAtConstraint`. */ export class QueryStartAtConstraint extends QueryConstraint { @@ -655,7 +647,7 @@ export function startAfter( * A `QueryEndAtConstraint` is used to exclude documents from the end of a * result set returned by a Firestore query. * `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or - * {@link (endBefore:1)} and can then be passed to {@link query} to create a new + * {@link (endBefore:1)} and can then be passed to {@link (query:1)} to create a new * query instance that also contains this `QueryEndAtConstraint`. */ export class QueryEndAtConstraint extends QueryConstraint { diff --git a/packages/firestore/src/lite-api/reference.ts b/packages/firestore/src/lite-api/reference.ts index 7ade1ec0714..0a9fdcf71cd 100644 --- a/packages/firestore/src/lite-api/reference.ts +++ b/packages/firestore/src/lite-api/reference.ts @@ -242,7 +242,7 @@ export class Query { /** * A `CollectionReference` object can be used for adding documents, getting - * document references, and querying for documents (using {@link query}). + * document references, and querying for documents (using {@link (query:1)}). */ export class CollectionReference extends Query { /** The type of this Firestore reference. */ diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index a92eef8443c..bec924e3fb9 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -1329,9 +1329,10 @@ apiDescribe('Queries', (persistence: boolean) => { }); }); - // TODO(orquery): Enable these tests when prod supports OR queries. + // OR Query tests only run when the SDK is configured for persistence + // because they validate that the result from server and cache match. // eslint-disable-next-line no-restricted-properties - (false && persistence ? describe : describe.skip)('OR Queries', () => { + (persistence ? describe : describe.skip)('OR Queries', () => { it('can use query overloads', () => { const testDocs = { doc1: { a: 1, b: 0 }, @@ -1369,13 +1370,6 @@ apiDescribe('Queries', (persistence: boolean) => { 'doc4' ); - // a == 1, limit 2, b - desc - await checkOnlineAndOfflineResultsMatch( - query(coll, where('a', '==', 1), limit(2), orderBy('b', 'desc')), - 'doc4', - 'doc5' - ); - // explicit OR: a == 1 || b == 1 with limit 2 await checkOnlineAndOfflineResultsMatch( query(coll, or(where('a', '==', 1), where('b', '==', 1)), limit(2)), @@ -1418,14 +1412,6 @@ apiDescribe('Queries', (persistence: boolean) => { 'doc5' ); - // with one inequality: a>2 || b==1. - await checkOnlineAndOfflineResultsMatch( - query(coll, or(where('a', '>', 2), where('b', '==', 1))), - 'doc5', - 'doc2', - 'doc3' - ); - // (a==1 && b==0) || (a==3 && b==2) await checkOnlineAndOfflineResultsMatch( query( @@ -1464,6 +1450,242 @@ apiDescribe('Queries', (persistence: boolean) => { 'doc3' ); + // Test with limits without orderBy (the __name__ ordering is the tie breaker). + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', '==', 2), where('b', '==', 1)), limit(1)), + 'doc2' + ); + }); + }); + + it('can use or queries with in', () => { + const testDocs = { + doc1: { a: 1, b: 0 }, + doc2: { b: 1 }, + doc3: { a: 3, b: 2 }, + doc4: { a: 1, b: 3 }, + doc5: { a: 1 }, + doc6: { a: 2 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + // a==2 || b in [2,3] + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', '==', 2), where('b', 'in', [2, 3]))), + 'doc3', + 'doc4', + 'doc6' + ); + }); + }); + + it('can use or queries with array membership', () => { + const testDocs = { + doc1: { a: 1, b: [0] }, + doc2: { b: [1] }, + doc3: { a: 3, b: [2, 7] }, + doc4: { a: 1, b: [3, 7] }, + doc5: { a: 1 }, + doc6: { a: 2 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + // a==2 || b array-contains 7 + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', '==', 2), where('b', 'array-contains', 7))), + 'doc3', + 'doc4', + 'doc6' + ); + + // a==2 || b array-contains-any [0, 3] + await checkOnlineAndOfflineResultsMatch( + query( + coll, + or(where('a', '==', 2), where('b', 'array-contains-any', [0, 3])) + ), + 'doc1', + 'doc4', + 'doc6' + ); + }); + }); + + // TODO(orquery) enable this test when the backend supports + // one in per disjunction + // eslint-disable-next-line no-restricted-properties + it.skip('supports multiple in ops', () => { + const testDocs = { + doc1: { a: 1, b: 0 }, + doc2: { b: 1 }, + doc3: { a: 3, b: 2 }, + doc4: { a: 1, b: 3 }, + doc5: { a: 1 }, + doc6: { a: 2 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + // Two IN operations on different fields with disjunction. + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', 'in', [2, 3]), where('b', 'in', [0, 2]))), + 'doc1', + 'doc3', + 'doc6' + ); + + // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', 'in', [0, 3]), where('a', 'in', [0, 2]))), + 'doc3', + 'doc6' + ); + }); + }); + + // TODO(orquery) enable this test when the backend supports + // one in or array-contains-any per disjunction + // eslint-disable-next-line no-restricted-properties + it.skip('supports using in with array contains any', () => { + const testDocs = { + doc1: { a: 1, b: [0] }, + doc2: { b: [1] }, + doc3: { a: 3, b: [2, 7], c: 10 }, + doc4: { a: 1, b: [3, 7] }, + doc5: { a: 1 }, + doc6: { a: 2, c: 20 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + await checkOnlineAndOfflineResultsMatch( + query( + coll, + or( + where('a', 'in', [2, 3]), + where('b', 'array-contains-any', [0, 7]) + ) + ), + 'doc1', + 'doc3', + 'doc4', + 'doc6' + ); + + await checkOnlineAndOfflineResultsMatch( + query( + coll, + or( + and(where('a', 'in', [2, 3]), where('c', '==', 10)), + where('b', 'array-contains-any', [0, 7]) + ) + ), + 'doc1', + 'doc3', + 'doc4' + ); + }); + }); + + // eslint-disable-next-line no-restricted-properties + it('supports using in with array contains', () => { + const testDocs = { + doc1: { a: 1, b: [0] }, + doc2: { b: [1] }, + doc3: { a: 3, b: [2, 7] }, + doc4: { a: 1, b: [3, 7] }, + doc5: { a: 1 }, + doc6: { a: 2 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + await checkOnlineAndOfflineResultsMatch( + query( + coll, + or(where('a', 'in', [2, 3]), where('b', 'array-contains', 3)) + ), + 'doc3', + 'doc4', + 'doc6' + ); + + await checkOnlineAndOfflineResultsMatch( + query( + coll, + and(where('a', 'in', [2, 3]), where('b', 'array-contains', 7)) + ), + 'doc3' + ); + + await checkOnlineAndOfflineResultsMatch( + query( + coll, + or( + where('a', 'in', [2, 3]), + and(where('b', 'array-contains', 3), where('a', '==', 1)) + ) + ), + 'doc3', + 'doc4', + 'doc6' + ); + + await checkOnlineAndOfflineResultsMatch( + query( + coll, + and( + where('a', 'in', [2, 3]), + or(where('b', 'array-contains', 7), where('a', '==', 1)) + ) + ), + 'doc3' + ); + }); + }); + }); + + // OR Query tests only run when the SDK is configured for persistence + // because they validate that the result from server and cache match + // Additionally these tests must be skipped if running against production + // because it results in a 'missing index' error. The Firestore Emulator, + // however, does serve these queries. + // eslint-disable-next-line no-restricted-properties + (persistence && USE_EMULATOR ? describe : describe.skip)('OR Queries', () => { + it('can use query overloads', () => { + const testDocs = { + doc1: { a: 1, b: 0 }, + doc2: { a: 2, b: 1 }, + doc3: { a: 3, b: 2 }, + doc4: { a: 1, b: 3 }, + doc5: { a: 1, b: 1 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + // a == 1, limit 2, b - desc + await checkOnlineAndOfflineResultsMatch( + query(coll, where('a', '==', 1), limit(2), orderBy('b', 'desc')), + 'doc4', + 'doc5' + ); + }); + }); + + it('can use or queries', () => { + const testDocs = { + doc1: { a: 1, b: 0 }, + doc2: { a: 2, b: 1 }, + doc3: { a: 3, b: 2 }, + doc4: { a: 1, b: 3 }, + doc5: { a: 1, b: 1 } + }; + + return withTestCollection(persistence, testDocs, async coll => { + // with one inequality: a>2 || b==1. + await checkOnlineAndOfflineResultsMatch( + query(coll, or(where('a', '>', 2), where('b', '==', 1))), + 'doc5', + 'doc2', + 'doc3' + ); + // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2 await checkOnlineAndOfflineResultsMatch( query(coll, or(where('a', '==', 1), where('b', '>', 0)), limit(2)), @@ -1505,16 +1727,10 @@ apiDescribe('Queries', (persistence: boolean) => { ), 'doc2' ); - - // Test with limits without orderBy (the __name__ ordering is the tie breaker). - await checkOnlineAndOfflineResultsMatch( - query(coll, or(where('a', '==', 2), where('b', '==', 1)), limit(1)), - 'doc2' - ); }); }); - it('can use or queries with in and not-in', () => { + it('can use or queries with not-in', () => { const testDocs = { doc1: { a: 1, b: 0 }, doc2: { b: 1 }, @@ -1525,14 +1741,6 @@ apiDescribe('Queries', (persistence: boolean) => { }; return withTestCollection(persistence, testDocs, async coll => { - // a==2 || b in [2,3] - await checkOnlineAndOfflineResultsMatch( - query(coll, or(where('a', '==', 2), where('b', 'in', [2, 3]))), - 'doc3', - 'doc4', - 'doc6' - ); - // a==2 || b not-in [2,3] // Has implicit orderBy b. await checkOnlineAndOfflineResultsMatch( @@ -1543,34 +1751,29 @@ apiDescribe('Queries', (persistence: boolean) => { }); }); - it('can use or queries with array membership', () => { + // eslint-disable-next-line no-restricted-properties + it('supports order by equality', () => { const testDocs = { doc1: { a: 1, b: [0] }, doc2: { b: [1] }, - doc3: { a: 3, b: [2, 7] }, + doc3: { a: 3, b: [2, 7], c: 10 }, doc4: { a: 1, b: [3, 7] }, doc5: { a: 1 }, - doc6: { a: 2 } + doc6: { a: 2, c: 20 } }; return withTestCollection(persistence, testDocs, async coll => { - // a==2 || b array-contains 7 await checkOnlineAndOfflineResultsMatch( - query(coll, or(where('a', '==', 2), where('b', 'array-contains', 7))), - 'doc3', + query(coll, where('a', '==', 1), orderBy('a')), + 'doc1', 'doc4', - 'doc6' + 'doc5' ); - // a==2 || b array-contains-any [0, 3] await checkOnlineAndOfflineResultsMatch( - query( - coll, - or(where('a', '==', 2), where('b', 'array-contains-any', [0, 3])) - ), - 'doc1', - 'doc4', - 'doc6' + query(coll, where('a', 'in', [2, 3]), orderBy('a')), + 'doc6', + 'doc3' ); }); });