【问题标题】:How to update a nested object child element, where some condition(s) is/are true如何更新嵌套对象子元素,其中某些条件为真
【发布时间】:2022-01-13 07:40:14
【问题描述】:

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

我在 Firestore 的集合/文档中有一个嵌套对象和一个数组字段。

主要类别

  • 饮料
  • 小吃

饮料项目

  • (水、能量、牛奶……)

零食是

  • (薯片、饼干、玉米、..)

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

  • 饮料->能量
  • 饮料->牛奶
  • 小吃->薯片

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

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

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);
    });

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

【问题讨论】:

  • @mark-rotteveel,感谢您的编辑,但您从问题中删除了 firebase 关键字,我希望将其加粗,并且具有 firebase 专业知识的人会看到该问题。
  • 这就是标签系统的用途。您不应该只在问题的标题中添加标签。

标签: javascript firebase google-cloud-firestore nested-object compound-query


【解决方案1】:

此查询不起作用:

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

这将返回具有:

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

这些字段都不存在于您显示的文档中,因此查询不会返回该文档。如果您在文档的根目录下添加字段,您将看到查询返回它。


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

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

但在这里您必须指定必须存在于数组中的整个对象。如果项目存在具有特定值,则无法检查一个属性是否存在,并且您也无法检查嵌套数组,因为您似乎在这里。


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

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

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

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

您不能添加第二个 array-contains 子句,因为查询在 Firestore 中只能有一个这样的子句。因此,在使用查询仅检索包含 drinks 的文档后,您必须在应用程序代码中过滤 types

【讨论】:

  • Frank,谢谢你的回答,主要问题是如何更新名称等于饮料,类型等于能量的对象,场景如下:主要类别是(饮料,零食, ..)饮料项目是(水,能量,牛奶,......)零食项目是(薯片,饼干,玉米,..)用户可以订阅两个类别的多个具有到期日期的项目: - 饮料 - >Energy - Drinks->Milk - Snack->Chips 所以我用于插入数据的架构工作正常,现在我无法更新这些项目的到期日期,请看截图。
  • 在使用查询仅检索包含drinks 的文档后,您必须过滤应用程序代码中的types,然后遍历剩余的文档并依次更新每个文档(正如你已经在做的那样)。
  • 弗兰克,请你把查询和代码写下来会更有帮助,我按照你说的做了,但我无法更新对象,提前谢谢你。
  • 如果您在使用我的回答方法时遇到问题,请在问题底部提供更新,显示您为实现它而编写的代码。
  • 我正在尝试将这种方法和一种新方法结合起来,如果我得到结果,我会在这里发布,谢谢你的投入,弗兰克。
【解决方案2】:

根据我的理解,firebase 复合查询不适用于这种情况,我通过使用 javascript map helper 更改对象并使用函数 (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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    相关资源
    最近更新 更多