Skip to content

Commit 89dac6c

Browse files
Markduckworth/or queries pr 9 (#6755)
* OR Query Public API * In operator expansion
1 parent 7c54b30 commit 89dac6c

File tree

13 files changed

+1472
-730
lines changed

13 files changed

+1472
-730
lines changed

common/api-review/firestore-lite.api.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,14 @@ export class Query<T = DocumentData> {
230230
withConverter<U>(converter: FirestoreDataConverter<U>): Query<U>;
231231
}
232232

233+
// @public
234+
export function query<T>(query: Query<T>, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query<T>;
235+
233236
// @public
234237
export function query<T>(query: Query<T>, ...queryConstraints: QueryConstraint[]): Query<T>;
235238

236239
// @public
237-
export class QueryCompositeFilterConstraint extends QueryFilterConstraint {
240+
export class QueryCompositeFilterConstraint {
238241
readonly type: 'or' | 'and';
239242
}
240243

@@ -244,7 +247,7 @@ export abstract class QueryConstraint {
244247
}
245248

246249
// @public
247-
export type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore' | 'and' | 'or';
250+
export type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore';
248251

249252
// @public
250253
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {
@@ -261,19 +264,21 @@ export class QueryEndAtConstraint extends QueryConstraint {
261264
export function queryEqual<T>(left: Query<T>, right: Query<T>): boolean;
262265

263266
// @public
264-
export class QueryFieldFilterConstraint extends QueryFilterConstraint {
267+
export class QueryFieldFilterConstraint extends QueryConstraint {
265268
readonly type = "where";
266269
}
267270

268271
// @public
269-
export abstract class QueryFilterConstraint extends QueryConstraint {
270-
}
272+
export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint;
271273

272274
// @public
273275
export class QueryLimitConstraint extends QueryConstraint {
274276
readonly type: 'limit' | 'limitToLast';
275277
}
276278

279+
// @public
280+
export type QueryNonFilterConstraint = QueryOrderByConstraint | QueryLimitConstraint | QueryStartAtConstraint | QueryEndAtConstraint;
281+
277282
// @public
278283
export class QueryOrderByConstraint extends QueryConstraint {
279284
readonly type = "orderBy";

common/api-review/firestore.api.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,14 @@ export class Query<T = DocumentData> {
359359
withConverter<U>(converter: FirestoreDataConverter<U>): Query<U>;
360360
}
361361

362+
// @public
363+
export function query<T>(query: Query<T>, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query<T>;
364+
362365
// @public
363366
export function query<T>(query: Query<T>, ...queryConstraints: QueryConstraint[]): Query<T>;
364367

365368
// @public
366-
export class QueryCompositeFilterConstraint extends QueryFilterConstraint {
369+
export class QueryCompositeFilterConstraint {
367370
readonly type: 'or' | 'and';
368371
}
369372

@@ -373,7 +376,7 @@ export abstract class QueryConstraint {
373376
}
374377

375378
// @public
376-
export type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore' | 'and' | 'or';
379+
export type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore';
377380

378381
// @public
379382
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {
@@ -390,19 +393,21 @@ export class QueryEndAtConstraint extends QueryConstraint {
390393
export function queryEqual<T>(left: Query<T>, right: Query<T>): boolean;
391394

392395
// @public
393-
export class QueryFieldFilterConstraint extends QueryFilterConstraint {
396+
export class QueryFieldFilterConstraint extends QueryConstraint {
394397
readonly type = "where";
395398
}
396399

397400
// @public
398-
export abstract class QueryFilterConstraint extends QueryConstraint {
399-
}
401+
export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint;
400402

401403
// @public
402404
export class QueryLimitConstraint extends QueryConstraint {
403405
readonly type: 'limit' | 'limitToLast';
404406
}
405407

408+
// @public
409+
export type QueryNonFilterConstraint = QueryOrderByConstraint | QueryLimitConstraint | QueryStartAtConstraint | QueryEndAtConstraint;
410+
406411
// @public
407412
export class QueryOrderByConstraint extends QueryConstraint {
408413
readonly type = "orderBy";

packages/firestore/lite/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,11 @@ export {
6969
QueryConstraint,
7070
QueryConstraintType,
7171
QueryCompositeFilterConstraint,
72-
QueryFieldFilterConstraint,
7372
QueryFilterConstraint,
73+
QueryFieldFilterConstraint,
7474
QueryOrderByConstraint,
7575
QueryLimitConstraint,
76+
QueryNonFilterConstraint,
7677
QueryStartAtConstraint,
7778
QueryEndAtConstraint,
7879
OrderByDirection,

packages/firestore/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"test:all:ci": "run-p test:browser test:lite:browser test:travis",
3131
"test:all": "run-p test:browser test:lite:browser test:travis test:minified",
3232
"test:browser": "karma start --single-run",
33+
"test:browser:emulator:debug": "karma start --browsers=Chrome --local",
3334
"test:browser:emulator": "karma start --single-run --local",
3435
"test:browser:unit": "karma start --single-run --unit",
3536
"test:browser:debug": "karma start --browsers=Chrome --auto-watch",

packages/firestore/src/api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ export {
8686
QueryConstraint,
8787
QueryConstraintType,
8888
QueryCompositeFilterConstraint,
89-
QueryFieldFilterConstraint,
9089
QueryFilterConstraint,
90+
QueryFieldFilterConstraint,
9191
QueryOrderByConstraint,
9292
QueryLimitConstraint,
93+
QueryNonFilterConstraint,
9394
QueryStartAtConstraint,
9495
QueryEndAtConstraint,
9596
OrderByDirection,

packages/firestore/src/api/filter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ export {
3737
QueryOrderByConstraint,
3838
QueryLimitConstraint,
3939
QueryStartAtConstraint,
40-
QueryEndAtConstraint
40+
QueryEndAtConstraint,
41+
QueryNonFilterConstraint
4142
} from '../lite-api/query';

packages/firestore/src/lite-api/query.ts

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -84,67 +84,108 @@ export type QueryConstraintType =
8484
| 'startAt'
8585
| 'startAfter'
8686
| 'endAt'
87-
| 'endBefore'
88-
| 'and'
89-
| 'or';
87+
| 'endBefore';
88+
89+
/**
90+
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
91+
* to a Firestore query.
92+
*/
93+
export abstract class AppliableConstraint {
94+
/**
95+
* Takes the provided {@link Query} and returns a copy of the {@link Query} with this
96+
* {@link AppliableConstraint} applied.
97+
*/
98+
abstract _apply<T>(query: Query<T>): Query<T>;
99+
}
90100

91101
/**
92102
* A `QueryConstraint` is used to narrow the set of documents returned by a
93103
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
94-
* {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link
95-
* (endBefore:1)}, {@link (endAt:1)}, {@link limit}, {@link limitToLast}, {@link or} or {@link and} and
104+
* {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
105+
* endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
96106
* can then be passed to {@link query} to create a new query instance that
97107
* also contains this `QueryConstraint`.
98108
*/
99-
export abstract class QueryConstraint {
109+
export abstract class QueryConstraint extends AppliableConstraint {
100110
/** The type of this query constraint */
101111
abstract readonly type: QueryConstraintType;
102112

103113
/**
104114
* Takes the provided {@link Query} and returns a copy of the {@link Query} with this
105-
* {@link QueryConstraint} applied.
115+
* {@link AppliableConstraint} applied.
106116
*/
107117
abstract _apply<T>(query: Query<T>): Query<T>;
108118
}
109119

110120
/**
111-
* Creates a new immutable instance of {@link Query} that is extended to also include
112-
* additional query constraints.
121+
* Creates a new immutable instance of {@link Query} that is extended to also
122+
* include additional query constraints.
123+
*
124+
* @param query - The {@link Query} instance to use as a base for the new
125+
* constraints.
126+
* @param compositeFilter - The {@link QueryCompositeFilterConstraint} to
127+
* apply. Create {@link QueryCompositeFilterConstraint} using {@link and} or
128+
* {@link or}.
129+
* @param queryConstraints - Additional {@link QueryNonFilterConstraint}s to
130+
* apply (e.g. {@link orderBy}, {@link limit}).
131+
* @throws if any of the provided query constraints cannot be combined with the
132+
* existing or new constraints.
133+
*/
134+
export function query<T>(
135+
query: Query<T>,
136+
compositeFilter: QueryCompositeFilterConstraint,
137+
...queryConstraints: QueryNonFilterConstraint[]
138+
): Query<T>;
139+
140+
/**
141+
* Creates a new immutable instance of {@link Query} that is extended to also
142+
* include additional query constraints.
113143
*
114-
* @param query - The {@link Query} instance to use as a base for the new constraints.
144+
* @param query - The {@link Query} instance to use as a base for the new
145+
* constraints.
115146
* @param queryConstraints - The list of {@link QueryConstraint}s to apply.
116147
* @throws if any of the provided query constraints cannot be combined with the
117148
* existing or new constraints.
118149
*/
119150
export function query<T>(
120151
query: Query<T>,
121152
...queryConstraints: QueryConstraint[]
153+
): Query<T>;
154+
155+
export function query<T>(
156+
query: Query<T>,
157+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
158+
filterOrQueryConstraints: any,
159+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
160+
...nonFilters: any
122161
): Query<T> {
162+
let queryConstraints: AppliableConstraint[] = [];
163+
if (filterOrQueryConstraints instanceof AppliableConstraint) {
164+
queryConstraints.push(filterOrQueryConstraints);
165+
} else if (Array.isArray(filterOrQueryConstraints)) {
166+
queryConstraints.concat(filterOrQueryConstraints as AppliableConstraint[]);
167+
}
168+
169+
if (nonFilters !== undefined) {
170+
queryConstraints = queryConstraints.concat(
171+
nonFilters as QueryNonFilterConstraint[]
172+
);
173+
}
174+
123175
for (const constraint of queryConstraints) {
124176
query = constraint._apply(query);
125177
}
126178
return query;
127179
}
128180

129181
/**
130-
* A `QueryFilterConstraint` is used to narrow the set of documents returned by
182+
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
131183
* a Firestore query by filtering on one or more document fields.
132-
* `QueryFilterConstraint`s are created by invoking {@link where}, {@link or} or
133-
* {@link and} and can then be passed to {@link query} to create a new query
134-
* instance that also contains this `QueryFilterConstraint`.
135-
*/
136-
export abstract class QueryFilterConstraint extends QueryConstraint {
137-
abstract _parse<T>(query: Query<T>): Filter;
138-
}
139-
140-
/**
141-
* A `QueryFieldFilterConstraint` is used to narrow the set of documents
142-
* returned by a Firestore query by filtering on one document field.
143-
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and
144-
* can then be passed to {@link query} to create a new query instance that
145-
* also contains this `QueryFieldFilterConstraint`.
184+
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
185+
* be passed to {@link query} to create a new query instance that also contains
186+
* this `QueryFieldFilterConstraint`.
146187
*/
147-
export class QueryFieldFilterConstraint extends QueryFilterConstraint {
188+
export class QueryFieldFilterConstraint extends QueryConstraint {
148189
/** The type of this query constraint */
149190
readonly type = 'where';
150191

@@ -233,12 +274,12 @@ export function where(
233274
/**
234275
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
235276
* returned by a Firestore query by performing the logical OR or AND of multiple
236-
* {@link QueryFieldFilterConstraint} or {@link QueryCompositeFilterConstraint}.
277+
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
237278
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
238279
* {@link and} and can then be passed to {@link query} to create a new query
239-
* instance that also contains this `QueryCompositeFilterConstraint`.
280+
* instance that also contains the `QueryCompositeFilterConstraint`.
240281
*/
241-
export class QueryCompositeFilterConstraint extends QueryFilterConstraint {
282+
export class QueryCompositeFilterConstraint extends AppliableConstraint {
242283
/**
243284
* @internal
244285
*/
@@ -287,7 +328,7 @@ export class QueryCompositeFilterConstraint extends QueryFilterConstraint {
287328
);
288329
}
289330

290-
_getQueryConstraints(): readonly QueryConstraint[] {
331+
_getQueryConstraints(): readonly AppliableConstraint[] {
291332
return this._queryConstraints;
292333
}
293334

@@ -296,13 +337,39 @@ export class QueryCompositeFilterConstraint extends QueryFilterConstraint {
296337
}
297338
}
298339

340+
/**
341+
* `QueryNonFilterConstraint` is a helper union type that represents
342+
* QueryConstraints which are used to narrow or order the set of documents,
343+
* but that do not explicitly filter on a document field.
344+
* `QueryNonFilterConstraint`s are created by invoking {@link orderBy},
345+
* {@link startAt}, {@link startAfter}, {@link endBefore}, {@link endAt},
346+
* {@link limit} or {@link limitToLast} and can then be passed to {@link query}
347+
* to create a new query instance that also contains the `QueryConstraint`.
348+
*/
349+
export type QueryNonFilterConstraint =
350+
| QueryOrderByConstraint
351+
| QueryLimitConstraint
352+
| QueryStartAtConstraint
353+
| QueryEndAtConstraint;
354+
355+
/**
356+
* `QueryFilterConstraint` is a helper union type that represents
357+
* {@link QueryFieldFilterConstraint} and {@link QueryCompositeFilterConstraint}.
358+
* `QueryFilterConstraint`s are created by invoking {@link or} or {@link and}
359+
* and can then be passed to {@link query} to create a new query instance that
360+
* also contains the `QueryConstraint`.
361+
*/
362+
export type QueryFilterConstraint =
363+
| QueryFieldFilterConstraint
364+
| QueryCompositeFilterConstraint;
365+
299366
/**
300367
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical OR
301-
* of all the provided {@link QueryFilterConstraint}.
368+
* of all the provided {@link QueryFilterConstraint}s.
302369
*
303-
* @param queryConstraints - Optional. The {@link queryConstraints} for OR
304-
* operation. These must be created with calls to {@link where}, {@link or}, or
305-
* {@link and}.
370+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
371+
* for OR operation. These must be created with calls to {@link where},
372+
* {@link or}, or {@link and}.
306373
* @returns The created {@link QueryCompositeFilterConstraint}.
307374
*/
308375
export function or(
@@ -321,11 +388,11 @@ export function or(
321388

322389
/**
323390
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical AND
324-
* of all the provided {@link QueryFilterConstraint}.
391+
* of all the provided {@link QueryFilterConstraint}s.
325392
*
326-
* @param queryConstraints - Optional. The {@link queryConstraints} for AND
327-
* operation. These must be created with calls to {@link where}, {@link or}, or
328-
* {@link and}.
393+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
394+
* for AND operation. These must be created with calls to {@link where},
395+
* {@link or}, or {@link and}.
329396
* @returns The created {@link QueryCompositeFilterConstraint}.
330397
*/
331398
export function and(
@@ -1139,15 +1206,15 @@ function validateOrderByAndInequalityMatch(
11391206

11401207
export function validateQueryFilterConstraint(
11411208
functionName: string,
1142-
queryConstraint: QueryConstraint
1209+
queryConstraint: AppliableConstraint
11431210
): void {
11441211
if (
1145-
!(queryConstraint instanceof QueryFilterConstraint) &&
1212+
!(queryConstraint instanceof QueryFieldFilterConstraint) &&
11461213
!(queryConstraint instanceof QueryCompositeFilterConstraint)
11471214
) {
11481215
throw new FirestoreError(
11491216
Code.INVALID_ARGUMENT,
1150-
`Function ${functionName}() requires QueryContraints created with a call to 'where(...)'.`
1217+
`Function ${functionName}() requires AppliableContraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`
11511218
);
11521219
}
11531220
}

0 commit comments

Comments
 (0)