`export const firestoreCollectionDocumentsJoin = (firestore: AngularFirestore, joins: Join[], clazz: any) => {
return (source: Observable) => defer(() => {
let documents: any[];
const cache = new Map();
return source.pipe(
distinctUntilChanged((x, y) => {
const x1 = x.filter((dca: DocumentChangeAction<unknown>) => {
return !dca.payload.doc.metadata.hasPendingWrites;
}).map((dca: DocumentChangeAction<unknown>) => {
return set(dca.payload.doc.data() as any, 'id', dca.payload.doc.id);
});
const y1 = y.filter((dca: DocumentChangeAction<unknown>) => {
return !dca.payload.doc.metadata.hasPendingWrites;
}).map((dca: DocumentChangeAction<unknown>) => {
return set(dca.payload.doc.data() as any, 'id', dca.payload.doc.id);
});
const c = x.map((dca: DocumentChangeAction<unknown>) => {
return dca.payload.doc.metadata.fromCache;
}).filter((d: any) => !!d);
// return x.length === y.length;
// console.log(`CCCCCCC`, c);
return false;
// return JSON.stringify(x1) === JSON.stringify(y1);
}),
// pipe(filter((c) => c.payload.doc.metadata.fromCache === false)),
switchMap((_dca: DocumentChangeAction<DocumentData>[]) => {
cache.clear();
const reads$ = [];
let i = 0;
documents = _dca.map(dca => {
let doc = set(dca.payload.doc.data(), 'id', dca.payload.doc.id);
if (!environment.production) {
// console.log('doc ', doc);
}
doc = set(doc, 'metadata', dca.payload.doc.metadata);
return doc;
});
for (const doc of documents) {
for (const join of joins) {
let docIds = get(doc, join.id);
if (isString(docIds)) {
docIds = [docIds];
}
if (isArray(docIds)) {
for (const docId of docIds) {
if (isString(docId)) {
if (cache.has(join.uniqueId(docId))) {
continue;
}
cache.set(join.uniqueId(docId), i);
i++;
reads$.push(
firestore
.collection(join.collection)
.doc(docId)
.snapshotChanges()
.pipe(
map(dca => {
return set(dca.payload.data() as any, 'id', dca.payload.id);
}),
distinctUntilChanged((p, c) => {
return JSON.stringify(p) === JSON.stringify(c);
}),
)
);
}
}
}
}
}
return reads$.length ? combineLatest(reads$) : of([]);
}),
map((values) => {
return documents.map(document => {
for (const join of joins) {
const data = get(document, join.id);
let docIds = [];
if (isString(data)) {
docIds = [data];
} else if (isArray(data)) {
docIds = data;
}
if (isArray(docIds)) {
for (const docId of docIds) {
if (isString(docId)) {
const uniqueId = join.uniqueId(docId);
const index = cache.get(uniqueId);
const docData = merge(values[index], set(values[index] as object, 'id', docId));
let currentDocData = get(document, join.data);
if (currentDocData) {
currentDocData = merge(currentDocData, new join.clazz(docData));
if (isArray(currentDocData)) {
document[join.data].push(new join.clazz(docData));
} else {
document = merge(document, set(document, join.data, currentDocData));
}
} else {
if (isArray(data)) {
document[join.data] = [new join.clazz(docData)];
} else if (isString(data)) {
document = merge(document, set(document, join.data, new join.clazz(docData)));
}
}
}
}
}
}
if (!environment.production) {
console.log('document ', new clazz(document));
}
return new clazz(document);
});
}),
pipe(distinctUntilChanged((p, c) => {
return JSON.stringify(p) === JSON.stringify(c);
})),
tap(docs => {
if (!environment.production) {
console.log(
`Queried ${docs ? (docs as any).length : 0}, Joined ${cache.size} docs.`
);
}
})
);
});
};`
I have tried to add filter collections with a field confirmed_booking_time and use same with startAfter for next page.
Can any one suggest how to use query cursors in paginate for firebase query.
2
Answers
I rewrite the code and this solve my issue
First of all, take a look to the firebase documentation: Firebase Query Cursor
After you understand how it works, take a look at this implementation:
As you can see, one of the parameters is a
cursor?: QueryDocumentSnapshot<DocumentData>;
which we also return. The purpose is to filter the data based on the returned cursor, if one is returned. If one is not returned, we just filter the data with no cursor. Finally, return the cursor with the latest reference point.