@rebasepro/client-firebase 0.8.0 → 0.9.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/components/RebaseFirebaseAppProps.d.ts +2 -2
- package/dist/hooks/useBuildUserManagement.d.ts +1 -0
- package/dist/hooks/useFirestoreDriver.d.ts +3 -3
- package/dist/index.es.js +141 -137
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +139 -135
- package/dist/index.umd.js.map +1 -1
- package/dist/types/text_search.d.ts +3 -3
- package/dist/utils/collections_firestore.d.ts +3 -3
- package/package.json +7 -7
- package/src/components/RebaseFirebaseApp.tsx +10 -5
- package/src/components/RebaseFirebaseAppProps.tsx +2 -2
- package/src/hooks/useBuildUserManagement.tsx +27 -28
- package/src/hooks/useFirebaseRealTimeDBDelegate.ts +47 -52
- package/src/hooks/useFirebaseStorageSource.ts +2 -2
- package/src/hooks/useFirestoreDriver.ts +113 -95
- package/src/types/text_search.ts +3 -3
- package/src/utils/collections_firestore.ts +5 -5
- package/src/utils/database.ts +2 -2
- package/src/utils/local_text_search_controller.ts +6 -6
- package/src/utils/pinecone.ts +2 -2
- package/src/utils/rebase_search_controller.ts +3 -3
- package/src/utils/text_search_controller.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataDriver,
|
|
1
|
+
import { DataDriver, DeleteProps, CollectionConfig, EntityReference, FetchCollectionProps, FetchOneProps, FilterCombination, FilterValues, GeoPoint, ListenCollectionProps, ListenOneProps, SaveProps, WhereFilterOp } from "@rebasepro/types";
|
|
2
2
|
import { User } from "@firebase/auth";
|
|
3
3
|
import {
|
|
4
4
|
collection as collectionClause,
|
|
@@ -62,7 +62,7 @@ export interface FirestoreDataDriverProps {
|
|
|
62
62
|
|
|
63
63
|
export type FirestoreIndexesBuilder = (params: {
|
|
64
64
|
path: string,
|
|
65
|
-
collection:
|
|
65
|
+
collection: CollectionConfig<any>,
|
|
66
66
|
}) => FilterCombination<string>[] | undefined
|
|
67
67
|
|
|
68
68
|
export type FirestoreDataDriver = DataDriver & {
|
|
@@ -70,7 +70,7 @@ export type FirestoreDataDriver = DataDriver & {
|
|
|
70
70
|
initTextSearch: (props: {
|
|
71
71
|
path: string,
|
|
72
72
|
databaseId?: string,
|
|
73
|
-
collection?:
|
|
73
|
+
collection?: CollectionConfig
|
|
74
74
|
}) => Promise<boolean>,
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -120,6 +120,26 @@ export function useFirestoreDriver({
|
|
|
120
120
|
.filter(([_, entry]) => !!entry)
|
|
121
121
|
.forEach(([key, filterParameter]) => {
|
|
122
122
|
const [op, value] = filterParameter as [WhereFilterOp, any];
|
|
123
|
+
|
|
124
|
+
// Null-testing operators map to Firestore's == / != against null.
|
|
125
|
+
if (op === "is-null") {
|
|
126
|
+
queryParams.push(whereClause(key, "==", null));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (op === "is-not-null") {
|
|
130
|
+
queryParams.push(whereClause(key, "!=", null));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Firestore has no LIKE/ILIKE — fail loudly rather than silently
|
|
135
|
+
// returning wrong results. Use `searchString` / a search index instead.
|
|
136
|
+
if (op === "like" || op === "ilike" || op === "not-like" || op === "not-ilike") {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Firestore does not support the "${op}" operator (SQL pattern matching). ` +
|
|
139
|
+
"Use a full-text search index or the collection's searchString instead."
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
123
143
|
queryParams.push(whereClause(key, op, cmsToFirestoreModel(value, firestore)));
|
|
124
144
|
});
|
|
125
145
|
}
|
|
@@ -139,31 +159,31 @@ export function useFirestoreDriver({
|
|
|
139
159
|
return query(collectionReference, ...queryParams);
|
|
140
160
|
}, [firebaseApp]);
|
|
141
161
|
|
|
142
|
-
const getAndBuildEntity = useCallback(
|
|
143
|
-
|
|
162
|
+
const getAndBuildEntity = useCallback((path: string,
|
|
163
|
+
id: string | number,
|
|
144
164
|
databaseId?: string
|
|
145
|
-
): Promise<
|
|
165
|
+
): Promise<Record<string, unknown> | undefined> => {
|
|
146
166
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
147
167
|
|
|
148
168
|
const firestore = databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp);
|
|
149
169
|
|
|
150
|
-
return getDoc(doc(firestore, path, String(
|
|
151
|
-
.then((
|
|
152
|
-
if (!
|
|
170
|
+
return getDoc(doc(firestore, path, String(id)))
|
|
171
|
+
.then((docEntity) => {
|
|
172
|
+
if (!docEntity.exists()) {
|
|
153
173
|
return undefined;
|
|
154
174
|
}
|
|
155
|
-
return
|
|
175
|
+
return createRowFromDocument(docEntity);
|
|
156
176
|
});
|
|
157
177
|
}, [firebaseApp]);
|
|
158
178
|
|
|
159
|
-
const
|
|
179
|
+
const listenOne = useCallback(<M extends Record<string, any>>(
|
|
160
180
|
{
|
|
161
181
|
path,
|
|
162
|
-
|
|
182
|
+
id,
|
|
163
183
|
collection,
|
|
164
184
|
onUpdate,
|
|
165
185
|
onError
|
|
166
|
-
}:
|
|
186
|
+
}: ListenOneProps<M>): () => void => {
|
|
167
187
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
168
188
|
|
|
169
189
|
const databaseId = collection?.databaseId;
|
|
@@ -171,10 +191,10 @@ export function useFirestoreDriver({
|
|
|
171
191
|
const resolvedPath = path;
|
|
172
192
|
|
|
173
193
|
return onSnapshot(
|
|
174
|
-
doc(firestore, resolvedPath, String(
|
|
194
|
+
doc(firestore, resolvedPath, String(id)),
|
|
175
195
|
{
|
|
176
|
-
next: (
|
|
177
|
-
onUpdate(
|
|
196
|
+
next: (docEntity) => {
|
|
197
|
+
onUpdate(docEntity.exists() ? createRowFromDocument(docEntity) : null);
|
|
178
198
|
},
|
|
179
199
|
error: onError
|
|
180
200
|
}
|
|
@@ -190,7 +210,7 @@ export function useFirestoreDriver({
|
|
|
190
210
|
path: string,
|
|
191
211
|
databaseId?: string,
|
|
192
212
|
searchString: string;
|
|
193
|
-
onUpdate: (
|
|
213
|
+
onUpdate: (rows: Record<string, unknown>[]) => void,
|
|
194
214
|
}): () => void => {
|
|
195
215
|
|
|
196
216
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
@@ -222,23 +242,24 @@ export function useFirestoreDriver({
|
|
|
222
242
|
onUpdate([]);
|
|
223
243
|
}
|
|
224
244
|
|
|
225
|
-
const
|
|
245
|
+
const rows: Record<string, unknown>[] = [];
|
|
226
246
|
const addedEntitiesSet = new Set<string | number>();
|
|
227
247
|
subscriptions = (ids ?? [])
|
|
228
|
-
.map((
|
|
229
|
-
return
|
|
248
|
+
.map((id) => {
|
|
249
|
+
return listenOne({
|
|
230
250
|
path,
|
|
231
|
-
|
|
232
|
-
onUpdate: (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
251
|
+
id,
|
|
252
|
+
onUpdate: (row: Record<string, unknown> | null) => {
|
|
253
|
+
const incomingId = row?.id as string | number | undefined;
|
|
254
|
+
if (row && incomingId !== undefined) {
|
|
255
|
+
if (!addedEntitiesSet.has(incomingId)) {
|
|
256
|
+
addedEntitiesSet.add(incomingId);
|
|
257
|
+
rows.push(row);
|
|
258
|
+
onUpdate(rows);
|
|
238
259
|
}
|
|
239
|
-
} else
|
|
240
|
-
addedEntitiesSet.delete(
|
|
241
|
-
onUpdate([...
|
|
260
|
+
} else {
|
|
261
|
+
addedEntitiesSet.delete(id);
|
|
262
|
+
onUpdate([...rows.filter(r => r.id !== id)])
|
|
242
263
|
}
|
|
243
264
|
}
|
|
244
265
|
})
|
|
@@ -250,12 +271,12 @@ export function useFirestoreDriver({
|
|
|
250
271
|
subscriptions.forEach((p) => p());
|
|
251
272
|
}
|
|
252
273
|
|
|
253
|
-
}, [firebaseApp,
|
|
274
|
+
}, [firebaseApp, listenOne]);
|
|
254
275
|
|
|
255
276
|
const initTextSearch = useCallback(async (props: {
|
|
256
277
|
path: string,
|
|
257
278
|
databaseId?: string,
|
|
258
|
-
collection?:
|
|
279
|
+
collection?: CollectionConfig
|
|
259
280
|
}) => {
|
|
260
281
|
console.debug("Init text search controller", searchControllerRef.current, props.path);
|
|
261
282
|
if (!searchControllerRef.current) {
|
|
@@ -281,7 +302,7 @@ export function useFirestoreDriver({
|
|
|
281
302
|
* @param orderBy
|
|
282
303
|
* @param order
|
|
283
304
|
* @return Function to cancel subscription
|
|
284
|
-
* @see
|
|
305
|
+
* @see useCollection if you need this functionality implemented as a hook
|
|
285
306
|
* @group Firestore
|
|
286
307
|
*/
|
|
287
308
|
const fetchCollection = useCallback(async <M extends Record<string, any>>({
|
|
@@ -294,7 +315,7 @@ export function useFirestoreDriver({
|
|
|
294
315
|
order,
|
|
295
316
|
collection
|
|
296
317
|
}: FetchCollectionProps<M>
|
|
297
|
-
): Promise<
|
|
318
|
+
): Promise<Record<string, unknown>[]> => {
|
|
298
319
|
|
|
299
320
|
const databaseId = collection?.databaseId;
|
|
300
321
|
|
|
@@ -310,8 +331,8 @@ export function useFirestoreDriver({
|
|
|
310
331
|
});
|
|
311
332
|
const query = buildQuery(resolvedPath, filter, orderBy, order, startAfter as unknown[] | undefined, limit, databaseId);
|
|
312
333
|
|
|
313
|
-
const
|
|
314
|
-
return
|
|
334
|
+
const entity = await getDocs(query);
|
|
335
|
+
return entity.docs.map((doc) => createRowFromDocument(doc));
|
|
315
336
|
}, [buildQuery]);
|
|
316
337
|
|
|
317
338
|
/**
|
|
@@ -327,7 +348,7 @@ export function useFirestoreDriver({
|
|
|
327
348
|
* @param order
|
|
328
349
|
* @param onUpdate
|
|
329
350
|
* @return Function to cancel subscription
|
|
330
|
-
* @see
|
|
351
|
+
* @see useCollection if you need this functionality implemented as a hook
|
|
331
352
|
* @group Firestore
|
|
332
353
|
*/
|
|
333
354
|
const listenCollection = useCallback(<M extends Record<string, any>>(
|
|
@@ -379,9 +400,9 @@ export function useFirestoreDriver({
|
|
|
379
400
|
const query = buildQuery(resolvedPath, filter, orderBy, order, startAfter as unknown[] | undefined, limit, databaseId);
|
|
380
401
|
return onSnapshot(query,
|
|
381
402
|
{
|
|
382
|
-
next: (
|
|
403
|
+
next: (entity) => {
|
|
383
404
|
if (!searchString)
|
|
384
|
-
onUpdate(
|
|
405
|
+
onUpdate(entity.docs.map((doc) => createRowFromDocument(doc)));
|
|
385
406
|
},
|
|
386
407
|
error: onError
|
|
387
408
|
}
|
|
@@ -390,47 +411,47 @@ export function useFirestoreDriver({
|
|
|
390
411
|
}, [buildQuery, firebaseApp, performTextSearch]);
|
|
391
412
|
|
|
392
413
|
/**
|
|
393
|
-
* Retrieve
|
|
414
|
+
* Retrieve a entity given a path and a collection
|
|
394
415
|
* @param path
|
|
395
|
-
* @param
|
|
416
|
+
* @param id
|
|
396
417
|
* @param collection
|
|
397
418
|
* @group Firestore
|
|
398
419
|
*/
|
|
399
|
-
const
|
|
420
|
+
const fetchOne = useCallback(<M extends Record<string, any>>({
|
|
400
421
|
path,
|
|
401
|
-
|
|
422
|
+
id,
|
|
402
423
|
collection
|
|
403
|
-
}:
|
|
404
|
-
): Promise<
|
|
424
|
+
}: FetchOneProps<M>
|
|
425
|
+
): Promise<Record<string, unknown> | undefined> => {
|
|
405
426
|
const resolvedPath = path;
|
|
406
|
-
return getAndBuildEntity(resolvedPath,
|
|
427
|
+
return getAndBuildEntity(resolvedPath, id, collection?.databaseId);
|
|
407
428
|
}, [getAndBuildEntity]);
|
|
408
429
|
|
|
409
430
|
/**
|
|
410
431
|
* Save entity to the specified path. Note that Firestore does not allow
|
|
411
432
|
* undefined values.
|
|
412
433
|
* @param path
|
|
413
|
-
* @param
|
|
434
|
+
* @param id
|
|
414
435
|
* @param values
|
|
415
436
|
* @param schemaId
|
|
416
437
|
* @param collection
|
|
417
438
|
* @param status
|
|
418
439
|
* @group Firestore
|
|
419
440
|
*/
|
|
420
|
-
const
|
|
441
|
+
const save = useCallback(<M extends Record<string, any>>(
|
|
421
442
|
{
|
|
422
443
|
path,
|
|
423
|
-
|
|
444
|
+
id,
|
|
424
445
|
values: valuesProp,
|
|
425
446
|
collection,
|
|
426
447
|
status
|
|
427
|
-
}:
|
|
448
|
+
}: SaveProps<M>): Promise<Record<string, unknown>> => {
|
|
428
449
|
|
|
429
450
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
430
451
|
|
|
431
452
|
console.debug("1", {
|
|
432
453
|
path,
|
|
433
|
-
|
|
454
|
+
id,
|
|
434
455
|
values: valuesProp,
|
|
435
456
|
collection
|
|
436
457
|
})
|
|
@@ -438,7 +459,7 @@ export function useFirestoreDriver({
|
|
|
438
459
|
|
|
439
460
|
console.debug("2", {
|
|
440
461
|
path,
|
|
441
|
-
|
|
462
|
+
id,
|
|
442
463
|
values: valuesProp,
|
|
443
464
|
collection
|
|
444
465
|
})
|
|
@@ -449,24 +470,23 @@ export function useFirestoreDriver({
|
|
|
449
470
|
const collectionReference: CollectionReference = collectionClause(firestore, path);
|
|
450
471
|
console.debug("Saving entity", {
|
|
451
472
|
path,
|
|
452
|
-
|
|
473
|
+
id,
|
|
453
474
|
values,
|
|
454
475
|
databaseId
|
|
455
476
|
});
|
|
456
477
|
|
|
457
478
|
let documentReference: DocumentReference;
|
|
458
|
-
if (
|
|
459
|
-
documentReference = doc(collectionReference, String(
|
|
479
|
+
if (id) {
|
|
480
|
+
documentReference = doc(collectionReference, String(id));
|
|
460
481
|
} else {
|
|
461
482
|
documentReference = doc(collectionReference);
|
|
462
483
|
}
|
|
463
484
|
return setDoc(documentReference, values as Record<string, unknown>, { merge: true })
|
|
464
485
|
.then(() => {
|
|
465
486
|
return {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
} as Entity<M>;
|
|
487
|
+
...(firestoreToCMSModel(values) as Record<string, unknown>),
|
|
488
|
+
id: documentReference.id
|
|
489
|
+
};
|
|
470
490
|
})
|
|
471
491
|
.catch((error) => {
|
|
472
492
|
console.error("Error saving entity", error);
|
|
@@ -476,22 +496,23 @@ export function useFirestoreDriver({
|
|
|
476
496
|
}, [firebaseApp]);
|
|
477
497
|
|
|
478
498
|
/**
|
|
479
|
-
* Delete
|
|
499
|
+
* Delete a entity
|
|
480
500
|
* @param entity
|
|
481
501
|
* @param collection
|
|
482
502
|
* @group Firestore
|
|
483
503
|
*/
|
|
484
|
-
const
|
|
504
|
+
const deleteOne = useCallback(<M extends Record<string, any>>(
|
|
485
505
|
{
|
|
486
|
-
|
|
487
|
-
|
|
506
|
+
row,
|
|
507
|
+
collection
|
|
508
|
+
}: DeleteProps<M>
|
|
488
509
|
): Promise<void> => {
|
|
489
510
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
490
511
|
|
|
491
|
-
const databaseId =
|
|
512
|
+
const databaseId = collection?.databaseId;
|
|
492
513
|
const firestore = databaseId ? getFirestore(firebaseApp, databaseId) : getFirestore(firebaseApp);
|
|
493
514
|
|
|
494
|
-
return deleteDoc(doc(firestore,
|
|
515
|
+
return deleteDoc(doc(firestore, row.path, String(row.id)));
|
|
495
516
|
}, [firebaseApp]);
|
|
496
517
|
|
|
497
518
|
/**
|
|
@@ -500,7 +521,7 @@ export function useFirestoreDriver({
|
|
|
500
521
|
* @param name of the property
|
|
501
522
|
* @param value
|
|
502
523
|
* @param property
|
|
503
|
-
* @param
|
|
524
|
+
* @param id
|
|
504
525
|
* @return `true` if there are no other fields besides the given entity
|
|
505
526
|
* @group Firestore
|
|
506
527
|
*/
|
|
@@ -508,8 +529,8 @@ export function useFirestoreDriver({
|
|
|
508
529
|
path: string,
|
|
509
530
|
name: string,
|
|
510
531
|
value: unknown,
|
|
511
|
-
|
|
512
|
-
collection?:
|
|
532
|
+
id?: string | number,
|
|
533
|
+
collection?: CollectionConfig<any>
|
|
513
534
|
): Promise<boolean> => {
|
|
514
535
|
|
|
515
536
|
if (!firebaseApp) throw Error("useFirestoreDriver Firebase not initialised");
|
|
@@ -521,12 +542,12 @@ export function useFirestoreDriver({
|
|
|
521
542
|
return Promise.resolve(true);
|
|
522
543
|
}
|
|
523
544
|
const q = query(collectionClause(firestore, path), whereClause(name, "==", cmsToFirestoreModel(value, firestore)));
|
|
524
|
-
const
|
|
525
|
-
return
|
|
545
|
+
const entity = await getDocs(q);
|
|
546
|
+
return entity.docs.filter(doc => doc.id !== id).length === 0;
|
|
526
547
|
|
|
527
548
|
}, [firebaseApp]);
|
|
528
549
|
|
|
529
|
-
const
|
|
550
|
+
const count = useCallback(async ({
|
|
530
551
|
path,
|
|
531
552
|
filter,
|
|
532
553
|
order,
|
|
@@ -537,8 +558,8 @@ export function useFirestoreDriver({
|
|
|
537
558
|
const databaseId = collection?.databaseId;
|
|
538
559
|
const resolvedPath = path;
|
|
539
560
|
const query = buildQuery(resolvedPath, filter, orderBy, order, undefined, undefined, databaseId);
|
|
540
|
-
const
|
|
541
|
-
return
|
|
561
|
+
const entity = await getCountFromServer(query);
|
|
562
|
+
return entity.data().count;
|
|
542
563
|
}, [firebaseApp]);
|
|
543
564
|
|
|
544
565
|
const isFilterCombinationValid = useCallback(({
|
|
@@ -548,7 +569,7 @@ export function useFirestoreDriver({
|
|
|
548
569
|
sortBy
|
|
549
570
|
}: {
|
|
550
571
|
path: string,
|
|
551
|
-
collection:
|
|
572
|
+
collection: CollectionConfig<any>,
|
|
552
573
|
filterValues: FilterValues<any>,
|
|
553
574
|
sortBy?: [string, "asc" | "desc"],
|
|
554
575
|
}): boolean => {
|
|
@@ -600,40 +621,37 @@ export function useFirestoreDriver({
|
|
|
600
621
|
initTextSearch,
|
|
601
622
|
fetchCollection,
|
|
602
623
|
listenCollection,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
624
|
+
fetchOne,
|
|
625
|
+
listenOne,
|
|
626
|
+
save,
|
|
627
|
+
delete: deleteOne,
|
|
607
628
|
checkUniqueField,
|
|
608
|
-
|
|
629
|
+
count,
|
|
609
630
|
isFilterCombinationValid
|
|
610
631
|
}), [
|
|
611
632
|
firebaseApp,
|
|
612
633
|
initTextSearch,
|
|
613
634
|
fetchCollection,
|
|
614
635
|
listenCollection,
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
636
|
+
fetchOne,
|
|
637
|
+
listenOne,
|
|
638
|
+
save,
|
|
639
|
+
deleteOne,
|
|
619
640
|
checkUniqueField,
|
|
620
|
-
|
|
641
|
+
count,
|
|
621
642
|
isFilterCombinationValid
|
|
622
643
|
]);
|
|
623
644
|
|
|
624
645
|
}
|
|
625
646
|
|
|
626
|
-
const
|
|
627
|
-
docSnap: DocumentSnapshot
|
|
628
|
-
|
|
629
|
-
)
|
|
630
|
-
const values = firestoreToCMSModel(docSnap.data()) as M;
|
|
631
|
-
const path = getCMSPathFromFirestorePath(docSnap.ref.path);
|
|
647
|
+
const createRowFromDocument = (
|
|
648
|
+
docSnap: DocumentSnapshot
|
|
649
|
+
): Record<string, unknown> => {
|
|
650
|
+
const values = firestoreToCMSModel(docSnap.data()) as Record<string, unknown>;
|
|
632
651
|
return {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
databaseId
|
|
652
|
+
...values,
|
|
653
|
+
// Spread the canonical document id last so it wins over a literal `id` field
|
|
654
|
+
id: docSnap.id
|
|
637
655
|
};
|
|
638
656
|
};
|
|
639
657
|
|
|
@@ -770,7 +788,7 @@ function buildTextSearchControllerWithLocalSearch({
|
|
|
770
788
|
init: async (props: {
|
|
771
789
|
path: string,
|
|
772
790
|
databaseId?: string,
|
|
773
|
-
collection?:
|
|
791
|
+
collection?: CollectionConfig
|
|
774
792
|
}) => {
|
|
775
793
|
const b = await textSearchController.init(props);
|
|
776
794
|
if (b) {
|
package/src/types/text_search.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { User as FirebaseUser } from "@firebase/auth";
|
|
2
2
|
import { FirebaseApp } from "@firebase/app";
|
|
3
|
-
import {
|
|
3
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
4
4
|
|
|
5
5
|
export type FirestoreTextSearchControllerBuilder = (props: {
|
|
6
6
|
firebaseApp: FirebaseApp;
|
|
@@ -25,7 +25,7 @@ export type FirestoreTextSearchController = {
|
|
|
25
25
|
init: (props: {
|
|
26
26
|
path: string,
|
|
27
27
|
databaseId?: string,
|
|
28
|
-
collection?:
|
|
28
|
+
collection?: CollectionConfig
|
|
29
29
|
}) => Promise<boolean>,
|
|
30
30
|
/**
|
|
31
31
|
* Do the search and return a list of ids.
|
|
@@ -36,7 +36,7 @@ export type FirestoreTextSearchController = {
|
|
|
36
36
|
path: string,
|
|
37
37
|
currentUser?: FirebaseUser,
|
|
38
38
|
databaseId?: string,
|
|
39
|
-
collection?:
|
|
39
|
+
collection?: CollectionConfig
|
|
40
40
|
}) => (Promise<readonly string[] | undefined>),
|
|
41
41
|
|
|
42
42
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deleteField, DocumentSnapshot } from "@firebase/firestore";
|
|
2
|
-
import {
|
|
2
|
+
import { CollectionConfig, FirebaseCollectionConfig, Properties, Property } from "@rebasepro/types";
|
|
3
3
|
import { COLLECTION_PATH_SEPARATOR, sortProperties, stripCollectionPath } from "@rebasepro/common";
|
|
4
4
|
|
|
5
5
|
export function buildCollectionId(idOrPath: string, parentCollectionSlugs?: string[], parentEntityIds?: string[]): string {
|
|
@@ -9,7 +9,7 @@ export function buildCollectionId(idOrPath: string, parentCollectionSlugs?: stri
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
export const docsToCollectionTree = (docs: DocumentSnapshot[]):
|
|
12
|
+
export const docsToCollectionTree = (docs: DocumentSnapshot[]): CollectionConfig[] => {
|
|
13
13
|
|
|
14
14
|
const collectionsMap = docs.map((doc) => {
|
|
15
15
|
const id: string = doc.id;
|
|
@@ -26,7 +26,7 @@ export const docsToCollectionTree = (docs: DocumentSnapshot[]): EntityCollection
|
|
|
26
26
|
const parentId = id.split(COLLECTION_PATH_SEPARATOR).slice(0, -1).join(COLLECTION_PATH_SEPARATOR);
|
|
27
27
|
const parentCollection = collectionsMap[parentId];
|
|
28
28
|
if (parentCollection)
|
|
29
|
-
(parentCollection as
|
|
29
|
+
(parentCollection as FirebaseCollectionConfig).subcollections = () => [...((parentCollection as FirebaseCollectionConfig).subcollections?.() ?? []), collection];
|
|
30
30
|
delete collectionsMap[id];
|
|
31
31
|
}
|
|
32
32
|
});
|
|
@@ -34,7 +34,7 @@ export const docsToCollectionTree = (docs: DocumentSnapshot[]): EntityCollection
|
|
|
34
34
|
return Object.values(collectionsMap);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export const docToCollection = (doc: DocumentSnapshot):
|
|
37
|
+
export const docToCollection = (doc: DocumentSnapshot): CollectionConfig => {
|
|
38
38
|
const data = doc.data();
|
|
39
39
|
if (!data)
|
|
40
40
|
throw Error("Entity collection has not been persisted correctly");
|
|
@@ -48,7 +48,7 @@ export const docToCollection = (doc: DocumentSnapshot): EntityCollection => {
|
|
|
48
48
|
...data,
|
|
49
49
|
properties: sortedProperties,
|
|
50
50
|
slug: data.id ?? data.alias ?? data.slug
|
|
51
|
-
} as
|
|
51
|
+
} as CollectionConfig;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
|
package/src/utils/database.ts
CHANGED
|
@@ -12,8 +12,8 @@ export async function getFirestoreDataInPath(firebaseApp: FirebaseApp, path: str
|
|
|
12
12
|
const firestore = getFirestore(firebaseApp);
|
|
13
13
|
if (!parentPaths || parentPaths.length === 0) {
|
|
14
14
|
const q = query(collection(firestore, path), limitClause(limit));
|
|
15
|
-
return getDocs(q).then((
|
|
16
|
-
return
|
|
15
|
+
return getDocs(q).then((queryEntity) => {
|
|
16
|
+
return queryEntity.docs.map(doc => doc.data());
|
|
17
17
|
});
|
|
18
18
|
} else {
|
|
19
19
|
let currentDocs: QueryDocumentSnapshot[] | undefined = undefined;
|
|
@@ -2,7 +2,7 @@ import { collection, getFirestore, onSnapshot, query } from "@firebase/firestore
|
|
|
2
2
|
import Fuse from "fuse.js"
|
|
3
3
|
|
|
4
4
|
import { FirebaseApp } from "@firebase/app";
|
|
5
|
-
import {
|
|
5
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
6
6
|
import { FirestoreTextSearchController, FirestoreTextSearchControllerBuilder } from "../types";
|
|
7
7
|
|
|
8
8
|
const MAX_SEARCH_RESULTS = 80;
|
|
@@ -32,7 +32,7 @@ export const localSearchControllerBuilder: FirestoreTextSearchControllerBuilder
|
|
|
32
32
|
databaseId
|
|
33
33
|
}: {
|
|
34
34
|
path: string,
|
|
35
|
-
collection?:
|
|
35
|
+
collection?: CollectionConfig,
|
|
36
36
|
databaseId?: string
|
|
37
37
|
}): Promise<boolean> => {
|
|
38
38
|
|
|
@@ -49,11 +49,11 @@ export const localSearchControllerBuilder: FirestoreTextSearchControllerBuilder
|
|
|
49
49
|
const col = collection(firestore, path);
|
|
50
50
|
listeners[path] = onSnapshot(query(col),
|
|
51
51
|
{
|
|
52
|
-
next: (
|
|
53
|
-
if (
|
|
52
|
+
next: (entity) => {
|
|
53
|
+
if (entity.metadata.fromCache && entity.metadata.hasPendingWrites) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
const docs =
|
|
56
|
+
const docs = entity.docs.map(doc => ({
|
|
57
57
|
id: doc.id,
|
|
58
58
|
...doc.data()
|
|
59
59
|
}));
|
|
@@ -112,7 +112,7 @@ export const localSearchControllerBuilder: FirestoreTextSearchControllerBuilder
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
function buildIndex(list: (object & { id: string })[], collection:
|
|
115
|
+
function buildIndex(list: (object & { id: string })[], collection: CollectionConfig) {
|
|
116
116
|
|
|
117
117
|
const keys = ["id", ...Object.keys(collection.properties)];
|
|
118
118
|
|
package/src/utils/pinecone.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { User as FirebaseUser } from "@firebase/auth";
|
|
2
2
|
import { FirestoreTextSearchController, FirestoreTextSearchControllerBuilder } from "../types";
|
|
3
|
-
import {
|
|
3
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
4
4
|
|
|
5
5
|
const DEFAULT_SERVER = "https://api.rebase.pro";
|
|
6
6
|
|
|
@@ -60,7 +60,7 @@ export function buildPineconeSearchController({
|
|
|
60
60
|
|
|
61
61
|
const init = (props: {
|
|
62
62
|
path: string,
|
|
63
|
-
collection?:
|
|
63
|
+
collection?: CollectionConfig
|
|
64
64
|
}) => {
|
|
65
65
|
// do nothing
|
|
66
66
|
return Promise.resolve(isPathSupported(props.path));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FirestoreTextSearchController, FirestoreTextSearchControllerBuilder } from "../types";
|
|
2
2
|
import { FirebaseApp } from "@firebase/app";
|
|
3
3
|
import { getFunctions, httpsCallable } from "@firebase/functions";
|
|
4
|
-
import {
|
|
4
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Configuration returned by the Rebase Search Extension
|
|
@@ -204,7 +204,7 @@ export function buildRebaseSearchController(
|
|
|
204
204
|
*/
|
|
205
205
|
const init = async (props: {
|
|
206
206
|
path: string;
|
|
207
|
-
collection?:
|
|
207
|
+
collection?: CollectionConfig;
|
|
208
208
|
databaseId?: string;
|
|
209
209
|
}): Promise<boolean> => {
|
|
210
210
|
try {
|
|
@@ -289,7 +289,7 @@ export function buildRebaseSearchController(
|
|
|
289
289
|
searchString: string;
|
|
290
290
|
path: string;
|
|
291
291
|
databaseId?: string;
|
|
292
|
-
collection?:
|
|
292
|
+
collection?: CollectionConfig;
|
|
293
293
|
}): Promise<readonly string[] | undefined> => {
|
|
294
294
|
if (!typesenseClient) {
|
|
295
295
|
// Ensure client is initialized
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FirestoreTextSearchController, FirestoreTextSearchControllerBuilder } from "../types";
|
|
2
|
-
import {
|
|
2
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Utility function to perform a text search in an external index,
|
|
@@ -20,7 +20,7 @@ export function buildExternalSearchController({
|
|
|
20
20
|
|
|
21
21
|
const init = (props: {
|
|
22
22
|
path: string,
|
|
23
|
-
collection?:
|
|
23
|
+
collection?: CollectionConfig
|
|
24
24
|
}) => {
|
|
25
25
|
return Promise.resolve(isPathSupported(props.path));
|
|
26
26
|
}
|