Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Merge multiple queries in Firestore
export type CompareFunction = (a: object, b: object) => number;
export type MergeQueryObserver = (docs: DocumentData[]) => void;
export type MergeErrorObserver = (
e: Error | string | object,
queryPosition: number
) => void;
export class MergeQuery {
private observers: Set<{ fn: MergeQueryObserver; err: MergeErrorObserver }>;
private compareFunction = defaultCompareFunction;
private docs: DocumentData[][] = [[], []];
private queryCancelers: Function[] = [];
constructor(...queries: Query[]) {
this.observers = new Set();
queries.forEach((query, position) => {
this.queryCancelers.push(
query.onSnapshot(
snap => {
this.updateDocs(snap.docs, position);
},
e => this.err(e, position)
)
);
});
}
setCompareFunction(compareFx: CompareFunction): void {
this.compareFunction = compareFx;
}
/**
* Listen for updates to the merged data. Receives the entire array after each update.
*
* @param observer receives an array of data objects sorted using compareFunction.
* @return an unsubscribe function
*/
subscribe(
observer: MergeQueryObserver,
errorObserver?: MergeErrorObserver
): () => void {
const obs = {
fn: observer,
err:
errorObserver ||
(() => {
/* do nothing */
})
};
this.observers.add(obs);
return () => {
this.observers.delete(obs);
};
}
destroy() {
this.queryCancelers.forEach(fn => fn());
this.queryCancelers.length = 0;
this.docs.length = 0;
this.observers.clear();
}
private err(e: Error | object, pos: number) {
this.observers.forEach(obs => {
if (obs.err) obs.err(e, pos);
});
}
private updateDocs(docs: DocumentData[], position: number) {
this.docs[position] = docs.map(dd => ({ $id: dd.id, ...dd.data() }));
const sortedData = [...new Set(this.docs.flat())].sort(
this.compareFunction
);
this.observers.forEach(obs => obs.fn(sortedData));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.