使用rxjs Angular Firestore从id获取合并的可观测值
我有个人和宠物的Firestore DB,宠物有主人id //集合/文档ID:{data}使用rxjs Angular Firestore从id获取合并的可观测值,angular,firebase,google-cloud-firestore,rxjs,angularfire,Angular,Firebase,Google Cloud Firestore,Rxjs,Angularfire,我有个人和宠物的Firestore DB,宠物有主人id //集合/文档ID:{data} persons / 'person1': { name: 'Romea' } persons / 'person2': { name: 'Julieto' } pets / 'pet1': { name: 'Forky', ownerID: 'person1' } pets / 'pet2': { name: 'Rasky', ownerID: 'person1' } pets / 'pet3': { n
persons / 'person1': { name: 'Romea' }
persons / 'person2': { name: 'Julieto' }
pets / 'pet1': { name: 'Forky', ownerID: 'person1' }
pets / 'pet2': { name: 'Rasky', ownerID: 'person1' }
pets / 'pet3': { name: 'Tursy', ownerID: 'person2' }
所以,当朱丽叶拥有图尔斯基时,罗米亚拥有福克斯和拉斯基
我正在使用@angular/fire,我想使用rxjs(6.5.x)的最新版本来获取每个宠物可观察到的用户列表,请参见以下结构:
[
0: {
name: 'Romea',
pets: [ 0: { name: 'Forky', ownerID: 'person1' }, 1: { name: 'Rasky', ownerID: 'person1' } ]
},
1: {
name: 'Julieto',
pets: [ 0: { name: 'Tursky', ownerID: 'person2' } ]
}
]
毕竟,我想给person.pets打电话,就像这样:
this.personService.getPersonsWithPets().subscribe(persons => {
console.log(persons[0].pets)
// result: [{ name: 'Forky', ownerID: 'person1' }, { name: 'Rasky', ownerID: 'person1' }]
persons.forEach(person => person.pets.forEach(pet => console.log(pet.name)))
// result: 'Forky', 'Rasky', 'Tursky'
})
解决方案 根据@bryan60的解决方案,这就是我所做的(rxjs加上一个在构造函数中同时接收person和pets的类): 服务:
private personsCollection: AngularFirestoreCollection<Person> = this.db.collection<Person>('persons')
private _getPersonsWithoutPets(): Observable<Person[]> {
return this.personsCollection.valueChanges()
}
getPersons(): Observable<(Person)[]> {
return this._getPersonsWithoutPets().pipe(
switchMap(persons => {
return combineLatest(persons.map(person => this.getPersonPets(person.id).pipe(
map(pets => new Person(person, pets))
)))
})
)
}
getPersonPets(personId: string): Observable<Pet[]> {
return this.db.collection<Pet>('pets', ref => ref.where('personId', '==', personId)).valueChanges()
}
组成部分:
personsWithPets: Person[] = []
constructor(private personService: PersonService) {
this.getPersons()
}
getPersons() {
this.personService.getPersons().subscribe(p => this.personsWithPets = p)
}
针对人员有多个参考(宠物、汽车)的情况的解决方案:
getPersonsWithReferences():可观察{
返回此.getPersons().pipe(开关映射(persons=>
组合相关测试(persons.map)(person=>
CombineTest(this.getPersonPets(person.id),this.getPersonCars(person.id))
.pipe(地图([宠物,汽车])=>新环境(人,宠物,汽车()(()()))((宠物,汽车))
}
您可以使用forkJoin
获取两个集合,从中可以获取所有人员和宠物的数据数组。然后,您可以修改您的数据,并以您喜欢的方式组合这些数据,即所有拥有主人x
的宠物都应添加到人x
的对象中。如果您想查看数据库中的实时更新,我建议使用combinelateest
这是一个示例,但您需要将其应用到angularfire的用例中,并相应地使用文档参考:)
allData=[];
//在此为该示例添加了一个id属性,您将使用doc ref
persons=[{id:1,姓名:“Romea”},{id:2,姓名:“Julieto”}];
宠物=[
{name:“Forky”,所有者ID:1},
{姓名:“拉斯基”,所有者ID:1},
{姓名:“Tursy”,所有者ID:2}
];
恩戈尼尼特(){
//在您的用例中,组合两个查询
forkJoin(这个人的)管道(/**这里相应地映射**/),
of(this.pets).pipe(/**此处相应映射**/).pipe(
//不要使用任何,请键入您的数据!
地图((数据:[任何,任何])=>{
const persons=数据[0];
常数=数据[1];
//这里结合数据
返回persons.map(person=>{
const personPets=pets.filter(p=>p.ownerID===person.id);
返回{…个人,宠物:[…个人宠物]};
});
})
)
.subscribe(d=>this.allData=d);
}
此处相应地映射
仅意味着在代码中,您需要根据查询方式映射值,这与使用快照更改有关
在上述代码中,这是一个switchMap用例
假设您有函数getPersons()
和getPersonsPets(ownerID)
,它们按照它们的名称执行,请按如下方式执行:
getPersonsWithPets() {
return this.getPersons().pipe(
switchMap(persons => {
return combineLatest(persons.map(person => this.getPersonsPets(person.id).pipe(
map(pets => Object.assign(person, {pets}))
)))
})
)
}
首先获取人物,将Map切换为CombineTest,将人物映射到他们的宠物流中,并将宠物分配给此人,然后返回带宠物的人物列表
更多作业:
getPersonsWithPets() {
return this.getPersons().pipe(
switchMap(persons => {
return combineLatest(persons.map(person =>
combineLatest(
this.getPersonsPets(person.id),
this.getPersonsCars(person.id),
this.getPersonsHouses(person.id)
).pipe(
map(([pets, cars, houses]) => Object.assign(person, {pets, cars, houses}))
)
))
})
)
}
你能用getPersonsWithPets()的代码更新你的问题吗?您的初始查询是什么样子的,您将使用它来获得结果。你会提出一个或两个请求吗?@Damiance我没有服务方法,问题是如何做,单独得到一个人的宠物是什么样子的,单独得到一个人是什么样子的,你想什么时候做?最后一个想法是,这是一个文档数据库,你应该考虑基于你将如何访问它来存储你的数据。这减少了应用程序在您每次请求时执行此操作的压力。谢谢,您知道如何使用多个可观察的引用来执行此操作吗?假设有很多收藏都有ownerID(宠物、汽车、房子),我想得到一个有宠物、汽车和房子的人的列表,并列出他们的组合测试,得到:.pipe(switchMap(persons=>combinetest(/*get person with pets/).pipe(switchMap(persons=>combinetest(/get person with cars*/))可以做得更干净tbh,CombineTest已经是一个更高阶的可观察对象,因此您不需要切换Map。更新了我的答案,请注意,如果此列表很长,您正在构建一个包含大量请求的非常大的流…1+(nPeople*NaSignments)http请求
getPersonsWithPets() {
return this.getPersons().pipe(
switchMap(persons => {
return combineLatest(persons.map(person => this.getPersonsPets(person.id).pipe(
map(pets => Object.assign(person, {pets}))
)))
})
)
}
getPersonsWithPets() {
return this.getPersons().pipe(
switchMap(persons => {
return combineLatest(persons.map(person =>
combineLatest(
this.getPersonsPets(person.id),
this.getPersonsCars(person.id),
this.getPersonsHouses(person.id)
).pipe(
map(([pets, cars, houses]) => Object.assign(person, {pets, cars, houses}))
)
))
})
)
}