firebase-rest-firestore 1.2.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/client.d.ts +417 -0
- package/dist/{client.js → cjs/client.js} +442 -279
- package/dist/{index.d.ts → cjs/index.d.ts} +1 -1
- package/dist/{index.js → cjs/index.js} +1 -2
- package/dist/{types.d.ts → cjs/types.d.ts} +4 -0
- package/dist/cjs/utils/auth.d.ts +13 -0
- package/dist/{utils → cjs/utils}/auth.js +14 -10
- package/dist/cjs/utils/config.d.ts +6 -0
- package/dist/{utils → cjs/utils}/config.js +0 -12
- package/dist/cjs/utils/path.d.ts +73 -0
- package/dist/cjs/utils/path.js +176 -0
- package/dist/esm/client.d.ts +417 -0
- package/dist/esm/client.js +1066 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/types.d.ts +69 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils/auth.d.ts +13 -0
- package/dist/esm/utils/auth.js +57 -0
- package/dist/esm/utils/config.d.ts +6 -0
- package/dist/esm/utils/config.js +11 -0
- package/dist/esm/utils/converter.d.ts +27 -0
- package/dist/esm/utils/converter.js +107 -0
- package/dist/esm/utils/path.d.ts +73 -0
- package/dist/esm/utils/path.js +169 -0
- package/dist/types/client.d.ts +417 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/types.d.ts +69 -0
- package/dist/types/utils/auth.d.ts +13 -0
- package/dist/types/utils/config.d.ts +6 -0
- package/dist/types/utils/converter.d.ts +27 -0
- package/dist/types/utils/path.d.ts +73 -0
- package/package.json +18 -5
- package/dist/client.d.ts +0 -381
- package/dist/utils/auth.d.ts +0 -13
- package/dist/utils/config.d.ts +0 -13
- package/dist/utils/path.d.ts +0 -14
- package/dist/utils/path.js +0 -27
- /package/dist/{types.js → cjs/types.js} +0 -0
- /package/dist/{utils → cjs/utils}/converter.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/converter.js +0 -0
|
@@ -6,24 +6,30 @@ const auth_1 = require("./utils/auth");
|
|
|
6
6
|
const converter_1 = require("./utils/converter");
|
|
7
7
|
const path_1 = require("./utils/path");
|
|
8
8
|
const config_1 = require("./utils/config");
|
|
9
|
+
const path_2 = require("./utils/path");
|
|
9
10
|
/**
|
|
10
|
-
* Firestore
|
|
11
|
+
* Firestore client class
|
|
11
12
|
*/
|
|
12
13
|
class FirestoreClient {
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param config Firestore
|
|
15
|
+
* Constructor
|
|
16
|
+
* @param config Firestore configuration object
|
|
16
17
|
*/
|
|
17
18
|
constructor(config) {
|
|
18
19
|
this.token = null;
|
|
19
20
|
this.tokenExpiry = 0;
|
|
20
21
|
this.configChecked = false;
|
|
22
|
+
this.debug = false;
|
|
21
23
|
this.config = config;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
this.pathUtil = (0, path_2.createFirestorePath)(config, config.debug || false);
|
|
25
|
+
this.debug = !!config.debug;
|
|
26
|
+
// Log configuration if debug is enabled
|
|
27
|
+
if (this.debug) {
|
|
28
|
+
console.log("Firestore client initialized with config:", JSON.stringify(this.config, null, 2));
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
|
-
*
|
|
32
|
+
* Check configuration parameters
|
|
27
33
|
* @private
|
|
28
34
|
*/
|
|
29
35
|
checkConfig() {
|
|
@@ -31,11 +37,11 @@ class FirestoreClient {
|
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
39
|
// 必須パラメータのチェック
|
|
34
|
-
const requiredParams = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"clientEmail"
|
|
38
|
-
|
|
40
|
+
const requiredParams = ["projectId"];
|
|
41
|
+
// Only require auth parameters when not using emulator
|
|
42
|
+
if (!this.config.useEmulator) {
|
|
43
|
+
requiredParams.push("privateKey", "clientEmail");
|
|
44
|
+
}
|
|
39
45
|
const missingParams = requiredParams.filter(param => !this.config[param]);
|
|
40
46
|
if (missingParams.length > 0) {
|
|
41
47
|
throw new Error(`Missing required Firestore configuration parameters: ${missingParams.join(", ")}`);
|
|
@@ -43,14 +49,24 @@ class FirestoreClient {
|
|
|
43
49
|
this.configChecked = true;
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
46
|
-
*
|
|
52
|
+
* Get authentication token (with caching)
|
|
47
53
|
*/
|
|
48
54
|
async getToken() {
|
|
49
|
-
//
|
|
55
|
+
// Check settings before operation
|
|
50
56
|
this.checkConfig();
|
|
57
|
+
// In emulator mode, we don't need a token
|
|
58
|
+
if (this.config.useEmulator) {
|
|
59
|
+
if (this.debug) {
|
|
60
|
+
console.log("Emulator mode: skipping token generation");
|
|
61
|
+
}
|
|
62
|
+
return "emulator-fake-token";
|
|
63
|
+
}
|
|
51
64
|
const now = Date.now();
|
|
52
65
|
// トークンが期限切れか未取得の場合は新しく取得
|
|
53
66
|
if (!this.token || now >= this.tokenExpiry) {
|
|
67
|
+
if (this.debug) {
|
|
68
|
+
console.log("Generating new auth token");
|
|
69
|
+
}
|
|
54
70
|
this.token = await (0, auth_1.getFirestoreToken)(this.config);
|
|
55
71
|
// 50分後に期限切れとする(実際は1時間)
|
|
56
72
|
this.tokenExpiry = now + 50 * 60 * 1000;
|
|
@@ -58,23 +74,44 @@ class FirestoreClient {
|
|
|
58
74
|
return this.token;
|
|
59
75
|
}
|
|
60
76
|
/**
|
|
61
|
-
*
|
|
62
|
-
* @param
|
|
63
|
-
* @returns
|
|
77
|
+
* Prepare request headers
|
|
78
|
+
* @param additionalHeaders Additional headers
|
|
79
|
+
* @returns Prepared headers object
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
async prepareHeaders(additionalHeaders = {}) {
|
|
83
|
+
const headers = {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
...additionalHeaders,
|
|
86
|
+
};
|
|
87
|
+
// Only add auth token for production environment
|
|
88
|
+
if (!this.config.useEmulator) {
|
|
89
|
+
const token = await this.getToken();
|
|
90
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
91
|
+
}
|
|
92
|
+
else if (this.debug) {
|
|
93
|
+
console.log("Using emulator mode, skipping authorization header");
|
|
94
|
+
}
|
|
95
|
+
return headers;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get collection reference
|
|
99
|
+
* @param path Collection path
|
|
100
|
+
* @returns CollectionReference instance
|
|
64
101
|
*/
|
|
65
102
|
collection(path) {
|
|
66
|
-
//
|
|
103
|
+
// Configuration check is performed at the time of actual operation
|
|
67
104
|
return new CollectionReference(this, path);
|
|
68
105
|
}
|
|
69
106
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @param path
|
|
72
|
-
* @returns DocumentReference
|
|
107
|
+
* Get document reference
|
|
108
|
+
* @param path Document path
|
|
109
|
+
* @returns DocumentReference instance
|
|
73
110
|
*/
|
|
74
111
|
doc(path) {
|
|
75
|
-
//
|
|
112
|
+
// Configuration check is performed at the time of actual operation
|
|
76
113
|
const parts = path.split("/");
|
|
77
|
-
if (parts.length % 2
|
|
114
|
+
if (parts.length % 2 !== 0) {
|
|
78
115
|
throw new Error("Invalid document path. Document path must point to a document, not a collection.");
|
|
79
116
|
}
|
|
80
117
|
const collectionPath = parts.slice(0, parts.length - 1).join("/");
|
|
@@ -82,81 +119,116 @@ class FirestoreClient {
|
|
|
82
119
|
return new DocumentReference(this, collectionPath, docId);
|
|
83
120
|
}
|
|
84
121
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @param path
|
|
87
|
-
* @returns CollectionGroup
|
|
122
|
+
* Get collection group reference
|
|
123
|
+
* @param path Collection group ID
|
|
124
|
+
* @returns CollectionGroup instance
|
|
88
125
|
*/
|
|
89
126
|
collectionGroup(path) {
|
|
90
127
|
return new CollectionGroup(this, path);
|
|
91
128
|
}
|
|
92
129
|
/**
|
|
93
|
-
* Firestore
|
|
94
|
-
* @param collectionName
|
|
95
|
-
* @param data
|
|
96
|
-
* @returns
|
|
130
|
+
* Add document to Firestore
|
|
131
|
+
* @param collectionName Collection name
|
|
132
|
+
* @param data Data to add
|
|
133
|
+
* @returns Added document
|
|
97
134
|
*/
|
|
98
135
|
async add(collectionName, data) {
|
|
99
|
-
//
|
|
136
|
+
// Check settings before operation
|
|
100
137
|
this.checkConfig();
|
|
101
|
-
|
|
138
|
+
if (this.debug) {
|
|
139
|
+
console.log(`Adding document to collection: ${collectionName}`, data);
|
|
140
|
+
}
|
|
141
|
+
const url = this.pathUtil.getCollectionPath(collectionName);
|
|
102
142
|
const firestoreData = (0, converter_1.convertToFirestoreDocument)(data);
|
|
103
|
-
|
|
143
|
+
if (this.debug) {
|
|
144
|
+
console.log(`Making request to: ${url}`, firestoreData);
|
|
145
|
+
}
|
|
146
|
+
const headers = await this.prepareHeaders();
|
|
104
147
|
const response = await fetch(url, {
|
|
105
148
|
method: "POST",
|
|
106
|
-
headers
|
|
107
|
-
"Content-Type": "application/json",
|
|
108
|
-
Authorization: `Bearer ${token}`,
|
|
109
|
-
},
|
|
149
|
+
headers,
|
|
110
150
|
body: JSON.stringify(firestoreData),
|
|
111
151
|
});
|
|
152
|
+
if (this.debug) {
|
|
153
|
+
console.log(`Response status: ${response.status}`);
|
|
154
|
+
}
|
|
112
155
|
if (!response.ok) {
|
|
113
|
-
|
|
156
|
+
const errorText = await response.text();
|
|
157
|
+
if (this.debug) {
|
|
158
|
+
console.error(`Error response: ${errorText}`);
|
|
159
|
+
}
|
|
160
|
+
throw new Error(`Firestore API error: ${response.statusText || response.status} - ${errorText}`);
|
|
114
161
|
}
|
|
115
162
|
const result = (await response.json());
|
|
116
163
|
return (0, converter_1.convertFromFirestoreDocument)(result);
|
|
117
164
|
}
|
|
118
165
|
/**
|
|
119
|
-
*
|
|
120
|
-
* @param collectionName
|
|
121
|
-
* @param documentId
|
|
122
|
-
* @returns
|
|
166
|
+
* Get document
|
|
167
|
+
* @param collectionName Collection name
|
|
168
|
+
* @param documentId Document ID
|
|
169
|
+
* @returns Retrieved document (null if it doesn't exist)
|
|
123
170
|
*/
|
|
124
171
|
async get(collectionName, documentId) {
|
|
125
|
-
//
|
|
172
|
+
// Check settings before operation
|
|
126
173
|
this.checkConfig();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const response = await fetch(url, {
|
|
130
|
-
method: "GET",
|
|
131
|
-
headers: {
|
|
132
|
-
Authorization: `Bearer ${token}`,
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
if (response.status === 404) {
|
|
136
|
-
return null;
|
|
174
|
+
if (this.debug) {
|
|
175
|
+
console.log(`Getting document from collection: ${collectionName}, documentId: ${documentId}`);
|
|
137
176
|
}
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
const url = this.pathUtil.getDocumentPath(collectionName, documentId);
|
|
178
|
+
if (this.debug) {
|
|
179
|
+
console.log(`Making request to: ${url}`);
|
|
180
|
+
}
|
|
181
|
+
const headers = await this.prepareHeaders();
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch(url, {
|
|
184
|
+
method: "GET",
|
|
185
|
+
headers,
|
|
186
|
+
});
|
|
187
|
+
if (this.debug) {
|
|
188
|
+
console.log(`Response status: ${response.status}`);
|
|
189
|
+
}
|
|
190
|
+
// Capture response text for debugging
|
|
191
|
+
const responseText = await response.text();
|
|
192
|
+
if (this.debug) {
|
|
193
|
+
console.log(`Response text: ${responseText.substring(0, 200)}${responseText.length > 200 ? "..." : ""}`);
|
|
194
|
+
}
|
|
195
|
+
if (response.status === 404) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
throw new Error(`Firestore API error: ${response.statusText || response.status} - ${responseText}`);
|
|
200
|
+
}
|
|
201
|
+
// Parse the response text
|
|
202
|
+
const result = JSON.parse(responseText);
|
|
203
|
+
return (0, converter_1.convertFromFirestoreDocument)(result);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error("Error in get method:", error);
|
|
207
|
+
throw error;
|
|
140
208
|
}
|
|
141
|
-
const result = (await response.json());
|
|
142
|
-
return (0, converter_1.convertFromFirestoreDocument)(result);
|
|
143
209
|
}
|
|
144
210
|
/**
|
|
145
|
-
*
|
|
146
|
-
* @param collectionName
|
|
147
|
-
* @param documentId
|
|
148
|
-
* @param data
|
|
149
|
-
* @returns
|
|
211
|
+
* Update document
|
|
212
|
+
* @param collectionName Collection name
|
|
213
|
+
* @param documentId Document ID
|
|
214
|
+
* @param data Data to update
|
|
215
|
+
* @returns Updated document
|
|
150
216
|
*/
|
|
151
217
|
async update(collectionName, documentId, data) {
|
|
152
|
-
//
|
|
218
|
+
// Check settings before operation
|
|
153
219
|
this.checkConfig();
|
|
154
|
-
|
|
155
|
-
|
|
220
|
+
if (this.debug) {
|
|
221
|
+
console.log(`Updating document in collection: ${collectionName}, documentId: ${documentId}`, data);
|
|
222
|
+
}
|
|
223
|
+
const url = this.pathUtil.getDocumentPath(collectionName, documentId);
|
|
224
|
+
if (this.debug) {
|
|
225
|
+
console.log(`Making request to: ${url}`);
|
|
226
|
+
}
|
|
227
|
+
// Get existing document and merge
|
|
156
228
|
const existingDoc = await this.get(collectionName, documentId);
|
|
157
229
|
if (existingDoc) {
|
|
158
|
-
//
|
|
159
|
-
// data
|
|
230
|
+
// Check for nested fields
|
|
231
|
+
// Check if data contains dot notation keys (e.g., "favorites.color")
|
|
160
232
|
const updateData = { ...data };
|
|
161
233
|
const dotNotationKeys = Object.keys(data).filter(key => key.includes("."));
|
|
162
234
|
if (dotNotationKeys.length > 0) {
|
|
@@ -195,186 +267,263 @@ class FirestoreClient {
|
|
|
195
267
|
}
|
|
196
268
|
}
|
|
197
269
|
const firestoreData = (0, converter_1.convertToFirestoreDocument)(data);
|
|
198
|
-
const
|
|
270
|
+
const headers = await this.prepareHeaders();
|
|
199
271
|
const response = await fetch(url, {
|
|
200
272
|
method: "PATCH",
|
|
201
|
-
headers
|
|
202
|
-
"Content-Type": "application/json",
|
|
203
|
-
Authorization: `Bearer ${token}`,
|
|
204
|
-
},
|
|
273
|
+
headers,
|
|
205
274
|
body: JSON.stringify(firestoreData),
|
|
206
275
|
});
|
|
276
|
+
if (this.debug) {
|
|
277
|
+
console.log(`Response status: ${response.status}`);
|
|
278
|
+
}
|
|
207
279
|
if (!response.ok) {
|
|
208
|
-
|
|
280
|
+
const errorText = await response.text();
|
|
281
|
+
if (this.debug) {
|
|
282
|
+
console.error(`Error response: ${errorText}`);
|
|
283
|
+
}
|
|
284
|
+
throw new Error(`Firestore API error: ${response.statusText || response.status} - ${errorText}`);
|
|
209
285
|
}
|
|
210
286
|
const result = (await response.json());
|
|
211
287
|
return (0, converter_1.convertFromFirestoreDocument)(result);
|
|
212
288
|
}
|
|
213
289
|
/**
|
|
214
|
-
*
|
|
215
|
-
* @param collectionName
|
|
216
|
-
* @param documentId
|
|
217
|
-
* @returns
|
|
290
|
+
* Delete document
|
|
291
|
+
* @param collectionName Collection name
|
|
292
|
+
* @param documentId Document ID
|
|
293
|
+
* @returns true if deletion successful
|
|
218
294
|
*/
|
|
219
295
|
async delete(collectionName, documentId) {
|
|
220
|
-
//
|
|
296
|
+
// Check settings before operation
|
|
221
297
|
this.checkConfig();
|
|
222
|
-
|
|
223
|
-
|
|
298
|
+
if (this.debug) {
|
|
299
|
+
console.log(`Deleting document from collection: ${collectionName}, documentId: ${documentId}`);
|
|
300
|
+
}
|
|
301
|
+
const url = this.pathUtil.getDocumentPath(collectionName, documentId);
|
|
302
|
+
if (this.debug) {
|
|
303
|
+
console.log(`Making request to: ${url}`);
|
|
304
|
+
}
|
|
305
|
+
// Different header handling for emulator
|
|
306
|
+
const headers = {};
|
|
307
|
+
// Only add auth token for production environment
|
|
308
|
+
if (!this.config.useEmulator) {
|
|
309
|
+
const token = await this.getToken();
|
|
310
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
311
|
+
}
|
|
224
312
|
const response = await fetch(url, {
|
|
225
313
|
method: "DELETE",
|
|
226
|
-
headers
|
|
227
|
-
Authorization: `Bearer ${token}`,
|
|
228
|
-
},
|
|
314
|
+
headers,
|
|
229
315
|
});
|
|
316
|
+
if (this.debug) {
|
|
317
|
+
console.log(`Response status: ${response.status}`);
|
|
318
|
+
}
|
|
230
319
|
if (!response.ok) {
|
|
231
|
-
|
|
320
|
+
const errorText = await response.text();
|
|
321
|
+
if (this.debug) {
|
|
322
|
+
console.error(`Error response: ${errorText}`);
|
|
323
|
+
}
|
|
324
|
+
throw new Error(`Firestore API error: ${response.statusText || response.status} - ${errorText}`);
|
|
232
325
|
}
|
|
233
326
|
return true;
|
|
234
327
|
}
|
|
235
328
|
/**
|
|
236
|
-
*
|
|
237
|
-
* @param collectionPath
|
|
238
|
-
* @param options
|
|
239
|
-
* @param allDescendants
|
|
240
|
-
* @returns
|
|
329
|
+
* Query documents in a collection
|
|
330
|
+
* @param collectionPath Collection path
|
|
331
|
+
* @param options Query options
|
|
332
|
+
* @param allDescendants Whether to include descendant collections
|
|
333
|
+
* @returns Array of documents matching the query
|
|
241
334
|
*/
|
|
242
|
-
async query(collectionPath, options = {}, allDescendants) {
|
|
243
|
-
//
|
|
335
|
+
async query(collectionPath, options = {}, allDescendants = false) {
|
|
336
|
+
// Check settings before operation
|
|
244
337
|
this.checkConfig();
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
338
|
+
try {
|
|
339
|
+
// Parse the collection path
|
|
340
|
+
const segments = collectionPath.split("/");
|
|
341
|
+
const collectionId = segments[segments.length - 1];
|
|
342
|
+
// Get the proper runQuery URL from our path helper
|
|
343
|
+
const queryUrl = this.pathUtil.getRunQueryPath(collectionPath);
|
|
344
|
+
if (this.debug) {
|
|
345
|
+
console.log(`Executing query on collection: ${collectionPath}`);
|
|
346
|
+
console.log(`Using runQuery URL: ${queryUrl}`);
|
|
347
|
+
}
|
|
348
|
+
// Create the structured query
|
|
349
|
+
const requestBody = {
|
|
350
|
+
structuredQuery: {
|
|
351
|
+
from: [
|
|
352
|
+
{
|
|
353
|
+
collectionId,
|
|
354
|
+
allDescendants,
|
|
355
|
+
},
|
|
356
|
+
],
|
|
264
357
|
},
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
358
|
+
};
|
|
359
|
+
// Add where filters if present
|
|
360
|
+
if (options.where && options.where.length > 0) {
|
|
361
|
+
// Map our operators to Firestore REST API operators
|
|
362
|
+
const opMap = {
|
|
363
|
+
"==": "EQUAL",
|
|
364
|
+
"!=": "NOT_EQUAL",
|
|
365
|
+
"<": "LESS_THAN",
|
|
366
|
+
"<=": "LESS_THAN_OR_EQUAL",
|
|
367
|
+
">": "GREATER_THAN",
|
|
368
|
+
">=": "GREATER_THAN_OR_EQUAL",
|
|
369
|
+
"array-contains": "ARRAY_CONTAINS",
|
|
370
|
+
in: "IN",
|
|
371
|
+
"array-contains-any": "ARRAY_CONTAINS_ANY",
|
|
372
|
+
"not-in": "NOT_IN",
|
|
278
373
|
};
|
|
374
|
+
// Single where clause
|
|
375
|
+
if (options.where.length === 1) {
|
|
376
|
+
const filter = options.where[0];
|
|
377
|
+
const firestoreOp = opMap[filter.op] || filter.op;
|
|
378
|
+
requestBody.structuredQuery.where = {
|
|
379
|
+
fieldFilter: {
|
|
380
|
+
field: { fieldPath: filter.field },
|
|
381
|
+
op: firestoreOp,
|
|
382
|
+
value: (0, converter_1.convertToFirestoreValue)(filter.value),
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
// Multiple where clauses (AND)
|
|
387
|
+
else {
|
|
388
|
+
requestBody.structuredQuery.where = {
|
|
389
|
+
compositeFilter: {
|
|
390
|
+
op: "AND",
|
|
391
|
+
filters: options.where.map(filter => {
|
|
392
|
+
const firestoreOp = opMap[filter.op] || filter.op;
|
|
393
|
+
return {
|
|
394
|
+
fieldFilter: {
|
|
395
|
+
field: { fieldPath: filter.field },
|
|
396
|
+
op: firestoreOp,
|
|
397
|
+
value: (0, converter_1.convertToFirestoreValue)(filter.value),
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
}),
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
279
404
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
structuredQuery.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
fieldFilter: {
|
|
287
|
-
field: { fieldPath: condition.field },
|
|
288
|
-
op: condition.op,
|
|
289
|
-
value: (0, converter_1.convertToFirestoreValue)(condition.value),
|
|
290
|
-
},
|
|
291
|
-
})),
|
|
405
|
+
// Add order by if present
|
|
406
|
+
if (options.orderBy) {
|
|
407
|
+
requestBody.structuredQuery.orderBy = [
|
|
408
|
+
{
|
|
409
|
+
field: { fieldPath: options.orderBy },
|
|
410
|
+
direction: options.orderDirection || "ASCENDING",
|
|
292
411
|
},
|
|
293
|
-
|
|
412
|
+
];
|
|
294
413
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// オフセット
|
|
310
|
-
if (options.offset) {
|
|
311
|
-
structuredQuery.offset = options.offset;
|
|
312
|
-
}
|
|
313
|
-
const token = await this.getToken();
|
|
314
|
-
// 修正: structuredQueryをラップしたリクエストボディを作成
|
|
315
|
-
const requestBody = {
|
|
316
|
-
structuredQuery: structuredQuery,
|
|
317
|
-
};
|
|
318
|
-
console.log("クエリリクエスト:", JSON.stringify(requestBody, null, 2));
|
|
319
|
-
try {
|
|
320
|
-
const response = await fetch(url, {
|
|
414
|
+
// Add limit if present
|
|
415
|
+
if (options.limit) {
|
|
416
|
+
requestBody.structuredQuery.limit = options.limit;
|
|
417
|
+
}
|
|
418
|
+
// Add offset if present
|
|
419
|
+
if (options.offset) {
|
|
420
|
+
requestBody.structuredQuery.offset = options.offset;
|
|
421
|
+
}
|
|
422
|
+
if (this.debug) {
|
|
423
|
+
console.log(`Request payload:`, JSON.stringify(requestBody, null, 2));
|
|
424
|
+
}
|
|
425
|
+
// Use the existing prepareHeaders method for authentication consistency
|
|
426
|
+
const headers = await this.prepareHeaders();
|
|
427
|
+
const response = await fetch(queryUrl, {
|
|
321
428
|
method: "POST",
|
|
322
|
-
headers
|
|
323
|
-
"Content-Type": "application/json",
|
|
324
|
-
Authorization: `Bearer ${token}`,
|
|
325
|
-
},
|
|
429
|
+
headers,
|
|
326
430
|
body: JSON.stringify(requestBody),
|
|
327
431
|
});
|
|
432
|
+
// Collect response for debugging
|
|
328
433
|
const responseText = await response.text();
|
|
329
|
-
|
|
434
|
+
if (this.debug) {
|
|
435
|
+
console.log(`API Response:`, responseText);
|
|
436
|
+
}
|
|
330
437
|
if (!response.ok) {
|
|
331
|
-
throw new Error(`Firestore API error: ${response.
|
|
438
|
+
throw new Error(`Firestore API error: ${response.status} - ${responseText}`);
|
|
332
439
|
}
|
|
440
|
+
// Parse the response
|
|
333
441
|
const results = JSON.parse(responseText);
|
|
334
|
-
|
|
442
|
+
if (this.debug) {
|
|
443
|
+
console.log(`Results count: ${results?.length || 0}`);
|
|
444
|
+
}
|
|
445
|
+
// Process the results
|
|
446
|
+
if (!Array.isArray(results)) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
335
449
|
const convertedResults = results
|
|
336
450
|
.filter(item => item.document)
|
|
337
451
|
.map(item => (0, converter_1.convertFromFirestoreDocument)(item.document));
|
|
338
|
-
|
|
452
|
+
if (this.debug) {
|
|
453
|
+
console.log(`Converted results:`, convertedResults);
|
|
454
|
+
}
|
|
339
455
|
return convertedResults;
|
|
340
456
|
}
|
|
341
457
|
catch (error) {
|
|
342
|
-
console.error("
|
|
458
|
+
console.error("Query execution error:", error);
|
|
343
459
|
throw error;
|
|
344
460
|
}
|
|
345
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* ドキュメントを作成または上書き
|
|
464
|
+
* @param collectionName コレクション名
|
|
465
|
+
* @param documentId ドキュメントID
|
|
466
|
+
* @param data ドキュメントデータ
|
|
467
|
+
* @returns 作成されたドキュメントのリファレンス
|
|
468
|
+
*/
|
|
469
|
+
async createWithId(collectionName, documentId, data) {
|
|
470
|
+
// 操作前に設定をチェック
|
|
471
|
+
this.checkConfig();
|
|
472
|
+
const url = `${(0, path_1.getFirestoreBasePath)(this.config.projectId, this.config.databaseId, this.config)}/${collectionName}/${documentId}`;
|
|
473
|
+
const firestoreData = (0, converter_1.convertToFirestoreDocument)(data);
|
|
474
|
+
const token = await this.getToken();
|
|
475
|
+
const response = await fetch(url, {
|
|
476
|
+
method: "PATCH",
|
|
477
|
+
headers: {
|
|
478
|
+
"Content-Type": "application/json",
|
|
479
|
+
Authorization: `Bearer ${token}`,
|
|
480
|
+
},
|
|
481
|
+
body: JSON.stringify(firestoreData),
|
|
482
|
+
});
|
|
483
|
+
if (!response.ok) {
|
|
484
|
+
throw new Error(`Firestore API error: ${response.statusText}`);
|
|
485
|
+
}
|
|
486
|
+
const result = (await response.json());
|
|
487
|
+
return (0, converter_1.convertFromFirestoreDocument)(result);
|
|
488
|
+
}
|
|
346
489
|
}
|
|
347
490
|
exports.FirestoreClient = FirestoreClient;
|
|
348
491
|
/**
|
|
349
|
-
*
|
|
492
|
+
* Collection reference class
|
|
350
493
|
*/
|
|
351
494
|
class CollectionReference {
|
|
352
495
|
constructor(client, path) {
|
|
353
496
|
this.client = client;
|
|
354
|
-
this.
|
|
497
|
+
this._path = path;
|
|
355
498
|
this._queryConstraints = {
|
|
356
499
|
where: [],
|
|
357
500
|
};
|
|
358
501
|
}
|
|
359
502
|
/**
|
|
360
|
-
*
|
|
503
|
+
* Get collection path
|
|
504
|
+
*/
|
|
505
|
+
get path() {
|
|
506
|
+
return this._path;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Whether to include all descendant collections
|
|
361
510
|
*/
|
|
362
511
|
get allDescendants() {
|
|
363
512
|
return false;
|
|
364
513
|
}
|
|
365
514
|
/**
|
|
366
|
-
*
|
|
367
|
-
* @param documentPath
|
|
368
|
-
* @returns DocumentReference
|
|
515
|
+
* Get document reference
|
|
516
|
+
* @param documentPath Document ID (auto-generated if omitted)
|
|
517
|
+
* @returns DocumentReference instance
|
|
369
518
|
*/
|
|
370
519
|
doc(documentPath) {
|
|
371
520
|
const docId = documentPath || this._generateId();
|
|
372
521
|
return new DocumentReference(this.client, this.path, docId);
|
|
373
522
|
}
|
|
374
523
|
/**
|
|
375
|
-
*
|
|
376
|
-
* @param data
|
|
377
|
-
* @returns
|
|
524
|
+
* Add document (ID is auto-generated)
|
|
525
|
+
* @param data Document data
|
|
526
|
+
* @returns Reference to the created document
|
|
378
527
|
*/
|
|
379
528
|
async add(data) {
|
|
380
529
|
const result = await this.client.add(this.path, data);
|
|
@@ -382,17 +531,17 @@ class CollectionReference {
|
|
|
382
531
|
return new DocumentReference(this.client, this.path, docId);
|
|
383
532
|
}
|
|
384
533
|
/**
|
|
385
|
-
*
|
|
386
|
-
* @param fieldPath
|
|
387
|
-
* @param opStr
|
|
388
|
-
* @param value
|
|
389
|
-
* @returns Query
|
|
534
|
+
* Add filter condition
|
|
535
|
+
* @param fieldPath Field path
|
|
536
|
+
* @param opStr Operator
|
|
537
|
+
* @param value Value
|
|
538
|
+
* @returns Query instance
|
|
390
539
|
*/
|
|
391
540
|
where(fieldPath, opStr, value) {
|
|
392
541
|
const query = new Query(this.client, this.path, {
|
|
393
542
|
...this._queryConstraints,
|
|
394
543
|
}, this.allDescendants);
|
|
395
|
-
//
|
|
544
|
+
// Operator conversion
|
|
396
545
|
let firestoreOp;
|
|
397
546
|
switch (opStr) {
|
|
398
547
|
case "==":
|
|
@@ -436,10 +585,10 @@ class CollectionReference {
|
|
|
436
585
|
return query;
|
|
437
586
|
}
|
|
438
587
|
/**
|
|
439
|
-
*
|
|
440
|
-
* @param fieldPath
|
|
441
|
-
* @param directionStr
|
|
442
|
-
* @returns Query
|
|
588
|
+
* Add sorting condition
|
|
589
|
+
* @param fieldPath Field path
|
|
590
|
+
* @param directionStr Sort direction ('asc' or 'desc')
|
|
591
|
+
* @returns Query instance
|
|
443
592
|
*/
|
|
444
593
|
orderBy(fieldPath, directionStr = "asc") {
|
|
445
594
|
const query = new Query(this.client, this.path, {
|
|
@@ -451,9 +600,9 @@ class CollectionReference {
|
|
|
451
600
|
return query;
|
|
452
601
|
}
|
|
453
602
|
/**
|
|
454
|
-
*
|
|
455
|
-
* @param limit
|
|
456
|
-
* @returns Query
|
|
603
|
+
* Set limit on number of results
|
|
604
|
+
* @param limit Maximum number
|
|
605
|
+
* @returns Query instance
|
|
457
606
|
*/
|
|
458
607
|
limit(limit) {
|
|
459
608
|
const query = new Query(this.client, this.path, {
|
|
@@ -463,9 +612,9 @@ class CollectionReference {
|
|
|
463
612
|
return query;
|
|
464
613
|
}
|
|
465
614
|
/**
|
|
466
|
-
*
|
|
467
|
-
* @param offset
|
|
468
|
-
* @returns Query
|
|
615
|
+
* Set number of documents to skip
|
|
616
|
+
* @param offset Number to skip
|
|
617
|
+
* @returns Query instance
|
|
469
618
|
*/
|
|
470
619
|
offset(offset) {
|
|
471
620
|
const query = new Query(this.client, this.path, {
|
|
@@ -475,19 +624,19 @@ class CollectionReference {
|
|
|
475
624
|
return query;
|
|
476
625
|
}
|
|
477
626
|
/**
|
|
478
|
-
*
|
|
479
|
-
* @returns QuerySnapshot
|
|
627
|
+
* Execute query
|
|
628
|
+
* @returns QuerySnapshot instance
|
|
480
629
|
*/
|
|
481
630
|
async get() {
|
|
482
631
|
const results = await this.client.query(this.path, this._queryConstraints, this.allDescendants);
|
|
483
632
|
return new QuerySnapshot(results);
|
|
484
633
|
}
|
|
485
634
|
/**
|
|
486
|
-
*
|
|
487
|
-
* @returns
|
|
635
|
+
* Generate random ID
|
|
636
|
+
* @returns Random ID
|
|
488
637
|
*/
|
|
489
638
|
_generateId() {
|
|
490
|
-
// 20
|
|
639
|
+
// Generate 20-character random ID
|
|
491
640
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
492
641
|
let id = "";
|
|
493
642
|
for (let i = 0; i < 20; i++) {
|
|
@@ -498,7 +647,7 @@ class CollectionReference {
|
|
|
498
647
|
}
|
|
499
648
|
exports.CollectionReference = CollectionReference;
|
|
500
649
|
/**
|
|
501
|
-
*
|
|
650
|
+
* Document reference class
|
|
502
651
|
*/
|
|
503
652
|
class DocumentReference {
|
|
504
653
|
constructor(client, collectionPath, docId) {
|
|
@@ -507,66 +656,71 @@ class DocumentReference {
|
|
|
507
656
|
this.docId = docId;
|
|
508
657
|
}
|
|
509
658
|
/**
|
|
510
|
-
*
|
|
659
|
+
* Get document ID
|
|
511
660
|
*/
|
|
512
661
|
get id() {
|
|
513
662
|
return this.docId;
|
|
514
663
|
}
|
|
515
664
|
/**
|
|
516
|
-
*
|
|
665
|
+
* Get document path
|
|
517
666
|
*/
|
|
518
667
|
get path() {
|
|
519
668
|
return `${this.collectionPath}/${this.docId}`;
|
|
520
669
|
}
|
|
521
670
|
/**
|
|
522
|
-
*
|
|
523
|
-
|
|
524
|
-
|
|
671
|
+
* Get parent collection reference
|
|
672
|
+
*/
|
|
673
|
+
get parent() {
|
|
674
|
+
return new CollectionReference(this.client, this.collectionPath);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Get subcollection
|
|
678
|
+
* @param collectionPath Subcollection name
|
|
679
|
+
* @returns CollectionReference instance
|
|
525
680
|
*/
|
|
526
681
|
collection(collectionPath) {
|
|
527
682
|
return new CollectionReference(this.client, `${this.path}/${collectionPath}`);
|
|
528
683
|
}
|
|
529
684
|
/**
|
|
530
|
-
*
|
|
531
|
-
* @returns DocumentSnapshot
|
|
685
|
+
* Get document
|
|
686
|
+
* @returns DocumentSnapshot instance
|
|
532
687
|
*/
|
|
533
688
|
async get() {
|
|
534
689
|
const data = await this.client.get(this.collectionPath, this.docId);
|
|
535
690
|
return new DocumentSnapshot(this.docId, data);
|
|
536
691
|
}
|
|
537
692
|
/**
|
|
538
|
-
*
|
|
539
|
-
* @param data
|
|
540
|
-
* @param options
|
|
541
|
-
* @returns WriteResult
|
|
693
|
+
* Create or overwrite document
|
|
694
|
+
* @param data Document data
|
|
695
|
+
* @param options Options (merge is not currently supported)
|
|
696
|
+
* @returns WriteResult instance
|
|
542
697
|
*/
|
|
543
698
|
async set(data, options) {
|
|
544
|
-
//
|
|
699
|
+
// Get existing document
|
|
545
700
|
const existingDoc = await this.client.get(this.collectionPath, this.docId);
|
|
546
701
|
if (existingDoc) {
|
|
547
|
-
//
|
|
702
|
+
// If existing document exists, update
|
|
548
703
|
const mergedData = options?.merge ? { ...existingDoc, ...data } : data;
|
|
549
704
|
await this.client.update(this.collectionPath, this.docId, mergedData);
|
|
550
705
|
}
|
|
551
706
|
else {
|
|
552
|
-
//
|
|
553
|
-
|
|
554
|
-
await this.client.add(this.collectionPath, newData);
|
|
707
|
+
// New creation
|
|
708
|
+
await this.client.createWithId(this.collectionPath, this.docId, data);
|
|
555
709
|
}
|
|
556
710
|
return new WriteResult();
|
|
557
711
|
}
|
|
558
712
|
/**
|
|
559
|
-
*
|
|
560
|
-
* @param data
|
|
561
|
-
* @returns WriteResult
|
|
713
|
+
* Update document
|
|
714
|
+
* @param data Update data
|
|
715
|
+
* @returns WriteResult instance
|
|
562
716
|
*/
|
|
563
717
|
async update(data) {
|
|
564
718
|
await this.client.update(this.collectionPath, this.docId, data);
|
|
565
719
|
return new WriteResult();
|
|
566
720
|
}
|
|
567
721
|
/**
|
|
568
|
-
*
|
|
569
|
-
* @returns WriteResult
|
|
722
|
+
* Delete document
|
|
723
|
+
* @returns WriteResult instance
|
|
570
724
|
*/
|
|
571
725
|
async delete() {
|
|
572
726
|
await this.client.delete(this.collectionPath, this.docId);
|
|
@@ -575,7 +729,7 @@ class DocumentReference {
|
|
|
575
729
|
}
|
|
576
730
|
exports.DocumentReference = DocumentReference;
|
|
577
731
|
/**
|
|
578
|
-
*
|
|
732
|
+
* Collection group
|
|
579
733
|
*/
|
|
580
734
|
class CollectionGroup {
|
|
581
735
|
constructor(client, path) {
|
|
@@ -586,23 +740,23 @@ class CollectionGroup {
|
|
|
586
740
|
};
|
|
587
741
|
}
|
|
588
742
|
/**
|
|
589
|
-
*
|
|
743
|
+
* Whether to include all descendant collections
|
|
590
744
|
*/
|
|
591
745
|
get allDescendants() {
|
|
592
746
|
return true;
|
|
593
747
|
}
|
|
594
748
|
/**
|
|
595
|
-
*
|
|
596
|
-
* @param fieldPath
|
|
597
|
-
* @param opStr
|
|
598
|
-
* @param value
|
|
599
|
-
* @returns Query
|
|
749
|
+
* Add filter condition
|
|
750
|
+
* @param fieldPath Field path
|
|
751
|
+
* @param opStr Operator
|
|
752
|
+
* @param value Value
|
|
753
|
+
* @returns Query instance
|
|
600
754
|
*/
|
|
601
755
|
where(fieldPath, opStr, value) {
|
|
602
756
|
const query = new Query(this.client, this.path, {
|
|
603
757
|
...this._queryConstraints,
|
|
604
758
|
}, this.allDescendants);
|
|
605
|
-
//
|
|
759
|
+
// Operator conversion
|
|
606
760
|
let firestoreOp;
|
|
607
761
|
switch (opStr) {
|
|
608
762
|
case "==":
|
|
@@ -646,10 +800,10 @@ class CollectionGroup {
|
|
|
646
800
|
return query;
|
|
647
801
|
}
|
|
648
802
|
/**
|
|
649
|
-
*
|
|
650
|
-
* @param fieldPath
|
|
651
|
-
* @param directionStr
|
|
652
|
-
* @returns Query
|
|
803
|
+
* Add sorting condition
|
|
804
|
+
* @param fieldPath Field path
|
|
805
|
+
* @param directionStr Sort direction ('asc' or 'desc')
|
|
806
|
+
* @returns Query instance
|
|
653
807
|
*/
|
|
654
808
|
orderBy(fieldPath, directionStr = "asc") {
|
|
655
809
|
const query = new Query(this.client, this.path, {
|
|
@@ -661,9 +815,9 @@ class CollectionGroup {
|
|
|
661
815
|
return query;
|
|
662
816
|
}
|
|
663
817
|
/**
|
|
664
|
-
*
|
|
665
|
-
* @param limit
|
|
666
|
-
* @returns Query
|
|
818
|
+
* Set limit on number of results
|
|
819
|
+
* @param limit Maximum number
|
|
820
|
+
* @returns Query instance
|
|
667
821
|
*/
|
|
668
822
|
limit(limit) {
|
|
669
823
|
const query = new Query(this.client, this.path, {
|
|
@@ -673,9 +827,9 @@ class CollectionGroup {
|
|
|
673
827
|
return query;
|
|
674
828
|
}
|
|
675
829
|
/**
|
|
676
|
-
*
|
|
677
|
-
* @param offset
|
|
678
|
-
* @returns Query
|
|
830
|
+
* Set number of documents to skip
|
|
831
|
+
* @param offset Number to skip
|
|
832
|
+
* @returns Query instance
|
|
679
833
|
*/
|
|
680
834
|
offset(offset) {
|
|
681
835
|
const query = new Query(this.client, this.path, {
|
|
@@ -685,8 +839,8 @@ class CollectionGroup {
|
|
|
685
839
|
return query;
|
|
686
840
|
}
|
|
687
841
|
/**
|
|
688
|
-
*
|
|
689
|
-
* @returns QuerySnapshot
|
|
842
|
+
* Execute query
|
|
843
|
+
* @returns QuerySnapshot instance
|
|
690
844
|
*/
|
|
691
845
|
async get() {
|
|
692
846
|
const results = await this.client.query(this.path, this._queryConstraints, this.allDescendants);
|
|
@@ -695,7 +849,7 @@ class CollectionGroup {
|
|
|
695
849
|
}
|
|
696
850
|
exports.CollectionGroup = CollectionGroup;
|
|
697
851
|
/**
|
|
698
|
-
*
|
|
852
|
+
* Query class
|
|
699
853
|
*/
|
|
700
854
|
class Query {
|
|
701
855
|
constructor(client, collectionPath, constraints, allDescendants) {
|
|
@@ -705,17 +859,17 @@ class Query {
|
|
|
705
859
|
this.allDescendants = allDescendants;
|
|
706
860
|
}
|
|
707
861
|
/**
|
|
708
|
-
*
|
|
709
|
-
* @param fieldPath
|
|
710
|
-
* @param opStr
|
|
711
|
-
* @param value
|
|
712
|
-
* @returns Query
|
|
862
|
+
* Add filter condition
|
|
863
|
+
* @param fieldPath Field path
|
|
864
|
+
* @param opStr Operator
|
|
865
|
+
* @param value Value
|
|
866
|
+
* @returns Query instance
|
|
713
867
|
*/
|
|
714
868
|
where(fieldPath, opStr, value) {
|
|
715
869
|
const query = new Query(this.client, this.collectionPath, {
|
|
716
870
|
...this._queryConstraints,
|
|
717
871
|
}, this.allDescendants);
|
|
718
|
-
//
|
|
872
|
+
// Operator conversion
|
|
719
873
|
let firestoreOp;
|
|
720
874
|
switch (opStr) {
|
|
721
875
|
case "==":
|
|
@@ -759,10 +913,10 @@ class Query {
|
|
|
759
913
|
return query;
|
|
760
914
|
}
|
|
761
915
|
/**
|
|
762
|
-
*
|
|
763
|
-
* @param fieldPath
|
|
764
|
-
* @param directionStr
|
|
765
|
-
* @returns Query
|
|
916
|
+
* Add sorting condition
|
|
917
|
+
* @param fieldPath Field path
|
|
918
|
+
* @param directionStr Sort direction ('asc' or 'desc')
|
|
919
|
+
* @returns Query instance
|
|
766
920
|
*/
|
|
767
921
|
orderBy(fieldPath, directionStr = "asc") {
|
|
768
922
|
const query = new Query(this.client, this.collectionPath, {
|
|
@@ -774,9 +928,9 @@ class Query {
|
|
|
774
928
|
return query;
|
|
775
929
|
}
|
|
776
930
|
/**
|
|
777
|
-
*
|
|
778
|
-
* @param limit
|
|
779
|
-
* @returns Query
|
|
931
|
+
* Set limit on number of results
|
|
932
|
+
* @param limit Maximum number
|
|
933
|
+
* @returns Query instance
|
|
780
934
|
*/
|
|
781
935
|
limit(limit) {
|
|
782
936
|
const query = new Query(this.client, this.collectionPath, {
|
|
@@ -786,9 +940,9 @@ class Query {
|
|
|
786
940
|
return query;
|
|
787
941
|
}
|
|
788
942
|
/**
|
|
789
|
-
*
|
|
790
|
-
* @param offset
|
|
791
|
-
* @returns Query
|
|
943
|
+
* Set number of documents to skip
|
|
944
|
+
* @param offset Number to skip
|
|
945
|
+
* @returns Query instance
|
|
792
946
|
*/
|
|
793
947
|
offset(offset) {
|
|
794
948
|
const query = new Query(this.client, this.collectionPath, {
|
|
@@ -798,8 +952,8 @@ class Query {
|
|
|
798
952
|
return query;
|
|
799
953
|
}
|
|
800
954
|
/**
|
|
801
|
-
*
|
|
802
|
-
* @returns QuerySnapshot
|
|
955
|
+
* Execute query
|
|
956
|
+
* @returns QuerySnapshot instance
|
|
803
957
|
*/
|
|
804
958
|
async get() {
|
|
805
959
|
const results = await this.client.query(this.collectionPath, this._queryConstraints, this.allDescendants);
|
|
@@ -808,7 +962,7 @@ class Query {
|
|
|
808
962
|
}
|
|
809
963
|
exports.Query = Query;
|
|
810
964
|
/**
|
|
811
|
-
*
|
|
965
|
+
* Query result class
|
|
812
966
|
*/
|
|
813
967
|
class QuerySnapshot {
|
|
814
968
|
constructor(results) {
|
|
@@ -818,26 +972,26 @@ class QuerySnapshot {
|
|
|
818
972
|
});
|
|
819
973
|
}
|
|
820
974
|
/**
|
|
821
|
-
*
|
|
975
|
+
* Array of documents in the result
|
|
822
976
|
*/
|
|
823
977
|
get docs() {
|
|
824
978
|
return this._docs;
|
|
825
979
|
}
|
|
826
980
|
/**
|
|
827
|
-
*
|
|
981
|
+
* Whether the result is empty
|
|
828
982
|
*/
|
|
829
983
|
get empty() {
|
|
830
984
|
return this._docs.length === 0;
|
|
831
985
|
}
|
|
832
986
|
/**
|
|
833
|
-
*
|
|
987
|
+
* Number of results
|
|
834
988
|
*/
|
|
835
989
|
get size() {
|
|
836
990
|
return this._docs.length;
|
|
837
991
|
}
|
|
838
992
|
/**
|
|
839
|
-
*
|
|
840
|
-
* @param callback
|
|
993
|
+
* Execute callback for each document
|
|
994
|
+
* @param callback Callback function to execute for each document
|
|
841
995
|
*/
|
|
842
996
|
forEach(callback) {
|
|
843
997
|
this._docs.forEach(callback);
|
|
@@ -845,7 +999,7 @@ class QuerySnapshot {
|
|
|
845
999
|
}
|
|
846
1000
|
exports.QuerySnapshot = QuerySnapshot;
|
|
847
1001
|
/**
|
|
848
|
-
*
|
|
1002
|
+
* Document snapshot class
|
|
849
1003
|
*/
|
|
850
1004
|
class DocumentSnapshot {
|
|
851
1005
|
constructor(id, data) {
|
|
@@ -853,20 +1007,20 @@ class DocumentSnapshot {
|
|
|
853
1007
|
this._data = data;
|
|
854
1008
|
}
|
|
855
1009
|
/**
|
|
856
|
-
*
|
|
1010
|
+
* Document ID
|
|
857
1011
|
*/
|
|
858
1012
|
get id() {
|
|
859
1013
|
return this._id;
|
|
860
1014
|
}
|
|
861
1015
|
/**
|
|
862
|
-
*
|
|
1016
|
+
* Whether the document exists
|
|
863
1017
|
*/
|
|
864
1018
|
get exists() {
|
|
865
1019
|
return this._data !== null;
|
|
866
1020
|
}
|
|
867
1021
|
/**
|
|
868
|
-
*
|
|
869
|
-
* @returns
|
|
1022
|
+
* Get document data
|
|
1023
|
+
* @returns Document data (undefined if it doesn't exist)
|
|
870
1024
|
*/
|
|
871
1025
|
data() {
|
|
872
1026
|
return this._data || undefined;
|
|
@@ -874,7 +1028,7 @@ class DocumentSnapshot {
|
|
|
874
1028
|
}
|
|
875
1029
|
exports.DocumentSnapshot = DocumentSnapshot;
|
|
876
1030
|
/**
|
|
877
|
-
*
|
|
1031
|
+
* Write result class
|
|
878
1032
|
*/
|
|
879
1033
|
class WriteResult {
|
|
880
1034
|
constructor() {
|
|
@@ -883,28 +1037,37 @@ class WriteResult {
|
|
|
883
1037
|
}
|
|
884
1038
|
exports.WriteResult = WriteResult;
|
|
885
1039
|
/**
|
|
886
|
-
*
|
|
887
|
-
* @param config Firestore
|
|
888
|
-
* @returns FirestoreClient
|
|
1040
|
+
* Create a new Firestore client instance
|
|
1041
|
+
* @param config Firestore configuration object
|
|
1042
|
+
* @returns FirestoreClient instance
|
|
889
1043
|
*
|
|
890
1044
|
* @example
|
|
891
|
-
* //
|
|
1045
|
+
* // Connect to default database
|
|
892
1046
|
* const db = createFirestoreClient({
|
|
893
1047
|
* projectId: 'your-project-id',
|
|
894
1048
|
* privateKey: 'your-private-key',
|
|
895
1049
|
* clientEmail: 'your-client-email'
|
|
896
1050
|
* });
|
|
897
1051
|
*
|
|
898
|
-
* //
|
|
1052
|
+
* // Connect to a different named database
|
|
899
1053
|
* const customDb = createFirestoreClient({
|
|
900
1054
|
* projectId: 'your-project-id',
|
|
901
1055
|
* privateKey: 'your-private-key',
|
|
902
1056
|
* clientEmail: 'your-client-email',
|
|
903
1057
|
* databaseId: 'your-database-id'
|
|
904
1058
|
* });
|
|
1059
|
+
*
|
|
1060
|
+
* // Connect to local emulator (no auth required)
|
|
1061
|
+
* const emulatorDb = createFirestoreClient({
|
|
1062
|
+
* projectId: 'demo-project',
|
|
1063
|
+
* useEmulator: true,
|
|
1064
|
+
* emulatorHost: '127.0.',
|
|
1065
|
+
* emulatorPort: 8080,
|
|
1066
|
+
* debug: true // Optional: enables detailed logging
|
|
1067
|
+
* });
|
|
905
1068
|
*/
|
|
906
1069
|
function createFirestoreClient(config) {
|
|
907
|
-
//
|
|
1070
|
+
// Check private key format
|
|
908
1071
|
if (config.privateKey) {
|
|
909
1072
|
config = {
|
|
910
1073
|
...config,
|