簡體   English   中英

使用 Angularfire2 和 firestore 加入查詢

[英]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 並將它們全部解析為一個數組,因為firestoreget()方法是一個 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;
        })

在這一步中,我們傳遞了三個參數

  1. this.afStore = 這是 lib 的對象。
  2. 'client' = 這是作業集合的鍵/ID
  3. 'Clients' = 這是我們加入的集合名稱。

現在最后一步 顯示結果

 <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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM