如何更新嵌套对象子元素,其中某些条件 is/are 为真

How to update a nested object child element, where some condition(s) is/are true

注意:这不是重复问题,请阅读到最后并查看包含的图片。

我在 Firestore 中的 collection/document 中有一个嵌套对象和一个数组字段。

主要类别

饮料项目是

零食是

用户可以为具有到期日期的多个项目订阅两个类别:

我想更新 [expDate] 字段,其中 [name] 等于 drinks 和 [type ] 等于 [能量]

我更重要地探索了 Firestore 文档 compound queries in Cloud Firestore 并阅读了很多关于 stackeoverflow 的文章和问题,但我找不到我的答案,下面是我的代码的一部分,我 tr.

db.collection(this.dbName)
    .where("name", "==", "drinks")
    .where("subscriptions.data.type", "==", "energy")
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            let data = doc.data();
            if (data.email == email) {
                let subscriptions = data.subscriptions;
                subscriptions.forEach((subscription) => {
                    if (subscription.name == productName) {
                        let prodTypes = subscription.data;
                        prodTypes.forEach((prodType) => {
                            if (prodType.type == itemType) {
                                let docRef = fb.db.collection(this.dbName).doc(email);
                                fb.db
                                    .collection(this.dbName)
                                    .doc(email)
                                    .update(docRef, {
                                        subscriptions: [
                                            {
                                                data: [
                                                    {
                                                        expDate: expiration,
                                                        type: itemType,
                                                    },
                                                ],
                                                name: productName,
                                            },
                                        ],
                                    });
                            }
                        });
                    }
                });
            }
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

我没有得到上述查询的任何控制台日志结果。

此查询无效:

db.collection(this.dbName)
    .where("name", "==", "drinks")
    .where("subscriptions.data.type", "==", "energy")

这 returns 个文件有:

  • 字段 name 在其根部,值为 "drinks" AND
  • 有一个字段 type 嵌套在 data 下 nestedundersubscriptions**under the root** with the value"energy"`

您显示的文档中没有这些字段,因此查询不会 return 该文档。如果您在文档的根 下添加字段 ,您将看到查询 returns 它。


看起来您正在尝试 return 文档,其中 subscriptions 数组 的项目中包含 特定值。为此,您需要 use the array-contains operator。此操作可以测试数组字段是否包含特定的 complete 值,例如:

db.collection(this.dbName)
    .where("subscriptions", "array-contains", { ... })

但是这里你必须指定数组中必须存在的整个对象。如果该项目存在特定值,则无法检查 属性 是否存在,也无法检查嵌套数组,就像您在此处看到的那样。


与处理 NoSQL 数据库时通常的情况一样,解决方案是 change/augment 您的数据模型以允许用例。由于您要查询具有特定 nametype 的文档,请为该文档中存在的所有 namestypes 添加顶级字段:

{
  names: ["drinks", "snacks"],
  types: ["energy", "water"]
  subscriptions: [
    ...
  ]
}

现在您可以在查询中使用(其中一个)新的顶级字段:

db.collection(this.dbName)
    .where("names", "array-contains", "drinks")

您不能添加第二个 array-contains 子句,因为一个查询在 Firestore 中只能有一个这样的子句。因此,在使用查询仅检索包含 drinks.

的文档后,您必须过滤应用程序代码中的 types

根据我的理解,firebase 复合查询不适用于这种情况,我通过使用 javascript 映射帮助器更改对象并使用函数的 return 数据更新文档来解决问题(changeExpDate),如下:

changeExpDate(data, name, type, expDate) {
  data = [data];
  data.map((obj) => {
    let subscriptions = obj.subscriptions;
    for (var i = 0; i < subscriptions.length; i++) {
      let items = obj.subscriptions[i].data;
      for (var j = 0; j < items.length; j++) {
        if (obj.subscriptions[i].name == name) {
          if (items[j].type == type) {
            obj.subscriptions[i].data[j].expDate = expDate;
          }
        }
      }
    }
  });
  return data;
}

结合你的情况,得到了以下代码:

let v = this.changeExpDate(
    data, //The document objcet
    productName, //Product Name 
    itemType, //Item type
    expiration //Expiration date
);

try {
    fb.db
    .collection(this.dbName)
    .doc(email)
    .update(v[0])
    .then(function () {
        console.log("Updated!");
    })
    .catch(function (error) {
        console.error("Error Updating: ", error);
    });
} catch (error) {
    console.error(error);
}