[英]Join query with Angularfire2 and firestore
谢谢阅读。
我在 Firestore 中有两个集合,我使用的是 Angularfire2。
我有一组“客户”和一组“工作”。 客户端可以有多个作业,每个作业都有一个链接的客户端。
我创建了一个组件来显示所有作业的列表,并且我正在尝试使用 Firestore 密钥为每个作业拉入关联的客户端。
这是我的数据:
我已经能够“破解”一个解决方案,但它非常有问题,并且它失去了所有异步 - 所以我不妨编写一个 MySQL 后端而忘记 Firestore - 使用 Firestore 的全部意义在于它是“实时”的。
public jobs = {};
[...]
processData() {
const clients = [];
this.clientCollection.ref.get().then((results) => {
results.forEach((doc) => {
clients[doc.id] = doc.data();
});
const jobs = {};
this.jobsCollection.ref.get().then((docSnaps) => {
docSnaps.forEach((doc) => {
jobs[doc.id] = doc.data();
jobs[doc.id].id = doc.id;
jobs[doc.id].clientData = clients[doc.data().client];
});
this.jobs = jobs;
});
});
}
这在一定程度上有效 - 但它去除了异步。
关键问题:有什么方法可以像在 SQL 数据库中那样进行“连接”以将这两个数据集拉到一起? 有没有一种方法可以保持数据的异步性质?
您可以尝试使用Promise.all()
将多个承诺提取到一个对象中。 这是您的方法的修订版本,如果我正确理解您的问题,它应该会给出所需的结果:
async processData() {
const clientCollection = await firebase.firestore().collection(`clients`).get();
const jobsCollection = await firebase.firestore().collection(`jobs`).get();
const clients = [];
clients.push(clientCollection);
clients.push(jobsCollection);
const res = await Promise.all(clients);
// res should equal a combination of both querySnapshots
}
我只是重新创建了集合变量以显示要添加到数组中的内容,但是Promise.all()
接受一组 Promise 并将它们全部解析为一个数组,因为firestore
的get()
方法是一个 Promise。 这也是使用异步/等待。 希望这能有所帮助!
编辑:
由于您使用的是 AngularFire2,因此您应该使用他们的方法。
在您的组件中,您将要导入angularfire2/firestore
模块并使用它们提供的Observable
方法:
首先导入模块: import { AngularFireStore } from 'angularfire2/firestore'
;
然后将其提供给您的构造函数:
constructor(
private firestore: AngularFireStore
) { }
然后你可以使用Observable.combineLatest()
接收所有数据:
clients$: Observable<any[]>;
clients: any[];
jobs$: Observable<any[]>;
jobs: any;
joined: any;
ngOnInit() {
this.clients$ = this.getCollection('clients');
this.jobs$ = this.getCollection('jobs');
this.joined = Observable.combineLatest(this.clients$, this.jobs$);
this.joined.subscribe(([clients, jobs]) => {
this.clients = clients;
this.jobs = jobs;
});
}
getCollection(collectionName) {
return this.firestore.collection(`${collectionName}`).valueChanges();
}
在您的标记中,您只需使用*ngFor
循环数据:
<div *ngFor="let client of clients">{{ client.name }}</div>
这样,一旦 firestore 拥有数据,您的组件就会侦听新数据,并且这些数据将一次性全部传入,因此您没有容易创建多个订阅的嵌套订阅。 希望这能有所帮助。
解决方案迟到了,但经过 4 个小时的搜索,我发现这个解决方案库对于加入收集和获取数据非常有用。
主要资源: https : //github.com/AngularFirebase/133-firestore-joins-custom-rx-operators
首先创建一个类型脚本文件
文件名:collectionjoin.ts
import { combineLatest, pipe, of, defer } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
export const leftJoinDocument = (afs: AngularFirestore, field, collection) => {
return source =>
defer(() => {
// Operator state
let collectionData;
const cache = new Map();
return source.pipe(
switchMap(data => {
// Clear mapping on each emitted val ;
cache.clear();
// Save the parent data state
collectionData = data as any[];
const reads$ = [];
let i = 0;
for (const doc of collectionData) {
// Skip if doc field does not exist or is already in cache
if (!doc[field] || cache.get(doc[field])) {
continue;
}
// Push doc read to Array
reads$.push(
afs
.collection(collection)
.doc(doc[field])
.valueChanges()
);
cache.set(doc[field], i);
i++;
}
return reads$.length ? combineLatest(reads$) : of([]);
}),
map(joins => {
return collectionData.map((v, i) => {
const joinIdx = cache.get(v[field]);
return { ...v, [field]: joins[joinIdx] || null };
});
}),
tap(final =>
console.log(
`Queried ${(final as any).length}, Joined ${cache.size} docs`
)
)
);
});
};
之后在您的 homepage.ts 或任何页面打字稿文件中。
import {
AngularFirestore,
AngularFirestoreCollection,
AngularFirestoreDocument,
} from "@angular/fire/firestore";
jobs: any
constructor( public afStore: AngularFirestore ) { }
this.afStore.collection('Jobs').valueChanges().pipe(
leftJoinDocument(this.afStore, 'client', 'Clients'),
shareReplay(1)
).subscribe((response) => {
this.products = response;
})
在这一步中,我们传递了三个参数
现在最后一步 显示结果
<ion-list lines="full" *ngFor="let job of Jobs">
<ion-item button detail>
<ion-label class="ion-text-wrap">{{ job.Name }}</ion-label>
<p>{{ job.Clients.companyName}}</p>
</ion-item>
</ion-list>
最后这段代码给出了两个收藏记录。 谢谢你。
在尝试了多种解决方案后,我使用 RXJS combineLatest, take operator 完成了它。 使用 map 函数我们可以组合结果。
可能不是最佳解决方案,但在这里它可以解决您的问题。
combineLatest(
this.firestore.collection('Collection1').snapshotChanges(),
this.firestore.collection('Collection2').snapshotChanges(),
//In collection 2 we have document with reference id of collection 1
)
.pipe(
take(1),
).subscribe(
([dataFromCollection1, dataFromCollection2]) => {
this.dataofCollection1 = dataFromCollection1.map((data) => {
return {
id: data.payload.doc.id,
...data.payload.doc.data() as {},
}
as IdataFromCollection1;
});
this.dataofCollection2 = dataFromCollection2.map((data2) => {
return {
id: data2.payload.doc.id,
...data2.payload.doc.data() as {},
}
as IdataFromCollection2;
});
console.log(this.dataofCollection2, 'all feeess');
const mergeDataFromCollection =
this.dataofCollection1.map(itm => ({
payment: [this.dataofCollection2.find((item) => (item.RefId === itm.id))],
...itm
}))
console.log(mergeDataFromCollection, 'all data');
},
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.