diff --git a/README.md b/README.md index ca2d057..fc8b00c 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ const firestore = new FirestoreAdminClient(); `firestore-admin` will take care of parsing the data to/from JSON when fetching/updating documents. +If you work with Firestore Timestamp fields, read the +[Using timestamps](#using-timestamps) section.
If you need to query +documents by their ID, read the [Using document IDs in a filter](#using-document-ids-in-a-filter) +section. + ### Create a document ```typescript @@ -204,7 +209,8 @@ await firestore.createDocument("my-collection", { }); ``` -The above will be converted to a Firestore timestamp automatically. +The above `Date` object will be converted to a Firestore timestamp +automatically. When filtering results by timestamp, make sure to use `Date` objects as well, e.g.: @@ -220,3 +226,33 @@ const documents = await firestore.getDocumentsInCollection("my-collection", { }, }); ``` + +In the query results, timestamp fields will be returned as an ISO Date string, +e.g. `2025-01-01T00:00:00.000Z`. + +## Using document IDs in a filter + +If you need to filter query results by document IDs in the +`getDocumentsInCollection` method, you can do so with the `documentId` field. + +For example: + +```typescript +// Will return all documents in the collection with the document IDs `docId1` and `docId2`. +const documents = await firestore.getDocumentsInCollection("my-collection", { + where: { + filters: [["documentId", FirestoreOperator.IN, ["docId1", "docId2"]]], + }, +}); + +// You can still combine it with other filters, e.g.: + +const documents = await firestore.getDocumentsInCollection("my-collection", { + where: { + filters: [ + ["documentId", FirestoreOperator.IN, ["docId1", "docId2"]], + ["isActive", FirestoreOperator.EQUAL, true], + ], + }, +}); +``` diff --git a/deno.json b/deno.json index 0cb3081..30e17d2 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@koiztech/firestore-admin", - "version": "1.1.0", + "version": "1.1.1", "exports": "./mod.ts", "tasks": { "dev": "deno run --watch --env mod.ts", diff --git a/mod.ts b/mod.ts index a5e40e9..34a054f 100644 --- a/mod.ts +++ b/mod.ts @@ -231,13 +231,40 @@ export class FirestoreAdminClient { structuredQuery.where = { compositeFilter: { op: "AND", - filters: options.where.filters.map(([field, op, value]) => ({ - fieldFilter: { - field: { fieldPath: field }, - op, - value: this.encodeValue(value), - }, - })), + filters: options.where.filters.map(([field, op, value]) => { + if (field === "documentId") { + if (!Array.isArray(value)) { + value = [value]; + } + // For documentId filters, we need to construct the full document path + const fullPaths = value.map((id: string) => { + // If ID is already a full path, use it directly + if (id.includes("projects/")) return id; + // Otherwise construct the full path + return `projects/${this.GCP_PROJECT_NAME}/databases/(default)/documents/${path}/${id}`; + }); + return { + fieldFilter: { + field: { fieldPath: "__name__" }, + op: FirestoreOperator.IN, // Filtering by document ID(s) requires IN operator + value: { + arrayValue: { + values: fullPaths.map((p: any) => ({ + referenceValue: p, + })), + }, + }, + }, + }; + } + return { + fieldFilter: { + field: { fieldPath: field }, + op, + value: this.encodeValue(value), + }, + }; + }), }, }; }