使用 rxjs Angular Firestore 从 id 获取合并的 observable
Get merged observable from id with rxjs Angular Firestore
我有一个包含人和宠物的 Firestore 数据库,宠物有所有者 ID。
// Collection / 文档 ID:{数据}
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' }
因此,当 Julieto 拥有 Tursky 时,Romea 拥有 Forky 和 Rasky。
我正在使用 @angular/fire 并且我想使用 最新版本的 rxjs (6.5.x) 获取每个用户及其宠物的 observables 列表,请参见以下结构:
[
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加上一个class在构造函数中接收人和宠物):
服务:
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()
}
型号:
export class Person {
id?: string
name: string
public constructor(person: Person = null, private pets: Pet[] = null) {
Object.assign(this, person)
this.pets = pets
}
model?() {
return {
id: this.id,
name: this.name
}
}
}
export class Pet {
id?: string
name: string
constructor(pet: Pet = null) {
Object.assign(this, pet)
}
model?() {
return {
id: this.id,
name: this.name
}
}
}
组件:
personsWithPets: Person[] = []
constructor(private personService: PersonService) {
this.getPersons()
}
getPersons() {
this.personService.getPersons().subscribe(p => this.personsWithPets = p)
}
人有多个参考(宠物、汽车)的情况的解决方案:
getPersonsWithReferences(): Observable<Person[]> {
return this.getPersons().pipe(switchMap(persons =>
combineLatest(persons.map(person =>
combineLatest(this.getPersonPets(person.id), this.getPersonCars(person.id))
.pipe(map(([pets, cars]) => new Environment(person, pets, cars)))))))
}
您可以使用 forkJoin
获取您的两个集合,您可以在其中获取数据数组,包括所有 persons
和 pets
。然后你可以修改你的数据,并以你喜欢的方式组合这些数据,即所有所有者为 x
的宠物都应该添加到 person x
的对象中。如果您正在寻找来自数据库的实时更新,我建议改用 combineLatest
。
这是一个示例,但您需要使用 angularfire 将它应用到您的用例中,并相应地使用文档参考:)
allData = [];
// added an id property here for this sample, you will use the doc ref
persons = [{ id: 1, name: "Romea" }, { id: 2, name: "Julieto" }];
pets = [
{ name: "Forky", ownerID: 1 },
{ name: "Rasky", ownerID: 1 },
{ name: "Tursy", ownerID: 2 }
];
ngOnInit() {
// in your usecase, combine your two queries
forkJoin(of(this.persons).pipe( /** here map accordingly **/),
of(this.pets).pipe(/** here map accordingly **/)).pipe(
// DON'T USE ANY, type your data!
map((data: [any, any]) => {
const persons = data[0];
const pets = data[1];
// here combine the data
return persons.map(person => {
const personPets = pets.filter(p => p.ownerID === person.id);
return { ...person, pets: [...personPets] };
});
})
)
.subscribe(d => this.allData = d);
}
here map accordingly
只是意味着在您的代码中您需要根据您进行查询的方式映射值,假设使用 snapshotChanges
.
上面代码的
这是一个 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}))
)))
})
)
}
首先获取人物,将映射到 combineLatest of persons 映射到他们的宠物流并将宠物分配给人物,return 人物及其宠物的列表。
还有更多作业:
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}))
)
))
})
)
}
我有一个包含人和宠物的 Firestore 数据库,宠物有所有者 ID。
// Collection / 文档 ID:{数据}
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' }
因此,当 Julieto 拥有 Tursky 时,Romea 拥有 Forky 和 Rasky。
我正在使用 @angular/fire 并且我想使用 最新版本的 rxjs (6.5.x) 获取每个用户及其宠物的 observables 列表,请参见以下结构:
[
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加上一个class在构造函数中接收人和宠物):
服务:
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()
}
型号:
export class Person {
id?: string
name: string
public constructor(person: Person = null, private pets: Pet[] = null) {
Object.assign(this, person)
this.pets = pets
}
model?() {
return {
id: this.id,
name: this.name
}
}
}
export class Pet {
id?: string
name: string
constructor(pet: Pet = null) {
Object.assign(this, pet)
}
model?() {
return {
id: this.id,
name: this.name
}
}
}
组件:
personsWithPets: Person[] = []
constructor(private personService: PersonService) {
this.getPersons()
}
getPersons() {
this.personService.getPersons().subscribe(p => this.personsWithPets = p)
}
人有多个参考(宠物、汽车)的情况的解决方案:
getPersonsWithReferences(): Observable<Person[]> {
return this.getPersons().pipe(switchMap(persons =>
combineLatest(persons.map(person =>
combineLatest(this.getPersonPets(person.id), this.getPersonCars(person.id))
.pipe(map(([pets, cars]) => new Environment(person, pets, cars)))))))
}
您可以使用 forkJoin
获取您的两个集合,您可以在其中获取数据数组,包括所有 persons
和 pets
。然后你可以修改你的数据,并以你喜欢的方式组合这些数据,即所有所有者为 x
的宠物都应该添加到 person x
的对象中。如果您正在寻找来自数据库的实时更新,我建议改用 combineLatest
。
这是一个示例,但您需要使用 angularfire 将它应用到您的用例中,并相应地使用文档参考:)
allData = [];
// added an id property here for this sample, you will use the doc ref
persons = [{ id: 1, name: "Romea" }, { id: 2, name: "Julieto" }];
pets = [
{ name: "Forky", ownerID: 1 },
{ name: "Rasky", ownerID: 1 },
{ name: "Tursy", ownerID: 2 }
];
ngOnInit() {
// in your usecase, combine your two queries
forkJoin(of(this.persons).pipe( /** here map accordingly **/),
of(this.pets).pipe(/** here map accordingly **/)).pipe(
// DON'T USE ANY, type your data!
map((data: [any, any]) => {
const persons = data[0];
const pets = data[1];
// here combine the data
return persons.map(person => {
const personPets = pets.filter(p => p.ownerID === person.id);
return { ...person, pets: [...personPets] };
});
})
)
.subscribe(d => this.allData = d);
}
here map accordingly
只是意味着在您的代码中您需要根据您进行查询的方式映射值,假设使用 snapshotChanges
.
这是一个 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}))
)))
})
)
}
首先获取人物,将映射到 combineLatest of persons 映射到他们的宠物流并将宠物分配给人物,return 人物及其宠物的列表。
还有更多作业:
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}))
)
))
})
)
}