[英]How to merge and observe two collections in Firestore based on reference ID in documents?
I'm creating a StencilJS
app (no framework) with a Google Firestore backend, and I want to use the RxFire
and RxJS
libraries as much as possible to simplify data access code. 我正在使用Google Firestore后端创建一个StencilJS
应用程序(无框架),并且我想尽可能多地使用RxFire
和RxJS
库来简化数据访问代码。 How can I combine into a single observable stream data coming from two different collections that use a reference ID? 如何将来自两个使用引用ID的不同集合的数据合并到一个可观察的流中?
There are several examples online that I've read through and tried, each one using a different combination of operators with a different level of nested complexity. 我已经阅读并尝试了几个在线示例,每个示例都使用不同级别的嵌套复杂性的运算符的不同组合。 https://www.learnrxjs.io/ seems like a good resource, but it does not provide line-of-business examples that make sense to me. https://www.learnrxjs.io/似乎是一个很好的资源,但是它没有提供对我有意义的业务示例。 This question is very similar, and maybe the only difference is some translation into using RxFire? 这个问题非常相似,也许唯一的区别是使用RxFire进行了一些翻译吗? Still looking at that. 还在看着那个。 Just for comparison, in SQL this would be a SELECT
statement with an INNER JOIN
on the reference ID. 只是为了进行比较,在SQL中,这将是带有引用ID的INNER JOIN
的SELECT
语句。
Specifically, I have a collection for Games
: 具体来说,我有一个Games
集合:
{ id: "abc000001", name: "Billiards" },
{ id: "abc000002", name: "Croquet" },
...
and a collection for Game Sessions
: 以及Game Sessions
的集合:
{ id: "xyz000001", userId: "usr000001", gameId: "abc000001", duration: 30 },
{ id: "xyz000002", userId: "usr000001", gameId: "abc000001", duration: 45 },
{ id: "xyz000003", userId: "usr000001", gameId: "abc000002", duration: 55 },
...
And I want to observe a merged collection of Game Sessions
where gameId
is essentially replace with Game.name
. 我想观察Game Sessions
的合并集合,其中gameId
本质上被Game.name
取代。
I current have a game-sessions-service.ts
with a function to get sessions for a particular user: 我目前有一个game-sessions-service.ts
,它具有为特定用户获取会话的功能:
import { collectionData } from 'rxfire/firestore';
import { Observable } from 'rxjs';
import { GameSession } from '../interfaces';
observeUserGameSesssions(userId: string): Observable<GameSession[]> {
let collectionRef = this.db.collection('game-sessions');
let query = collectionRef.where('userId', '==', userId);
return collectionData(query, 'id);
}
And I've tried variations of things with pipe
and mergeMap
, but I don't understand how to make them all fit together properly. 而且我尝试使用pipe
和mergeMap
各种变体,但是我不知道如何使它们正确地组合在一起。 I would like to establish an interface GameSessionView
to represent the merged data: 我想建立一个接口GameSessionView
来代表合并后的数据:
export interface GameSessionView {
id: string,
userId: string,
gameName: string,
duration: number
}
observeUserGameSessionViews(userId: string): Observable<GameSessionView> {
this.observeUserGameSessions(userId)
.pipe(
mergeMap(sessions => {
// What do I do here? Iterate over sessions
// and embed other observables for each document?
}
)
}
Possibly, I'm just stuck in a normalized way of thinking, so I'm open to suggestions on better ways to manage the data. 可能地,我只是停留在规范化的思维方式上,因此我愿意就更好的数据管理方式提出建议。 I just don't want too much duplication to keep synchronized. 我只是不想过多的重复来保持同步。
You can use the following code (also available as Stackblitz ): 您可以使用以下代码(也可以作为Stackblitz使用 ):
const games: Game[] = [...];
const gameSessions: GameSession[] = [...];
combineLatest(
of(games),
of(gameSessions)
).pipe(
switchMap(results => {
const [gamesRes, gameSessionsRes] = results;
const gameSessionViews: GameSessionView[] = gameSessionsRes.map(gameSession => ({
id: gameSession.id,
userId: gameSession.userId,
gameName: gamesRes.find(game => game.id === gameSession.gameId).name,
duration: gameSession.duration
}));
return of(gameSessionViews);
})
).subscribe(mergedData => console.log(mergedData));
Explanation: 说明:
With combineLatest
you can combine the latest values from a number of Obervables. 使用combineLatest
您可以合并多个Obervable中的最新值。 It can be used if you have "multiple (..) observables that rely on eachother for some calculation or determination" . 如果您具有“彼此依赖进行某些计算或确定的多个(..)可观测对象” ,则可以使用它。
So assuming you lists of Game
s and GameSession
s are Observables, you can combine the values of each list. 因此,假设您的Game
和GameSession
的列表都是Observables,则可以合并每个列表的值。
Within the switchMap
you create new objects of type GameSessionView
by iterating over your GameSession
s, use the attributes id
, userId
and duration
and find the value for gameName
within the second list of Game
s by gameId
. 内switchMap
创建类型的新对象GameSessionView
通过遍历您GameSession
S,使用的属性id
, userId
和duration
,并找到价值gameName
的第二列表中Game
由s gameId
。 Mind that there is no error handling in this example. 请注意,此示例中没有错误处理。
As switchMap
expects that you return another Observable, the merged list will be returned with of(gameSessionViews)
. 正如switchMap
期望您返回另一个Observable一样,合并列表将与of(gameSessionViews)
返回。
Finally, you can subscribe
to this process and see the expected result. 最后,您可以subscribe
此过程并查看预期结果。
For sure this is not the only way you can do it, but I find it the simplest one. 当然,这不是唯一的方法,但我发现它是最简单的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.