【问题标题】:MongoDB upsert inside arrayMongoDB 在数组中插入
【发布时间】:2020-03-18 05:17:55
【问题描述】:

我正在尝试在 mongoDB 数组中插入、更新、删除值。我的 mongoDB 版本是 3.4.23

这是我的收藏:

{
  "link": "abc.com",
  "Values": [{
      "valueID": "v1",
      "date": "05-07-2015",
      "value": "10"
  }]
}

我正在尝试进行一些 upsert 类型的查询来插入/更新 Values 数组数据,但无法这样做。我得到了this 解决方案,但它对我不起作用。

假设我有"valueID": "v1"

{
    "valueID": "v1",
    "date": "05-07-2015",
    "value": "20"
}

所以在这种情况下,我想在上面的集合中设置"value": "20",所以输出应该是:

{
  "link": "abc.com",
  "Values": [{
      "valueID": "v1",
      "date": "05-07-2015",
      "value": "20"//updated value
  }]
}

如果我通过 "valueID": "v2" 那么它应该像这样将新值插入到集合中:

{
  "link": "abc.com",
  "Values": [{
      "valueID": "v1",
      "date": "05-07-2015",
      "value": "10"
  },
  {
    "valueID": "v2",
    "date": "xyz",
    "value": "xyz"
  }]
}

我们将非常感谢您的帮助。

【问题讨论】:

  • 试试here提到的bulkWrite方法

标签: javascript node.js mongodb mongodb-query


【解决方案1】:

很遗憾,这不能在使用 upsert 或类似名称的单个更新语句中完成。

这可以使用从 MongoDB 3.2 开始支持的bulkWrite() 来实现。

您还可以使用 MongoDB 4.2 中的新 update() 命令 - 请注意,这仅在 4.2 中可用。

更新命令现在允许多个更新语句。虽然您可能需要运行两个更新命令,但您只需发送一次命令并让 MongoDB 为您批处理语句。

根据您的要求,您可以尝试以下操作,首先尝试使用 $ positional 运算符更新数组中的相关元素。

然后我们使用$addToSet 数组运算符,它将尝试添加一个新的数组元素 - 仅当没有找到匹配的数组元素非常适合我们在步骤 1 中无法进行更新的场景.

两种解决方案都适用于您的场景。

使用 bulkWrite()

db.getCollection("tests").bulkWrite([
    {
        updateOne: {
            filter: {link: "abc.com", "Values.valueID": "v2"},
            update: {$set: {"Values.$.value": "xyz"}}
        }
    },
    {
        updateOne: {
            filter: {link: "abc.com"},
            update: {
                $addToSet: {
                    "Values": {
                        "valueID": "v2",
                        "date": "05-07-2015",
                        "value": "xyz"
                    }
                }
            }
        }
    }
]);

使用新的 update() 命令:

db.runCommand(
            {
                update: "tests",
                updates: [
                    {
                        q: {link: "abc.com", "Values.valueID": "v2"},
                        u: {$set: {"Values.$.value": "xyz"}}
                    },
                    {
                        q: {link: "abc.com"},
                        u: {
                            $addToSet: {
                                "Values": {
                                    "valueID": "v2",
                                    "date": "05-07-2015",
                                    "value": "xyz"
                                }
                            }
                        }
                    }
                ],
                ordered: false,
                writeConcern: {w: "majority", wtimeout: 5000}
            }
        )

样本数据:

db.getCollection("tests").insertMany([{
        "link": "abc.com",
        "Values": [
            {
                "valueID": "v1",
                "date": "05-07-2015",
                "value": "10"
            }
        ]
    },
    {
        "link": "def.com",
        "Values": [
            {
                "valueID": "v1",
                "date": "05-07-2015",
                "value": "1"
            }
        ]
    }]
);

不存在时插入数组:

db.runCommand(
            {
                update: "tests",
                updates: [
                    {
                        q: {link: "abc.com", "Values.valueID": "v2"},
                        u: {$set: {"Values.$.value": "xyz"}}
                    },
                    {
                        q: {link: "abc.com"},
                        u: {
                            $addToSet: {
                                "Values": {
                                    "valueID": "v2",
                                    "date": "05-07-2015",
                                    "value": "xyz"
                                }
                            }
                        }
                    }
                ],
                ordered: false,
                writeConcern: {w: "majority", wtimeout: 5000}
            }
        )

