使用rxjs Angular Firestore从id获取合并的可观测值

使用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

我有个人和宠物的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': { 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}))
        )
      ))
    })
  )
}