Skip to content

Commit

Permalink
Add List() API
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Sep 29, 2018
1 parent f63d4ce commit 682eb09
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 13 deletions.
48 changes: 48 additions & 0 deletions dev/src/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,54 @@ export class CollectionReference extends Query {
return this._path.relativeName;
}

/**
* Retrieves the list of documents in this collection.
*
* The document references returned may include missing documents, which
* are documents that have not been explicitly created but contain
* subcollections. If you attempt to read a missing document from, we will
* return a DocumentSnapshot whose `.exists` property is set to false.
*
* @return {Promise<DocumentReference[]>}
*
* @example
* let collectionRef = firestore.collection('col');
*
* collectionRef.list().then(documentRefs => {
* return firestore.getAll(documentRefs);
* }).then(documentSnapshots => {
* let documentsWithData = 0;
* let missingDocuments = 0;
*
* for (let documentSnapshot of documentSnapshots) {
* if (documentSnapshot.exists) {
* documentsWithData++;
* } else {
* missingDocuments++;
* }
* }
* });
*/
list(): Promise<DocumentReference[]> {
const request: api.IListDocumentsRequest = {
parent: this._path.parent()!.formattedName,
collectionId: this.id,
showMissing: true,
mask: {fieldPaths: []}
};

return this.firestore.request('listDocuments', request, requestTag())
.then((documents: api.IDocument[]) => {
// Note that the backend already orders these documents by name,
// so we do not need to manually sort them.
return documents.map(doc => {
const path = ResourcePath.fromSlashSeparatedString(doc.name!);
return this.doc(path.id!);
});
});
}


