$addToSet 运算符生成一组“独特”项目,这很可能是您所追求的。不幸的是,对于这个和其他“集合运算符”,MongoDB 有一个共同的口头禅:
“集合不被认为是有序的”
因此,当您将项目添加到“集合”(或与其他操作组合)时,无法保证项目出现在任何位置,无论它是“集合”的开头还是结尾。官方的说法是任何元素都可以以任何顺序出现,只要它是“集合”的一部分就没有关系。
但是还有另一种解决方法,它只是意味着您不使用$addToSet,而是结合$push:使用其他一些过滤逻辑
考虑文档:
{ "a": [ 3,1,2 ] }
因此,如果我想在列表中添加一个新元素 4 并将其添加到该元素尚未属于“集合”的“第一个”位置,那么您只需构建查询并像这样更新:
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 ] }