我有以下结构:
document: {
'data': {
'field_1': 'blabla',
'field_2': 'blabla++',
'interesting_field': [
{
'f_1': 'abc',
'this_is_interesting': [
{
'a1': 'text',
'a2': 'other_text'
},
{
'a1': 'text 2',
'a2': 'text 3'
}
]
},
'etc': 'etc'
]
},
'somthing_boring': 'bla'
}
我需要做的是更新所有' a2'来自' this_is_interesting'来自' interesting_field'字段(如果它们符合某个标准)。
要在1级数组中更新,我可以执行以下操作:
{
'_id': new ObjectId('5621020d78946ac20a000021'),
'data.interesting_field' : {
$elemMatch : {
'f_1' : 'abc'
}
}
},
{$set: {'data.interesting_field.$.something_to_update': 'something'}}
但是......似乎我无法使用2级数组的匹配。任何想法如何做到这一点? (想到了mapReduce ......但是我觉得我不能在更新查询中使用它。)
答案 0 :(得分:2)
正如您所指出的,通常使用位置运算符$
执行此操作。
不幸的是,现在位置运算符只支持数组深度匹配。
对于您想要的行为,有一个JIRA票证:https://jira.mongodb.org/browse/SERVER-831
如果您希望能够更改this_is_interesting
中的匹配元素,则必须更改数据结构 - 将其展平,或将其拆分为两个集合。
编辑:实际上,您可以使用MapReduce进行更新,如以下答案所述:https://stackoverflow.com/a/33172255/236660
但这真的非常低效。我建议您重新构建数据。
答案 1 :(得分:1)
好吧,假设您要更新集合中a2: 'other_text'
的所有文档,并将a2
值设置为new_other
。使用文档的实际结构,您需要遍历集合中的每个文档,然后对interesting_field
数组中的每个元素进行循环,然后对this_is_interesting
数组中的每个元素检查{{1}的值是否为a2
匹配给定的条件,在这种情况下,您使用所谓的"dot notation"和"bulk"操作更新相应的子文档,以实现最高效率。您必须动态构建查询才能使其生效。
var bulkOp = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.find({ "document.data.interesting_field.this_is_interesting.a2": 'other_text' }).forEach(function(doc) {
var interesting_field = doc.document.data.interesting_field;
var size = interesting_field.length;
for(var ind=0; ind < size; ind++) {
var this_is_interesting = doc.document.data.interesting_field[ind]['this_is_interesting'];
for(var ix=0; ix < this_is_interesting.length; ix++) {
if(this_is_interesting[ix]['a2'] === 'other_text') {
var updateDocument = {};
updateDocument['document.data.interesting_field.' + ind + '.this_is_interesting.' + ix + '.a2'] = 'new_other';
bulkOp.find({'_id': doc._id}).update({
'$set': updateDocument
});
count++;
}
}
if(count % 200 === 0) {
// Execute per 200 operations and re-init
bulkOp.execute();
bulkOp = db.collection.initializeOrderedBulkOp();
}
}
})
// Clean up queues
if(count > 0) {
bulkOp.execute();
}
但最好的办法是改变你的文件结构
答案 2 :(得分:0)
您可以使用positional filter with array filters,自MongoDB 3.6起可用。
例如,要将a2更新为a1 ==“文本2”的“某物”,可以使用:
db.yourCollection.update(
{
'_id': new ObjectId('5621020d78946ac20a000021'),
'data.interesting_field' : {
$elemMatch : {
'f_1' : 'abc'
}
}
},
{$set: {'data.interesting_field.$.this_is_interesting.$[elem].a2': 'something'}},
{ arrayFilters: [ {'elem.a1': 'text 2' ] })