使用$ addToSet修饰符在前面添加值

时间:2015-07-07 18:46:25

标签: mongodb mongoose mongodb-query

我正在尝试在mongo上运行更新查询,这将在现有数组属性中添加几个值。

我的目标是在数组的开头添加元素,而不是在默认情况下结束时添加元素。这是我正在使用的代码:

db.collection("Project").update({'_id': group.project}, {$addToSet: {'endpointOrder': {$each: group.endpointOrder, $position: 0}}}, {multi: true})

正如您在上面所看到的,如果我只是更改了“addToSet”'使用'推送'修饰符,然后工作正常,但这将保留重复,这是不可能的。所以,我的假设是,$ position只支持' $ push' ,而不是为了'$ addToSet'。

关于如何实现这一点的任何想法/建议?感谢。

1 个答案:

答案 0 :(得分:7)

$addToSet运算符生成一组“唯一”项,这可能就是你所追求的。不幸的是,MongoDB对于这个和其他“集合运算符”有一个共同的口头禅:

  

“套装不被视为有序”

因此,当您将项目添加到“集合”(或与其他操作结合)时,无法保证项目出现在任何位置,无论它是“集合”的开头还是结尾。官方的说法是任何元素都可以按任何顺序出现,只要它是“集合”的一部分就无关紧要。

但还有另外一种解决方法,它只是意味着您不使用$addToSet,而是将其他过滤逻辑与$push:结合使用

考虑一下文件:

{ "a": [ 3,1,2 ] }

因此,如果我想在列表中添加一个新元素4并将其添加到“第一”位置,其中元素不是“set”的一部分,那么您只需构造查询并更新像这样:

db.collection.update(
    { "a": { "$ne": 4 } },
    { "$push": { "a": { "$each": [4], "$position": 0 } } }
)

简而言之:“如果数组中已经存在该值,请不要推送到数组。”

真的就是这一切。如果您要求添加已存在的元素,那么查询条件当然是false,并且不会修改任何内容。

最后,您获得与$addToSet操作相同的结果,除非您完全控制元素定位或甚至根据需要“排序”。您需要做的就是在决定是否更改任何内容之前检查匹配元素。

处理多个元素的过程实际上应该使用“批量”操作API以实现最高效率

var items = [4,3];

var bulk = db.collection.initializeOrderedBulkOp();
bulk.find({ "a": { "$nin": items } }).updateOne(
    { "$push": { "a": { "$each": items, "$position": 0 } } }
);

items.forEach(function(item) {
    bulk.find({ "a": { "$ne": item } }).updateOne(
        { "$push": { "a": { "$each": [item], "$position": 0 } } }
    );
});
bulk.execute();    

这将导致循环中只有一个更新操作实际匹配或修改任何内容:

{ "a": [ 4,3,1,2 ] }

或者根本不存在的值:

var items = [5,8];

var bulk = db.collection.initializeOrderedBulkOp();
bulk.find({ "a": { "$nin": items } }).updateOne(
    { "$push": { "a": { "$each": items, "$position": 0 } } }
);

items.forEach(function(item) {
    bulk.find({ "a": { "$ne": item } }).updateOne(
        { "$push": { "a": { "$each": [item], "$position": 0 } } }
    );
});
bulk.execute();    

然后第一个操作实际上会修改,循环指令将不匹配,因为项目已经存在。

{ "a": [ 5,8,4,3,1,2 ] }