我正在Firebase Firestore上运行多个查询。它们都可以返回一个Observable。我想将所有Observable合并为一个Observable。
我尝试了不同的RxJS运算符,例如; CombineLatest,forkJoin,concat,zip,merge和mergeMap。但是我无法达到预期的效果。
getPrivateBooksFromAuthors(authors): Observable<Book[]> {
let bookRefs: Observable<Book[]>[] = [];
authors.map(key => {
bookRefs.push(this.afs.collection<Book>('books', ref =>
ref.where('public', '==', false)
.where('authors', 'array-contains', key)
).valueChanges())
});
return bookRefs[0]
}
在上面的代码中,我从authors [0]中获得了所有私人书籍。当我返回concat(... bookRefs)或concat(bookRefs [0],bookRefs [1])时,我仍然只从作者那里获得书籍[0]。我希望从提供的所有作者那里获得所有书籍。
答案 0 :(得分:1)
我认为最好的解决方案是使用fork join。
Fork
使您可以并行执行调用。每次调用的结果都放在一个对象(join
)中,您可以得到全部调用的结果。
答案 1 :(得分:0)
正如@Doflamingo所说,您可以使用 forkJoin 来并行调用它们并接收包含已解决所有响应的数组的响应。 您遇到的问题是每个响应都是Book [],因此在forkJoin中,您将收到一个Book []数组(Book [] [])。您需要将图书[] [] 平整到图书[]中。您可以使用地图和缩小功能。
在这里我贴了一个简单的代码,该代码在应用reduce函数后控制台记录了forkJoin的响应和结果。
function mockBooks$(key): Observable<Book[]> {
return rxjs.of([key + '-book1', key + '-book2', key + '-book3']);
}
function getPrivateBooksFromAuthors(authors): Observable<Book[]> {
let bookRefs: Observable<Book[]>[] = authors.map(key => mockBooks$(key));
return rxjs.forkJoin(bookRefs).pipe(
rxjs.operators.tap((books) => console.log('After forkJoin', books)),
// You need this flattening operation!!
rxjs.operators.map(books => books.reduce((acc, cur) => [...acc, ...cur], []) ));
}
const authors = ['a1', 'a2', 'a3', 'a4', 'a5'];
getPrivateBooksFromAuthors(authors).subscribe((data) => console.log('Final result', data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.js"></script>
希望这会有所帮助!
答案 2 :(得分:0)
@Doflamingo向我们指出了forkJoin的方向,@Llorenç在地图上给出了一个很好的代码示例,并简化了将Observable组合成一个Observable的管道。对我来说,如果某些作者没有书籍,forkJoin就会失败。因此,我最终选择了CombineLatest(它不等待所有Observable都完成)。这是我的最终代码(所有功劳归@Llorenç):
mockBooks$(key): Observable<Book[]> {
return this.afs.collection<Book>('books', ref =>
ref.where('private', '==', true)
.where('authors', 'array-contains', key)
).valueChanges()
// return of([key + '-book1', key + '-book2', key + '-book3']);
}
getPrivateBooksFromAuthors(authors): Observable<Book[]> {
let bookRefs: Observable<Book[]>[] = authors.map(key => this.mockBooks$(key));
// return combineLatest(bookRefs).pipe(
// tap((books) => console.log('After forkJoin', books)),
// // You need this flattening operation!!
// map(books => books.reduce((acc, cur) => [...acc, ...cur], []) ));
return combineLatest<Book[]>(bookRefs).pipe(
map(arr => arr.reduce((acc, cur) => acc.concat(cur) ) ),
)
}
我已注释掉Llorenç的代码,以显示差异。谢谢你们两个!