$ addToSet更新后,mongodb中的切片数组

时间:2014-01-30 19:58:07

标签: mongodb mongodb-query

我有一个类似于此

结构的文档
{
    "brand": "BMW",
    "models": ["320","545"]
}

模型必须是唯一的,我在添加新项目时使用以下查询

db.cars.update(
    {brand:'BMW'},
    {
        $addToSet: {
            models: '750'
        }
    },
    {upsert:true}
);

那会给出

{
    "brand": "BMW",
    "models": ["320","545","750"]
}

问题:

如何限制'模特'可以拥有的商品总数?假设我只想保留最后3个添加的模型。因此,如果我插入一个新模型'135',我最终会用

{
    "brand": "BMW",
    "models": ["545","750","135"]
}

我了解了$slice修饰符,但它似乎仅在使用$push而不是$addToSet时才可用

2 个答案:

答案 0 :(得分:6)

有趣的问题,因为这实际上是bug的主题,已经提出了一个实际的request来添加。至于请求,我不会屏住呼吸来添加这种行为。

错误在于您实际上可以发出以下内容:

db.cars.update(
    {brand:'BMW'},
    { $addToSet: { models: { $each: ['200'], $slice: 3 } } },
    {upsert: true}
)

和addToSet只会在忽略修饰符或警告的情况下工作。但是当然你现在在阵列中有四个元素。

CTO的评论同样指出了冲突。

因此,我唯一能想到的是首先在find中使用$elemMatch测试数组中的元素,如果文档都存在且$ elemMatch find不为true,则使用常规$进行更新推和切片。否则插入新文件或不管它

db.cars.update(
   {brand:'BMW'},
   {$push: { models: { $each: ['750'], $slice: -3 } }}
)

这将导致两个以上的操作通过电线。交替下拉文档并自己操作数组,这将是另一个操作。无论哪种方式,你都会失去upsert魔法并且必须手动完成。

答案 1 :(得分:0)

有同样的问题。这是我所做的解决方法(显然,批量操作的效率更高,但也可以单操作):

基本上,您添加虚拟空$ push运算符并在其上使用$ slice。但是由于$ addToSet不能保证顺序,因此我们还需要在其上添加$ sort。

据我了解,您需要按其插入顺序输入模型名称。因此,您需要为其添加时间戳(我们可以使用纪元)。因此,我们将模型转换为对象:{name:'750',ts:1552428379}

bulk = [
  {
    updateOne: {
      filter: { brand: 'BMW' },
      update: {
        $addToSet: {
          models: { name: '750', ts: 1552428379 }
        }
      },
      upsert: true
    }
  },
  {
    updateOne: {
      filter: { brand: 'BMW' },
      update: {
        $push: {
          models: {
            $each: [],
            $sort: { ts: 1 },
            $slice: -3
          }
        }
      }
    }
  },
]
db. cars.bulkWrite(bulk)

仅供参考,您似乎无法在同一操作中使用$ addToSet和$ push。这就是为什么我们使用2个更新。

重要提示:请确保您在批量操作中未使用“ ordered:false”!