MongoDB Java - 为现有数组的每个元素创建一个新的 ObjectId

MongoDB Java - Create a new ObjectId for each element of an existing array

我有一个现有的集合,其中包含多个文档。

[{
    "_id": "...1",
    "prop1": "...",
    "prop2": "...",
    "someArray": [
        {
            "value": "sub element 1.1"
        },
        {
            "value": "sub element 1.2"
        },
        {
            "value": "sub element 1.3"
        }
    ]
}, {
    "_id": "...2",
    "prop1": "...",
    "prop2": "...",
    "someArray": [
        {
            "value": "sub element 2.1"
        },
        {
            "value": "sub element 2.2"
        }
    ]
}, // many others here...
]

对于每个根文档,我想在 someArray 的每个子元素上添加类型 ObjectId_id 属性。所以,在我运行我的命令之后,集合的内容如下:

[{
    "_id": "...1",
    "prop1": "...",
    "prop2": "...",
    "someArray": [
        {
            "_id": ObjectId("..."),
            "value": "sub element 1.1"
        },
        {
            "_id": ObjectId("..."),
            "value": "sub element 1.2"
        },
        {
            "_id": ObjectId("..."),
            "value": "sub element 1.3"
        }
    ]
}, {
    "_id": "...2",
    "prop1": "...",
    "prop2": "...",
    "someArray": [
        {
            "_id": ObjectId("..."),
            "value": "sub element 2.1"
        },
        {
            "_id": ObjectId("..."),
            "value": "sub element 2.2"
        }
    ]
}, // ...
]

当然,每个 ObjectId 都是独一无二的。

我越接近这个:

db.getCollection('myCollection').updateMany({}, { "$set" : { "someArray.$[]._id" : ObjectId() } });

但是整个集合的每个子元素最终都具有相同的 ObjectId 值...

理想情况下,我需要使用 MongoDB 的 Java 驱动程序使其工作。我得到的最接近的版本是这个(它提出了完全相同的问题:创建的所有 ObjectId 都具有相同的值)。

database
    .getCollection("myCollection")
    .updateMany(
        Filters.ne("someArray", Collections.emptyList()), // do not update empty arrays
        new Document("$set", new Document("someArray.$[el]._id", "ObjectId()")), // set the new ObjectId...
        new UpdateOptions().arrayFilters(
            Arrays.asList(Filters.exists("el._id", false)) // ... only when the _id property doesn't already exist
        )
    );

使用MongoDB v4.4+,可以使用$function来使用javascript赋值数组中的_id。

db.collection.aggregate([
  {
    "$addFields": {
      "someArray": {
        $function: {
          body: function(arr) {
                  return arr.map(function(elem) {
                           elem['_id'] = new ObjectId(); 
                           return elem;
                         })
                },
          args: [
            "$someArray"
          ],
          lang: "js"
        }
      }
    }
  }
])

这里是Mongo playground供您参考。 (与上面的代码略有不同,因为playground要求js代码用双引号)


对于旧版本的MongoDB,您需要使用javascript循环文档并逐一更新它们。

db.getCollection("...").find({}).forEach(function(doc) {
  doc.someArray = doc.someArray.map(function(elem) {
    elem['_id'] = new ObjectId(); 
    return elem;
  })
  db.getCollection("...").save(doc);
})

这是我最后写的:

MongoCollection<Document> collection = database.getCollection("myCollection");
collection
    .find(Filters.ne("someArray", Collections.emptyList()), MyItem.class)
    .forEach(item -> {
        item.getSomeArray().forEach(element -> {
            if( element.getId() == null ){
                collection.updateOne(
                    Filters.and(
                        Filters.eq("_id", item.getId()),
                        Filters.eq("someArray.value", element.getValue())
                    ),
                    Updates.set("someArray.$._id", new ObjectId())
                );
            }
        });
    });

sub-elements 的 value 属性 必须是唯一的(幸运的是它是)。而且我必须执行单独的 updateOne 操作才能为每个元素获得不同的 ObjectId