{
_id: 42,
foo: {
bar: [1, 2, 3, 3, 4, 5, 5]
}
}
我想“删除foo.bar
中$lt: 4
的所有条目以及匹配$eq: 5
”的第一个匹配条目。 重要提示:$eq
部分只能删除单个条目!
我有一个使用3个更新查询的工作解决方案,但这对于这个简单的任务来说太过分了。不过,这就是我到目前为止所做的:
1。查找匹配$eq: 5
和$unset
的第一个条目。 (如您所知:$unset
不删除它。它只是将其设置为null
):
update(
{ 'foo.bar': 5 },
{ $unset: { 'foo.bar.$': 1 } }
)
2。 $pull
所有条目$eq: null
,以便之前的5
真的消失了:
update(
{},
{ $pull: { 'foo.bar': null } }
)
3。 $pull
所有条目$lt: 4
:
update(
{},
{ $pull: { 'foo.bar': { $lt: 4 } } }
)
{
_id: 42,
foo: {
bar: [4, 5]
}
}
扩展查询 1。,以便$unset
条目$lt: 4
和一个条目$eq: 5
。之后我们可以执行查询 2。,无需查询 3。。
将查询 2。扩展到$pull
与$or: [{$lt: 4}, {$eq: 5}]
匹配的所有内容。然后就不需要查询 3。。
将查询 2。扩展为$pull
$not: { $gte: 4 }
的所有内容。此表达式应与$lt: 4
和 $eq: null
匹配。
我已经尝试过实现这些查询,但有时会抱怨查询语法,有时候查询确实执行了,只是删除了什么。
如果有人为此提供了有效的解决方案,那就太好了。
答案 0 :(得分:1)
不确定我是否明白了这一点,但是为了“批量”更新文档,除了oringal $pull
之外,你总是可以采用这种方法,并添加一些“检测”你需要删除的文件“复制“5
来自:
// Remove less than four first
db.collection.update({},{ "$pull": { "foo.bar": { "$lt": 4 } } },{ "multi": true });
// Initialize Bulk
var bulk = db.collection.initializeOrderdBulkOp(),
count = 0;
// Detect and cycle documents with duplicate five to be removed
db.collection.aggregate([
// Project a "reduced" array and calculate if the same size as orig
{ "$project": {
"foo.bar": { "$setUnion": [ "$foo.bar", [] ] },
"same": { "$eq": [
{ "$size": { "$setUnion": [ "$foo.bar", [] ] } },
{ "$size": "$foo.bar" }
] }
}},
// Filter the results that were unchanged
{ "$match": { "same": true } }
]).forEach(function(doc) {
bulk.find({ "_id": doc._id })
.updateOne({ "$set": { "foo.bar": doc.foo.bar.sort() } });
count++;
// Execute per 1000 processed and re-init
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.initializeOrderdBulkOp();
}
});
// Clean up any batched
if ( count % 1000 != 0 )
bulk.execute();
删除小于“4”的任何内容以及从“set”长度的差异中检测到“重复”的所有重复项。
如果您只想将5
的值删除为重复项,则可以对检测和修改采用类似的逻辑方法,而不是使用“set operators”删除任何“重复”的内容使其成为有效的“设置”。
无论如何,一些检测策略将比迭代更新更好,直到“除了一个”之外的值都消失了。
当然你可以稍微简化你的语句并删除一个更新操作,因为$pull
在查询中不允许$or
条件,所以它并不漂亮,但是我希望你能得到这个想法适用:
db.collection.update(
{ "foo.bar": 5 },
{ "$unset": { "foo.bar.$": 1 } },
{ "multi": true }
); // same approach
// So include all the values "less than four"
db.collection.update(
{ "foo.bar": { "$in": [1,2,3,null] } },
{ "$pull": { "foo.bar": { "$in": [1,2,3,null] } }},
{ "multi": true }
);
处理稍微少一些但当然需要精确的整数值。否则坚持你正在做的三个更新。比在代码中循环更好。
作为参考,遗憾的是,无效的“更好”语法将是这样的:
db.collection.update(
{
"$or": [
{ "foo.bar": { "$lt": 4 } },
{ "foo.bar": null }
]
},
{
"$pull": {
"$or": [
{ "foo.bar": { "$lt": 4 } },
{ "foo.bar": null }
]
}
},
{ "multi": true }
);
可能值得一个JIRA问题,但我怀疑主要是因为数组元素不是紧跟在$pull
之后的“第一个”参数。
答案 1 :(得分:1)
您可以使用Array.prototype.filter()
和Array.prototype.splice()
方法
filter()
方法会创建一个foo.bar
值$lt: 4
的新闻数组,然后使用splice
方法删除这些值,第一个值等于5
来自foo.bar
var idx = [];
db.collection.find().forEach(function(doc){
idx = doc.foo.bar.filter(function(el){
return el < 4;
});
for(var i in idx){
doc.foo.bar.splice(doc.foo.bar.indexOf(idx[i]), 1);
}
doc.foo.bar.splice(doc.foo.bar.indexOf(5), 1);
db.collection.save(doc);
} )