[英]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.