如何将collectionGroup查询结果限制为祖先?

时间:2020-06-21 09:02:40

标签: javascript firebase google-cloud-firestore

我一直在阅读博客文章https://firebase.googleblog.com/2019/06/understanding-collection-group-queries.html,以更好地了解collectionGroup查询。

尽管如此,我仍然有一个问题:如何将结果限制为特定的祖先。让我解释一下自己。

想象一下,我有companies制造的carstyres。我们在不同的tyres中使用了不同的cars品牌。最后,我们建立了多对多关系。我知道我不应该在NoSQL世界中使用这个术语,但是我把狗称为狗:-)

无论如何,我的问题是:如果某个特定company品牌(例如米其林)的tyre A短缺,则需要标记此tyre缺货。我想运行一个collectionGroup查询,例如:

db.collectionQuery("tyre")
  .where("brand", "==", "Michelin")
  .get()
  .then(function (querySnapshot) {
    // update flag accordingly
  })

但这会更新其他companies的库存。

我的问题是:您如何缩小collectionGroup查询结果,以便仅更新公司A的tyres信息?

我可以将公司A docRef包含在tyres集合中,并使用where()来缩小结果范围。这似乎是一种有效的方法。虽然,这是顶级集合和子集合之间的混合。这是最佳做法吗?

更新

实际上,我是按照餐馆的例子来介绍如何使用Firebase / firestore。餐厅可以有多个菜单。菜单可以包含多个项目。项目可以重复使用,因此可以显示在多个菜单中。

collection('restaurants').doc(..).collection('menus').doc(..).collection('items')

我想这是构造数据的最佳方法(相对于项目的顶级集合)。但是,可以在多家餐厅的多个菜单中轻松找到诸如Coffee之类的项目。如果一家餐厅缺咖啡,我该如何使用以下方法更新该特定餐厅的咖啡项目:

db.collectionQuery("items")
  .where("name", "==", "Coffee")
  .get()
  .then(function (querySnapshot) {
    // set available = false
  })

2 个答案:

答案 0 :(得分:2)

如果一家餐厅缺咖啡,我该如何更新咖啡 该特定餐厅的食物?

通过使用collectionGroup查询,您可以这样做:

  db.collectionQuery('items')
    .where('name', '==', 'Coffee')
    .get()
    .then(function (querySnapshot) {
      querySnapshot.forEach(function (doc) {
        const itemQuantity = doc.data().itemQuantity;
        if (itemQuantity === 0) {
          const restaurantRef = doc.ref.parent.parent.parent.parent;
          return restaurantRef.update( {....})
        }
      });
    });

通过交替使用parentDocumentReference的{​​{1}}属性。


但是,如果您有很多餐厅,这可能不是最有效和负担得起的方法,因为您的collectionGroup查询将返回很多记录。

一种更有效的方法是保留一组counters并通过Firestore监听器或Cloud Functions观看它们。


最后,请注意一个重要的点:您写到“一个菜单可以包含多个项目。项目可以重复使用,因此可以出现在多个菜单中”。请注意,CollectionReference个文档位于

items

collection('restaurants').doc('r1').collection('menus').doc('m1').collection('items')

是完全不同的文档。这与SQL世界不同,在SQL世界中,一个表中的不同记录可以指向另一张表的相同记录。


结论:每个collection('restaurants').doc('r1').collection('menus').doc('m2').collection('items') 您最应该有一个itemsStock集合,并且每次其中一个项目被“消耗/订购”时,都可以使用FieldValue.increment(-1)来减少其数量。 / p>

换句话说,我建议将构成菜单的项目集合与保存项目计数器的项目集合分开(即restaurant集合)。第一个专用于菜单项的选择,第二个专用于管理餐厅的库存。当来宾/顾客选择/订购商品时,您只会减少包含商品计数器的集合。


根据您的评论进行更新:

如果您要更新餐厅所有菜单中的所有“千层面”项(例如,添加食材,正如您在评论中提到的那样),那么一种非常普遍的方法确实是修改所有相应文档(在NoSQL世界中,这称为数据复制。

您将在我的答案的顶部使用确切的代码:您查询餐厅所有菜单中的所有“千层面”项目文档并进行更新。您可以通过Cloud Function触发此过程,该功能将“监视”您具有参考项目的主集合:每次更改该集合的文档(即一个项目)时,您都会更新菜单中的所有相似/对应项目doc子集合。

答案 1 :(得分:0)

我可以将A公司docRef包含在轮胎集合中,并使用where()缩小结果范围。这似乎是一种有效的方法。虽然,这是顶级集合和子集合之间的混合。这是最佳做法吗?

这是一种常用方法,因为在收集组查询中过滤文档的唯一方法是使用文档的字段。您不能将文档路径中的任何内容用作过滤器。为了方便查询,通常会在NoSQL类型数据库中复制数据。

但是,如果您想将查询限制为仅子集合,则可能不想拥有与子集合同名的顶级集合。