如何使用rxfire和rxjs加入两个Firestore查询(或查询)

时间:2018-11-16 00:50:07

标签: firebase rxjs google-cloud-firestore rxfire

目标很简单:使用rxjsrxfirernfirebase React本机库加入两个Firestore查询。

我已经阅读了多本关于加入查询的教程12,但是它们都因不同的错误而失败。

//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.

或者,

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)

如何成功加入两个Firestore查询(OR)?

1 个答案:

答案 0 :(得分:8)

您已经非常接近解决方案。让我们逐步解决这些问题。

首先,不必创建Subject只是为了转换onSnapshot的结果。代替这个:

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

我们可以使用“管道转换运算符”实现相同的目标:

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

其他查询也是如此:

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

第二,结合这两个查询,combineLatest确实是正确的创建函数。

但是,您的错误可能是由于您使用了较新的RxJS版本导致的,该版本不再支持“流利的”运算符(officially called "patch operators")。从RxJS 6开始,它们已被“管道运算符”所取代。例如,myObs$.map(...)变成了myObs$.pipe(map(...))。这些教程可能会使用旧版本的RxJS,但仍然可以使用第一个版本。

此外,如果内部Observable只是switchMap运算符,则不必使用of。在这种情况下,使用map运算符就足够了,它的行为将相同。

结合使用新的RxJS 6+语法和map,组合将如下所示:

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

其余代码应该正确。

侧面说明:请记住,SQL中的代码等效于UNION(而不是JOIN)。为了以编程方式JOIN,您需要将结果集A的每个对象与结果集B的每个对象组合在一起,并为每对创建一个联接的对象。对于无键OUTER JOIN来说,这样的功能看起来像这样(放在您的map管道中):

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

如果您要拥有一个没有重复对象的UNION,请仅合并two中列表one中没有匹配主键的那些项。这将是您的映射功能:

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

演示:可以在此处找到包含以上代码和模拟FireStore的完整且有效的演示:

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