MongoDB 在特定索引处更新文档的数组元素,并在不匹配时插入新文档

问题描述 投票:0回答:1

更新:这个问题的真正目的是找到一个快速的解决方案,我是 MongoDB 的新手,所以我认为如果使用单个查询它会很快,但是,任何快速的解决方案都可以。

基本上我正在寻找解决以下问题的解决方案。由于每个时刻都有数百个这样的操作,因此操作需要快速。

所以我有一个曲线集合,其中包含这样的文档:

{
    curveId: 12,
    values: [1, 2, 12, 7, ...]
}

假设我想在索引 3 处设置曲线值并导致:

{
    curveId: 12,
    values: [1, 2, 12, new_value, ...]
}

如果没有匹配的 curveId,则会创建一条新曲线:

{
    curveId: 12,
    values: [null, null, null, new_value]
}

所以我写了这个更新插入查询:

db.curves.update({
    curveId: 12
},{
    $set: { "values.3": new_value }
},{
    upsert: true
})

当存在匹配的文档时,此方法有效。但是,如果没有匹配,它将创建一个新文档,如下所示:

{
    curveId: 12,
    values: { '3': new_value }
}

values
不是一个数组,不是我所期望的。

我用谷歌搜索了很长时间但还没有找到解决方案。是否有可能通过一个查询来解决问题?

谢谢你。

javascript arrays mongodb upsert
1个回答
0
投票

$concatArrays
数组存在并且是所需的最小大小时,此方法使用
$slice
value
。当不满足任何这些条件时,它会使用
$zip
$map
来填充缺失值。

更新管道处理这些情况:

[
  { "_id": "curveId exists and is larger", "curveId": 12, "values": [1, 2, 3, 7, 8, 9] },
  { "_id": "curveId exists and values is exact", "curveId": 12, "values": [1, 2, 3, 4] },
  { "_id": "curveId exists but values is short by 1", "curveId": 12, "values": [1, 2, 3] },
  { "_id": "curveId exists but values is short by 2", "curveId": 12, "values": [1, 2] },
  { "_id": "curveId exists but values is empty", "curveId": 12, "values": [] },
  { "_id": "curveId exists but values is null", "curveId": 12, "values": null },
  { "_id": "curveId exists but values does not exist", "curveId": 12 },
  { "_id": "curveId 99 doesn't exist" }
]

使用聚合表达式更新管道:

db.collection.update({ curveId: 12 },
[
  {
    $set: {
      values: {
        $let: {
          vars: {
            // index & new value to set
            idx: 3,
            new_val: 1000
          },
          in: {
            $cond: {
              if: {
                $and: [
                  { $isArray: "$values" },
                  { $lte: [ "$$idx", { $size: "$values" }] }  // "lte" is correct here
                ]
              },
              then: {
                $concatArrays: [
                  { $slice: ["$values", "$$idx"] },
                  ["$$new_val"],
                  { $slice: ["$values", { $add: ["$$idx", 1] }, { $size: "$values" }] }
                ]
              },
              else: {
                $let: {
                  vars: {
                    vals_nulls: {
                      $map: {
                        input: {
                          $zip: {
                            inputs: [
                              { $ifNull: ["$values", []] },
                              { $range: [0, "$$idx"] }
                            ],
                            useLongestLength: true
                          }
                        },
                        in: { $first: "$$this" }
                      }
                    }
                  },
                  in: {
                    $concatArrays: [
                      { $slice: ["$$vals_nulls", "$$idx"] },
                      ["$$new_val"],
                      { $slice: [
                        "$$vals_nulls",
                        { $add: ["$$idx", 1 ] },
                        { $size: "$$vals_nulls" }
                      ]}
                    ]
                  }
                }
              }
            }
          }
        }
      }
    }
  }
],
{ upsert: true, multi: true }
)

注:

  • 要设置的
    new_value
    index
    只需放在第一个
    $set -> values -> $let
    部分即可
    • 对它的所有其他引用都是变量。
    • 使用您语言的
      let
      参数(如果可用)代替此参数;猫鼬和游乐场没有它
  • multi: true
    只需要演示一次执行中的所有更新场景
  • 使用 curveId 99(任何非 12 的值)查看
    upsert
    行为。

蒙戈游乐场

顺便说一句,

$map
不是一个“慢”操作,至少在这种情况下或在评论中讨论的情况下不是。相比之下,更新的文档检索要慢得多。我将发布一个单独的答案,每次都使用 $map,就像在
cmghess 的评论
中一样,但使用 concat 和 slice。

© www.soinside.com 2019 - 2024. All rights reserved.