How can I join two Firestore queries using rxfire and rxjs (OR query)

放肆的年华 提交于 2021-01-20 18:39:25

问题


The goal is simple: to join two firestore queries utilizing rxjs, rxfire, and the rnfirebase react native library.

I've read multiple tutorials 1, 2 on joining queries, but they all fail with different errors.

//Simple test for collectionData
import { collectionData } from 'rxfire/firestore';

this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
collectionData(this.myQuery, 'id').subscribe(docs => console.log(docs))
//Fails with error: this._next is not a function.

Alternatively,

this.publicQuery = this.props.docRef.collection('messages').where('public', '==', true) 
this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
const myQuery$ = new Rx.Subject();
const publicQuery$ = new Rx.Subject();
this.myQuery.onSnapshot((querySnapshot) => {
    myQuery$.next(querySnapshot.docs.map(d => d.data()  ));
});
this.publicQuery.onSnapshot((querySnapshot) => {
    publicQuery$.next(querySnapshot.docs.map(d => d.data()  ));
});
const orQuery$ = combineLatest(this.myQuery, this.publicQuery).switchMap((docs) => {
    var [one, two] = docs;
    var combined = one.concat(two);
    return Rx.Observable.of(combined);
})
orQuery$.subscribe((result) => {
    console.log('>>>> ', result)
})
//TypeError: undefined is not a function (near ...switchMap)

How can I successfully join two firestore queries (OR)?


回答1:


You're already very close to the solution. Let's go through the issues step-by-step.

First, it's not necessary to create a Subject just to transform your result from onSnapshot. Instead of this:

this.myQuery.onSnapshot((querySnapshot) => {
    myQuery$.next(querySnapshot.docs.map(d => d.data()))
});

We can achieve the same using 'pipeable transformation operators':

const myQuery$ = this.myQuery.onSnapshot.pipe(
    map(querySnapshot => querySnapshot.docs.map(d => d.data()))
);

The same goes for the other query:

const publicQuery$ = this.publicQuery.onSnapshot.pipe(
    map(querySnapshot => querySnapshot.docs.map(d => d.data())
);

Second, to join those two queries, combineLatest is indeed the correct creation function.

However, your error might result from you using a newer RxJS version, that doesn't support 'fluent' operators (officially called "patch operators") anymore. They have been replaced by 'pipeable operators' from RxJS 6 onwards. As an example, myObs$.map(...) has become myObs$.pipe(map(...)). The tutorials probably use an older version of RxJS where the first is still possible.

Also, it shouldn't be necessary to use switchMap if the inner Observable is just an of operator. It is sufficient in this case to use the map operator, which will behave equally.

Using the new RxJS 6+ syntax together with map, the combination will look like this:

const orQuery$ = combineLatest(myQuery$, publicQuery$).pipe(
    map(([one, two]) => one.concat(two))
)

The rest of your code should be correct.

Side Note: Keep in mind that the equivalent of your code in SQL is UNION (not JOIN). In order to JOIN programatically, you'd need to combine each object of result set A with each object of result set B and create a joined object for each pair. Such a function for a keyless OUTER JOIN would look like this (placed in your map pipe):

one.map(a => 
   two.map(b => Object.assign({}, a, b)))
.reduce((p, c) => p.concat(c), [])

If you want to have a UNION with no duplicate objects, concat only those items from two that have no matching primary key in list one. This would be your mapping function:

one.concat(two.filter(twoItem => !one.some(oneItem => oneItem.id == twoItem.id)))

DEMO: A complete, working demo with the above code and a simulated FireStore can be found here:

https://stackblitz.com/edit/rxjs-mefynu?devtoolsheight=60



来源:https://stackoverflow.com/questions/53329908/how-can-i-join-two-firestore-queries-using-rxfire-and-rxjs-or-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!