使用mongo

时间:2019-10-25 11:06:59

标签: mongodb

我想向数组的所有元素添加一个id字段。没有值添加它没问题:

db.getCollection('mycollection').update({}, {$set : {"data.gastroscopy.$[].id": null }}

但是我真正需要的是将id值设置为元素索引,以获得类似这样的信息:

{
  data: {
    gastroscopy: [
      {
        id: 1,
        foo: 'bar'
      },
      {
        id: 2,
        foo: 'zar'
      },
      {
        id: 3,
        foo: 'car'
      },
    ]
  }
}

I could not find a way to refer the index and use it. Is it possible?

thank you

1 个答案:

答案 0 :(得分:0)

好的,但是也请真正阅读底部

这是“可能的” ,但是唯一可以在单条语句中实现的方法是using MongoDB 4.2 or greater

db.getCollection('mycollection').updateMany(
  {},
  [{ "$set": {
    "data.gastrocopy": {
      "$map": {
        "input": {
          "$range": [0, { "$size": "$data.gastrocopy" }]
        },
        "in": {
          "$mergeObjects": [
            { "$arrayElemAt": [ "$data.gastocopy", "$$this" ] },
            { "id": { "$add": [ "$$this", 1 ] } }
          ]
        }
      }
    }
  }}]
)

所以主要的技巧是$range运算符,它可以生成本质上是索引值的数组,然后将其与$map一起使用,以便将这些值转换为原始数组内容。但是请注意,在任何“更新”语句中使用这些聚合运算符是一项相对较新的功能(在撰写本文时),并且需要MongoDB 4.2或更高版本。

否则,您基本上就是循环收集并回写每个文档,最好使用bulkWrite()

var batch = [];

db.getCollection('mycollection').find().forEach(doc => {

   batch.push({
     "updateOne": {
       "filter": { _id: doc._id },
       "udpate": {
         "$set": { "data.gastrocopy": doc.data.gastrocopy.map((e,i) => ({ ...e, id: i+1 })) }
       }
     }
   });

   if (batch.length >= 1000) {
      db.getCollection('mycollection').bulkWrite(batch);
      batch = [];
   }

})

if (batch.length > 0) {
  db.getCollection('mycollection').bulkWrite(batch);
  batch = [];
}

基本上是相同的基本机制,但是当然要反复进行,并通过将每个文档读回客户端,因为在早期的MongoDB版本中没有其他方法可以做到这一点。

我也不喜欢这两种方法,因为两者都存在覆盖现有内容的固有风险,这些现有内容可能是通过在读取和读取之间的另一个过程添加的 写。 MongoDB 4.2“聚合”语法的出现机会较少,但我相信在大容量环境中它的使用机会仍然较小。

因此,实施风险自负。您可以通过位置更新使某些事情变得更好,但对“索引值”的任何更新都不是真正可靠的。同样,这完全取决于您对“所述”数组的实际使用情况。


重要

我会注意到,尽管如此,我仍然很难看到这种更新的实用性。数组毕竟是 Array ,因此固有地具有索引位置(特别是n-1,这是标准约定)。如果要按索引位置匹配事物,则MongoDB中已经内置了操作,而无需添加任意值进行存储。

您可能会查看"dot notation"$slice之类的运算符进行投影甚至聚合$slice来意识到您要创建的内容已经存在。关于 值得使用的地方将是带有查询表达式的,例如:

{ "data.gastrocopy": { "$elemMatch": { "id": { "$gte": 0, "$lte": 3 } } } }

然后仅当您在该id字段上创建索引时才可以。这将比已经提到的其他“基于数组索引”的方法具有更高的性能。

因此,实际上是否需要这样的更新取决于您要使用它的方式。