【问题标题】:Adding values at front with $addToSet modifier使用 $addToSet 修饰符在前面添加值
【发布时间】:2015-09-25 10:21:56
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 你为什么要那个?
  • 因为,这是某种“订单”信息,开头需要新元素。
  • 数组是否只需要包含唯一项?
  • @snozza,是的,你是对的!

标签: mongodb mongoose mongodb-query


【解决方案1】:

$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 ] }

【讨论】:

  • 谢谢,这解决了我尝试推送单个值时的问题。一组值呢?我猜 $ne 在这种情况下不会起作用。有什么想法吗?
  • @Rana Slould 知道你会问。您始终可以尝试使用 $not 包裹的 $all,但当然,如果“所有”数组值不存在,那将只允许 $push 操作。因此,如果这无法更新任何内容,那么您需要退回到单独推送每个数组元素。这是能够在所需位置将$push 发送到您的阵列的权衡。
  • @Rana 不知道你为什么不接受这个。这里的推理很清楚。对于相关讨论,您可以查看我最近的另一个答案here,它解释了许多相同的概念。
  • 如果你维护一个最后点击项目的数组,并且你只想更新数组中已经存在的项目的当前位置?
猜你喜欢
  • 1970-01-01
  • 2019-07-11
  • 2018-01-26
  • 2021-11-18
  • 1970-01-01
  • 2022-06-28
  • 1970-01-01
  • 2013-01-05
  • 1970-01-01
相关资源
最近更新 更多