结果:

    { 
        "_id" : ObjectId("5dd8164969f4361ce9821b88"), 
        "link" : "abc.com", 
        "Values" : [
            {
                "valueID" : "v1", 
                "date" : "05-07-2015", 
                "value" : "20"
            }, 
            {
                "valueID" : "v2", 
                "date" : "05-07-2015", 
                "value" : "xyz"
            }
        ]
    }    

更新现有值:

db.runCommand(
            {
                update: "tests",
                updates: [
                    {
                        q: {link: "abc.com", "Values.valueID": "v2"},
                        u: {$set: {"Values.$.value": "new value"}}
                    },
                    {
                        q: {link: "abc.com"},
                        u: {
                            $addToSet: {
                                "Values": {
                                    "valueID": "v2",
                                    "date": "05-07-2015",
                                    "value": "new value"
                                }
                            }
                        }
                    }
                ],
                ordered: false,
                writeConcern: {w: "majority", wtimeout: 5000}
            }
        )

结果:

{ 
    "_id" : ObjectId("5dd8164969f4361ce9821b88"), 
    "link" : "abc.com", 
    "Values" : [
        {
            "valueID" : "v1", 
            "date" : "05-07-2015", 
            "value" : "20"
        }, 
        {
            "valueID" : "v2", 
            "date" : "05-07-2015", 
            "value" : "new value"
        }
    ]
}

【讨论】:

  • 这个需要详细检查一下。
  • 当然,让您运行 v 4.2。我更新了答案以包含示例数据和结果。
  • 哦,我可能会在这里遇到版本控制问题。顺便说一句,我将尝试与 v 4.2 相同。
  • 您可以在 MongoDB 3.2 以上版本中使用 bulkWrite()
【解决方案2】:

看看 $addToSet 运算符

$addToSet 运算符向数组添加一个值,除非该值是 已经存在,在这种情况下 $addToSet 对该数组不做任何事情。

https://docs.mongodb.com/manual/reference/operator/update/addToSet/index.html

【讨论】:

    【解决方案3】:

    MongoDB 4.2

    Update with Aggregation Pipeline

    从 MongoDB 4.2 开始,db.collection.update() 方法可以接受 一个聚合管道 [ stage1, stage2, ... ],它指定 要执行的修改。

    所以这里$reduce 运算符和一些$condition 可以解决问题。

    db.getCollection("ashish2").update(
      //filter criteria for the update operation
      { },
      [{ "$set": {
        "Values": {
          "$reduce": {
            "input": "$Values",
            "initialValue": [],
            "in": {
              "$concatArrays": [
                "$$value",
                {
                  "$cond": [
                    // Here you can select your value with which you want to match
                    { "$eq": ["$$this.valueID", "v2"] },
                    // Updation after match
                    [{ "valueID": "v1", "date": "05-07-2015", "value": "20" }], 
                    // Updation when doesn't match
                    {
                      "$concatArrays": [
                        [{ "valueID": "v1", "date": "05-07-2015", "value": "20" }],
                        ["$$this"]
                      ]
                    }
                  ]
                }
              ]
            }
          }
        }
      }}]
    )
    

    注意:对于私有版本,您要么必须使用两个查询,要么可以查看此答案Can you specify a key for $addToSet in Mongo?

    【讨论】:

    • 上面写着code: 14 codeName: "TypeMismatch" errmsg: "Wrong type for 'u'. Expected a object, got a array." name: "MongoError"。我的 mongoDB 版本是 3.4.23
    • @SaurabhAgrawal 你确定你使用的是 mongodb 4.2 吗?
    • 不,是`3.4.23`
    • @SaurabhAgrawal 我已经在答案中提到你至少应该有 4.2
    • 根据我的经验,如果数组为空,则不会插入,并且每次不匹配时都会插入一个对象(而不是仅插入一次)。它似乎仅在您要更新仅包含一个对象并且与之匹配的数组时才有效。当我对@Ashh 的回答没有任何问题时,无法完成这项工作
    猜你喜欢
    • 1970-01-01
    • 2014-03-02
    • 2022-01-17
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多