【问题标题】:Update an object in array or insert into the array using a unique field更新数组中的对象或使用唯一字段插入数组
【发布时间】:2022-01-27 15:51:30
【问题描述】:

我有一个文档,其中包含一个包含对象元素的数组属性。

例如

{
    "_id": "61c01098bc29520008efc00f",
    "name": "codedump",
    "employments": [
         {
              "type": "PERMANENT",
              "salary": 1000000000
         },
         {
              "type": "TEMP",
              "salary": 1000000000
         },
         {
              "type": "PART_TIME",
              "salary": 1000000000
         },
    ]
}

并且在使用type 更新就业工资时,如果该类型已经在就业中,那么我只需要更新salary。否则我需要将新项目推送到数组中。我正在尝试找到一种方法在单个查询中完成这项工作。

  1. 如果该类型已经存在于数组中(用于更新):
/// Update data:
             {
                  "type": "PERMANENT",
                  "salary": 10
             }
// For this case I want the example document to become:
{
    "_id": "61c01098bc29520008efc00f",
    "name": "codedump",
    "employments": [
         {
              "type": "PERMANENT",
              "salary": 10 // From 1000000000 to 10         
         },
         {
              "type": "TEMP",
              "salary": 1000000000
         },
         {
              "type": "PART_TIME",
              "salary": 1000000000
         },
    ]
}
  1. 如果类型不存在:
/// Update data:
             {
                  "type": "INVESTMENT",
                  "salary": 10000
             }
// For this case I want the example document to become:
{
    "_id": "61c01098bc29520008efc00f",
    "name": "codedump",
    "employments": [
         {
              "type": "PERMANENT",
              "salary": 1000000000      
         },
         {
              "type": "TEMP",
              "salary": 1000000000
         },
         {
              "type": "PART_TIME",
              "salary": 1000000000
         },
         {
              "type": "INVESTMENT",
              "salary": 10000
         }
    ]
}

【问题讨论】:

    标签: node.js arrays mongodb aws-lambda mongodb-query


    【解决方案1】:

    我不知道你想要的东西是否可以在一次操作中实现......

    话虽如此,我会怎么做,使用$ 位置运算符来更新我想要的数组元素。 Doc & playground.

    db.collection.update({
      "employments.type": "PERMANENT",
      
    },
    {
      "$set": {
        "employments.$.salary": 10
      }
    },
    {
      "multi": false,
      "upsert": false
    })
    

    此操作仅在元素存在于数组中时才有效。否则,操作将失败。然后您可以在代码中捕获异常并使用$push 执行插入。 Playground

    db.collection.update({},
    {
      "$push": {
        "employments": [
          {
            "type": "INVESTMENT",
            "salary": 10000
          }
        ]
      }
    },
    {
      "multi": false,
      "upsert": false
    })
    

    【讨论】:

    • 是的,我曾想过。但我试图探索一个单一的查询选项。有 $addToSet 但对于更新或推送数组内的对象,它实际上是无用的。感谢您的回答!
    • 正如我所说我不知道​​另一种方式,upsert 在数组中不起作用。但也许有人知道更好的。
    【解决方案2】:

    技术上可以通过 1 次调用,从 Mongo v4.2 开始,您可以使用 pipelined updates,这实质上允许您在更新正文中使用聚合运算符,因此您几乎可以实现任何目标。

    我会说,但是它绝对不是很有效,这真的取决于您想要最大化/最小化系统的哪个部分(网络/数据库负载/等)。

    你可以这样做:

    const input =  {
      "type": "INVESTMENT",
      "salary": 10000
    }
    
    db.collection.update({},
    [
      {
        $set: {
          employments: {
            $cond: [
              {
                $in: [
                  input.type,
                  "$employments.type"
                ]
              },
              {
                $map: {
                  input: "$employments",
                  in: {
                    $cond: [
                      {
                        $eq: [
                          "$$this.type",
                          input.type,
                        ]
                      },
                      {
                        type: "$$this.type",
                        salary: input.salary
                      },
                      "$$this"
                    ]
                  }
                }
              },
              {
                $concatArrays: [
                  "$employments",
                  [
                    input
                  ]
                ]
              }
            ]
          }
        }
      }
    ])
    

    Mongo Playground

    【讨论】:

    • 这个查询效率低的任何具体原因?我想最小化数据库负载、多个数据库调用并减少代码。
    • 这个sn-p给我Error: MongoError: The dollar ($) prefixed field '$set' in '0.$set' is not valid for storage.
    • 我建议你先使用游乐场链接,它表明它是有效的。那时你应该弄清楚你做了什么改变来打破它。还要确保您使用的是 Mongo v4.2+
    • 是的,我发现了错误。我正在使用此逻辑对嵌套数组执行更新。似乎基于索引的选择不适用于更新管道,例如parent.0.user.employments。工作正常不嵌套寿。你对嵌套数组有什么想法吗?
    • 如果您想提出不同的问题以执行不同类型的更新,请随意这样做,但我提供的管道可以满足问题的要求。想象它是一个聚合管道,因此您无法访问其中的本机更新运算符。
    猜你喜欢
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    相关资源
    最近更新 更多