firebase 如何在cloud firestore中根据主集合字段和子集合字段查询子集合中的单据?

nx7onnlm  于 2023-02-13  发布在  其他
关注(0)|答案(2)|浏览(172)

我是新的firebase云firestore和需要帮助了解它一点。
我正在处理以下结构的项目。

每个“Restaurant”文档都包含自己的“products”子集合。
在这里,我想运行一个查询,以获取不同餐馆的所有产品,这些产品包含标签“coffee”,并且具有特定的Pincode“234144”。
我尝试了集合组查询,通过添加密码到每个产品拥有的特定餐厅,但改变密码将花费很大,因为所有的产品将不得不编辑,我猜。
是否有任何有效的方法来做这件事或它是不可能在这个数据库中以一种有效的方式?
请让我知道你的想法...谢谢。

rnmwe5a2

rnmwe5a21#

如果我理解正确的话,您希望检索具有特定标签和密码的所有产品(我想这类似于邮政编码?)。实际上,我能想到的只有两种选择:

收集组查询

正如您提到的,您可以将 tagpincode 都存储在 product 文档中,然后执行一个集合组查询,沿着所示(请原谅JavaScript,但我不熟悉Dart,它应该非常相似):

var products = await firestore.collectionGroup('products')
                       .where('tag', '==', 'coffee')
                       .where('pincode', '==', '234144').get();

正如您所注意到的,使用此解决方案时,您需要在每个 * 产品 * 中保留 pincode。这部分数据是重复的,您会觉得应该避免,因为这样做既浪费又危险(可能会失去同步),但这是正确的做法!这称为反规范化。这在this video by Todd Kerpelman中有详细解释。
然后,您可以创建由 restaurant 更新触发的云函数,以保持 products 中的 pincode 与 * restaurants* 中的相应 pincode 同步

先查询餐厅再查询产品

要仅保留餐厅的 * 密码 *,您必须分两步进行查询:首先过滤具有特定密码的餐馆,然后通过标签过滤产品:

// 1 - retrieve restaurants in specific pincode
var restaurants = await firestore.collection('restaurants').where('pincode', '==', '234144').get();

// 2 - For each retrieved restaurant, retrieve all products matching the tag
var products = [];
for(let i = 0; i < restaurants.docs.length; ++i) {
  var p = await restaurants.docs[i].collection("products").where("tag", "==", "coffee");
  products.push(p);
}

使用这种方法,不需要在每个 * 产品 * 中复制 * 密码 *,但是您的查询不是最优的,因为您加载了潜在无用的 * 餐馆 *,它们在您的密码中不提供咖啡。

guykilcj

guykilcj2#

我有一个类似的数据结构,Louis Coulet的回答帮了我很大的忙,但是,我遇到了一个问题,因为我的node/firebase环境抱怨restaurants.docs[i].collection不是一个函数,而我不得不使用restaurants.docs[i].ref.collection
因此,在我的例子中,下面是最终工作的代码(使用原始海报的“restaurants/products”数据模型作为示例):

// 1 - retrieve restaurants in specific pincode
const restaurants = await firestore.collection('restaurants').where('pincode', '==', '234144').get();

// 2 - For each retrieved restaurant, retrieve all products matching the tag 'coffee'
const products = [];
for (let i = 0; i < restaurants.docs.length; i++) {      
    await restaurants.docs[i].ref
          .collection('products')
          .where('tag', '==', 'coffee')
          .get()
          .then(s => Promise.all(s.docs.map(d => {
              products.push(d);
          })));
}

我发现我需要通过某种承诺返回子集合内容,否则,在我尝试获取变量中的数据(在本例中为products)后,它只是显示为未定义。
在填充products变量之后,我就可以读取它的内容,如下所示:

products.forEach(p => {
   console.log(p.data().name);
   console.log(p.data().tag);
}

我希望有人觉得这是有用的,很多很多感谢路易斯库莱特送我/我们走上正确的道路与这个子收集头痛:-)

相关问题