/**
* Gets a [DocumentReference]{@link DocumentReference} instance that
* refers to the document at the specified path. If no path is specified, an
Expand Down
18 changes: 18 additions & 0 deletions dev/system-test/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ describe('CollectionReference class', function() {
assert.equal(doc.get('foo'), 'a');
});
});

it('lists missing documents', async () => {
let batch = firestore.batch();

batch.set(randomCol.doc('a'),{});
batch.set(randomCol.doc('b/b/b'),{});
batch.set(randomCol.doc('c'),{});
await batch.commit();

const documentRefs = await randomCol.list();
const documents = await firestore.getAll(documentRefs);

const existingDocs = documents.filter(doc => doc.exists);
const missingDocs = documents.filter(doc => !doc.exists);

expect(existingDocs.map(doc => doc.id)).to.have.members(['a', 'c']);
expect(missingDocs.map(doc => doc.id)).to.have.members(['b']);
});
});

describe('DocumentReference class', function() {
Expand Down
24 changes: 23 additions & 1 deletion dev/test/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {expect} from 'chai';
import Firestore = require('../src');

import {DocumentReference} from '../src/reference';
import {COLLECTION_ROOT, createInstance, DATABASE_ROOT} from './util/helpers';
import {createInstance, DATABASE_ROOT, document} from './util/helpers';

// Change the argument to 'console.log' to enable debug output.
Firestore.setLogFunction(() => {});
Expand Down Expand Up @@ -135,6 +135,28 @@ describe('Collection interface', () => {
});
});

it('hast list() method', () => {
const overrides = {
listDocuments: (request, options, callback) => {
expect(request).to.deep.eq({
parent: `${DATABASE_ROOT}/documents/a/b`,
collectionId: 'c',
showMissing: true,
mask: {fieldPaths: []}
});

callback(null, [document('first'), document('second')]);
}
};

return createInstance(overrides).then(firestore => {
return firestore.collection('a/b/c').list().then(documentRefs => {
expect(documentRefs[0].id).to.eq('first');
expect(documentRefs[1].id).to.eq('second');
});
});
});

it('has isEqual() method', () => {
const coll1 = firestore.collection('coll1');
const coll1Equals = firestore.collection('coll1');
Expand Down
20 changes: 11 additions & 9 deletions dev/test/field-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ describe('FieldValue.arrayUnion()', () => {
it('can be used with set()', () => {
const overrides: ApiOverride = {
commit: (request, options, callback) => {
const expectedRequest = commitRequest(set(document('foo', 'bar'), [
arrayTransform('field', 'appendMissingElements', 'foo', 'bar'),
arrayTransform('map.field', 'appendMissingElements', 'foo', 'bar')
]));
const expectedRequest =
commitRequest(set(document('documentId', 'foo', 'bar'), [
arrayTransform('field', 'appendMissingElements', 'foo', 'bar'),
arrayTransform('map.field', 'appendMissingElements', 'foo', 'bar')
]));

expect(request).to.deep.equal(expectedRequest);

Expand Down Expand Up @@ -136,10 +137,11 @@ describe('FieldValue.arrayRemove()', () => {
it('can be used with set()', () => {
const overrides: ApiOverride = {
commit: (request, options, callback) => {
const expectedRequest = commitRequest(set(document('foo', 'bar'), [
arrayTransform('field', 'removeAllFromArray', 'foo', 'bar'),
arrayTransform('map.field', 'removeAllFromArray', 'foo', 'bar')
]));
const expectedRequest =
commitRequest(set(document('documentId', 'foo', 'bar'), [
arrayTransform('field', 'removeAllFromArray', 'foo', 'bar'),
arrayTransform('map.field', 'removeAllFromArray', 'foo', 'bar')
]));
expect(request).to.deep.equal(expectedRequest);

callback(null, writeResult(2));
Expand Down Expand Up @@ -170,7 +172,7 @@ describe('FieldValue.serverTimestamp()', () => {
const overrides: ApiOverride = {
commit: (request, options, callback) => {
const expectedRequest = commitRequest(
set(document('foo', 'bar'),
set(document('documentId', 'foo', 'bar'),
[serverTimestamp('field'), serverTimestamp('map.field')]));
expect(request).to.deep.equal(expectedRequest);

Expand Down
1 change: 1 addition & 0 deletions dev/test/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ xdescribe('firestore.d.ts', function() {
const docRef2: DocumentReference = collRef.doc('doc');
collRef.add(documentData).then((docRef: DocumentReference) => {
});
const list: Promise<DocumentReference[]> = collRef.list();
const equals: boolean = collRef.isEqual(collRef);
});

Expand Down
7 changes: 4 additions & 3 deletions dev/test/util/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type ApiOverride = {
commit?: (request, options, callback) => void;
rollback?: (request, options, callback) => void;
listCollectionIds?: (request, options, callback) => void;
listDocuments?: (request, options, callback) => void;
batchGetDocuments?: (request) => NodeJS.ReadableStream;
runQuery?: (request) => NodeJS.ReadableStream;
listen?: () => NodeJS.ReadWriteStream;
Expand Down Expand Up @@ -155,16 +156,16 @@ function value(value: string|api.IValue): api.IValue {
}

export function document(
field?: string, value?: string|api.IValue,
id: string, field?: string, value?: string|api.IValue,
...fieldOrValue: Array<string|api.IValue>): api.IDocument {
const document: api.IDocument = {
name: `${DATABASE_ROOT}/documents/collectionId/documentId`,
name: `${DATABASE_ROOT}/documents/collectionId/${id}`,
fields: {},
createTime: {seconds: 1, nanos: 2},
updateTime: {seconds: 3, nanos: 4},
};

for (let i = 0; i < arguments.length; i += 2) {
for (let i = 1; i < arguments.length; i += 2) {
const field: string = arguments[i];
const value: string|api.Value = arguments[i + 1];

Expand Down
12 changes: 12 additions & 0 deletions types/firestore.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,18 @@ declare namespace FirebaseFirestore {
*/
readonly path: string;

/**
* Retrieves the list of documents in this collection.
*
* The document references returned may include missing documents, which
* are documents that have not been explicitly created but contain
* subcollections. If you attempt to read a missing document from, we will
* return a DocumentSnapshot whose `.exists` property is set to false.
*
* @return {Promise<DocumentReference[]>}
*/
list(): Promise<CollectionReference[]>;

/**
* Get a `DocumentReference` for the document within the collection at the
* specified path. If no path is specified, an automatically-generated
Expand Down

0 comments on commit 682eb09

Please sign in to comment.