如何使用 rxfire 和 rxjs 加入两个 Firestore 查询(或查询)
How can I join two Firestore queries using rxfire and rxjs (OR query)
目标很简单:利用 rxjs
、rxfire
和 rnfirebase
React 本机库加入两个 firestore 查询。
我已经阅读了多个关于加入查询的教程 1, 2,但它们都因不同的错误而失败。
//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)?
您已经非常接近解决方案了。让我们逐步解决问题。
首先,没有必要创建一个 Subject
来转换 onSnapshot
的结果。而不是这个:
this.myQuery.onSnapshot((querySnapshot) => {
myQuery$.next(querySnapshot.docs.map(d => d.data()))
});
我们可以使用 'pipeable transformation operators':
来实现同样的效果
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 版本,该版本不再支持 'fluent' 运算符 (officially called "patch operators")。从 RxJS 6 开始,它们已被 'pipeable operators' 取代。例如,myObs$.map(...)
变成了 myObs$.pipe(map(...))
。这些教程可能使用旧版本的 RxJS,其中第一个版本仍然可用。
此外,如果内部 Observable 只是一个 of
运算符,则不必使用 switchMap
。在这种情况下,使用 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)))
DEMO: 可以在此处找到包含上述代码和模拟 FireStore 的完整、有效的演示:
目标很简单:利用 rxjs
、rxfire
和 rnfirebase
React 本机库加入两个 firestore 查询。
我已经阅读了多个关于加入查询的教程 1, 2,但它们都因不同的错误而失败。
//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)?
您已经非常接近解决方案了。让我们逐步解决问题。
首先,没有必要创建一个 Subject
来转换 onSnapshot
的结果。而不是这个:
this.myQuery.onSnapshot((querySnapshot) => {
myQuery$.next(querySnapshot.docs.map(d => d.data()))
});
我们可以使用 'pipeable transformation operators':
来实现同样的效果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 版本,该版本不再支持 'fluent' 运算符 (officially called "patch operators")。从 RxJS 6 开始,它们已被 'pipeable operators' 取代。例如,myObs$.map(...)
变成了 myObs$.pipe(map(...))
。这些教程可能使用旧版本的 RxJS,其中第一个版本仍然可用。
此外,如果内部 Observable 只是一个 of
运算符,则不必使用 switchMap
。在这种情况下,使用 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)))
DEMO: 可以在此处找到包含上述代码和模拟 FireStore 的完整、有效的演示: