Skip to content

Commit 6e4a7e3

Browse files
committed
Add basic tests
1 parent c274177 commit 6e4a7e3

File tree

9 files changed

+227
-55
lines changed

9 files changed

+227
-55
lines changed

packages/firestore/src/core/pipeline-util.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,20 @@ import {
6565
} from '../lite-api/stage';
6666
import { Pipeline } from '../api/pipeline';
6767
import { Pipeline as LitePipeline } from '../lite-api/pipeline';
68-
import { canonifyQuery, Query, queryEquals, stringifyQuery } from './query';
68+
import {
69+
canonifyQuery,
70+
Query,
71+
queryEquals,
72+
QueryImpl,
73+
stringifyQuery
74+
} from './query';
6975
import {
7076
canonifyTarget,
7177
Target,
7278
targetEquals,
7379
targetIsPipelineTarget
7480
} from './target';
81+
import { ResourcePath } from '../model/path';
7582

7683
/* eslint @typescript-eslint/no-explicit-any: 0 */
7784

@@ -405,7 +412,7 @@ export function getPipelineFlavor(p: Pipeline): PipelineFlavor {
405412

406413
export type PipelineSourceType =
407414
| 'collection'
408-
| 'collection-group'
415+
| 'collection_group'
409416
| 'database'
410417
| 'documents';
411418

@@ -416,10 +423,10 @@ export function getPipelineSourceType(
416423
const source = p.stages[0];
417424

418425
if (
419-
source.name === CollectionSource.name ||
420-
source.name === CollectionGroupSource.name ||
421-
source.name === DatabaseSource.name ||
422-
source.name === DocumentsSource.name
426+
source instanceof CollectionSource ||
427+
source instanceof CollectionGroupSource ||
428+
source instanceof DatabaseSource ||
429+
source instanceof DocumentsSource
423430
) {
424431
return source.name as PipelineSourceType;
425432
}
@@ -435,12 +442,34 @@ export function getPipelineCollection(p: Pipeline): string | undefined {
435442
}
436443

437444
export function getPipelineCollectionGroup(p: Pipeline): string | undefined {
438-
if (getPipelineSourceType(p) === 'collection-group') {
445+
if (getPipelineSourceType(p) === 'collection_group') {
439446
return (p.stages[0] as CollectionGroupSource).collectionId;
440447
}
441448
return undefined;
442449
}
443450

451+
export function asCollectionPipelineAtPath(
452+
pipeline: Pipeline,
453+
path: ResourcePath
454+
): Pipeline {
455+
const newStages = pipeline.stages.map(s => {
456+
if (s instanceof CollectionGroupSource) {
457+
return new CollectionSource(path.canonicalString());
458+
}
459+
460+
return s;
461+
});
462+
463+
return new Pipeline(
464+
pipeline.db,
465+
pipeline.userDataReader,
466+
pipeline.userDataWriter,
467+
pipeline.documentReferenceFactory,
468+
newStages,
469+
pipeline.converter
470+
);
471+
}
472+
444473
export function getPipelineDocuments(p: Pipeline): string[] | undefined {
445474
if (getPipelineSourceType(p) === 'documents') {
446475
return (p.stages[0] as DocumentsSource).docPaths;
@@ -451,7 +480,7 @@ export function getPipelineDocuments(p: Pipeline): string[] | undefined {
451480
export type QueryOrPipeline = Query | Pipeline;
452481

453482
export function isPipeline(q: QueryOrPipeline): q is Pipeline {
454-
return q instanceof Pipeline;
483+
return q instanceof Pipeline || q instanceof LitePipeline;
455484
}
456485

457486
export function stringifyQueryOrPipeline(q: QueryOrPipeline): string {

packages/firestore/src/core/pipeline_run.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
} from '../model/values';
3535
import { toEvaluable } from './expressions';
3636
import { UserDataReader } from '../lite-api/user_data_reader';
37-
import { Query, queryMatches } from './query';
37+
import { Query, queryMatches, queryMatchesAllDocuments } from './query';
3838
import { isPipeline, QueryOrPipeline } from './pipeline-util';
3939

4040
export type PipelineInputOutput = MutableDocument;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export class Pipeline<AppModelType = DocumentData>
150150
readonly stages: Stage[],
151151
// TODO(pipeline) support converter
152152
//private converter: FirestorePipelineConverter<AppModelType> = defaultPipelineConverter()
153-
protected converter: unknown = {}
153+
readonly converter: unknown = {}
154154
) {}
155155

156156
/**

packages/firestore/src/local/indexeddb_remote_document_cache.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ import { QueryContext } from './query_context';
5959
import { RemoteDocumentCache } from './remote_document_cache';
6060
import { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';
6161
import { SimpleDbStore } from './simple_db';
62+
import {
63+
getPipelineCollection,
64+
isPipeline,
65+
QueryOrPipeline
66+
} from '../core/pipeline-util';
67+
import { queryOrPipelineMatches } from '../core/pipeline_run';
6268

6369
export interface DocumentSizeEntry {
6470
document: MutableDocument;
@@ -199,7 +205,9 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache {
199205
return remoteDocumentsStore(transaction)
200206
.iterate((dbKey, dbDoc) => {
201207
const doc = this.maybeDecodeDocument(
202-
DocumentKey.fromSegments(dbDoc.prefixPath.concat(dbDoc.documentId)),
208+
DocumentKey.fromSegments(
209+
dbDoc.prefixPath.concat(dbDoc.collectionGroup, dbDoc.documentId)
210+
),
203211
dbDoc
204212
);
205213
results = results.insert(doc.key, doc);
@@ -293,12 +301,21 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache {
293301

294302
getDocumentsMatchingQuery(
295303
transaction: PersistenceTransaction,
296-
query: Query,
304+
query: QueryOrPipeline,
297305
offset: IndexOffset,
298306
mutatedDocs: OverlayMap,
299307
context?: QueryContext
300308
): PersistencePromise<MutableDocumentMap> {
301-
const collection = query.path;
309+
if (isPipeline(query)) {
310+
debugAssert(
311+
!!getPipelineCollection(query),
312+
'getDocumentsMatchingQuery can only handle collection pipelines'
313+
);
314+
}
315+
316+
const collection = isPipeline(query)
317+
? ResourcePath.fromString(getPipelineCollection(query)!)
318+
: query.path;
302319
const startKey = [
303320
collection.popLast().toArray(),
304321
collection.lastSegment(),
@@ -331,7 +348,8 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache {
331348
);
332349
if (
333350
document.isFoundDocument() &&
334-
(queryMatches(query, document) || mutatedDocs.has(document.key))
351+
(queryOrPipelineMatches(query, document) ||
352+
mutatedDocs.has(document.key))
335353
) {
336354
// Either the document matches the given query, or it is mutated.
337355
results = results.insert(document.key, document);

packages/firestore/src/local/local_documents_view.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { PersistenceTransaction } from './persistence_transaction';
6363
import { QueryContext } from './query_context';
6464
import { RemoteDocumentCache } from './remote_document_cache';
6565
import {
66+
asCollectionPipelineAtPath,
6667
canonifyPipeline,
6768
getPipelineCollection,
6869
getPipelineCollectionGroup,
@@ -566,10 +567,33 @@ export class LocalDocumentsView {
566567
offset: IndexOffset,
567568
context?: QueryContext
568569
): PersistencePromise<DocumentMap> {
569-
if (getPipelineSourceType(pipeline) === 'collection-group') {
570+
if (getPipelineSourceType(pipeline) === 'collection_group') {
570571
// TODO(pipeline): rewrite the pipeline as collection pipeline and recurse into this function
571572
// return this.getDocumentsMatchingPipeline(txn, pipeline, offset, context);
572-
throw new Error('not implemented for collection group yet');
573+
const collectionId = getPipelineCollectionGroup(pipeline)!;
574+
let results = documentMap();
575+
return this.indexManager
576+
.getCollectionParents(txn, collectionId)
577+
.next(parents => {
578+
// Perform a collection query against each parent that contains the
579+
// collectionId and aggregate the results.
580+
return PersistencePromise.forEach(parents, (parent: ResourcePath) => {
581+
const collectionPipeline = asCollectionPipelineAtPath(
582+
pipeline,
583+
parent.child(collectionId)
584+
);
585+
return this.getDocumentsMatchingPipeline(
586+
txn,
587+
collectionPipeline,
588+
offset,
589+
context
590+
).next(r => {
591+
r.forEach((key, doc) => {
592+
results = results.insert(key, doc);
593+
});
594+
});
595+
}).next(() => results);
596+
});
573597
} else {
574598
// Query the remote documents and overlay mutations.
575599
let overlays: OverlayMap;
@@ -661,7 +685,7 @@ export class LocalDocumentsView {
661685
ResourcePath.fromString(getPipelineCollection(pipeline)!),
662686
largestBatchId
663687
);
664-
case 'collection-group':
688+
case 'collection_group':
665689
throw new FirestoreError(
666690
'invalid-argument',
667691
`Unexpected collection group pipeline: ${canonifyPipeline(pipeline)}`

packages/firestore/src/local/query_engine.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ import {
5959
import * as querystring from 'node:querystring';
6060
import {
6161
pipelineMatches,
62-
pipelineMatchesAllDocuments
62+
pipelineMatchesAllDocuments,
63+
queryOrPipelineMatchesFullCollection
6364
} from '../core/pipeline_run';
6465
import { compareByKey } from '../model/document_comparator';
6566

packages/firestore/test/integration/api/pipeline.listen.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ apiDescribe('Pipelines', persistence => {
262262
]);
263263
});
264264

265-
it.only('basic listen works', async () => {
265+
it('basic listen works', async () => {
266266
const storeEvent = new EventsAccumulator<PipelineSnapshot>();
267267

268268
let result = firestore

packages/firestore/test/unit/core/pipeline.test.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,9 @@ import { runPipeline } from '../../../src/core/pipeline_run';
3838

3939
import { doc } from '../../util/helpers';
4040
import { and, or } from '../../../src/lite-api/expressions';
41+
import { newTestFirestore } from '../../util/api_helpers';
4142

42-
const fakeAuthProvider: CredentialsProvider<User> =
43-
{} as unknown as CredentialsProvider<User>;
44-
const fakeAppCheckProvider: CredentialsProvider<string> =
45-
{} as unknown as CredentialsProvider<string>;
46-
const db = new Firestore(
47-
fakeAuthProvider,
48-
fakeAppCheckProvider,
49-
DatabaseId.empty()
50-
);
43+
const db = newTestFirestore();
5144

5245
describe('Pipeline Canonify', () => {
5346
it('works as expected for simple where clause', () => {
@@ -158,7 +151,7 @@ describe('Pipeline Canonify', () => {
158151
});
159152
});
160153

161-
describe.only('pipelineEq', () => {
154+
describe('pipelineEq', () => {
162155
it('returns true for identical pipelines', () => {
163156
const p1 = db.pipeline().collection('test').where(eq(`foo`, 42));
164157
const p2 = db.pipeline().collection('test').where(eq(`foo`, 42));

0 commit comments

Comments
 (0)