我有一个类似于此
结构的文档{
"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
时才可用
答案 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”!