繁体   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