繁体   English   中英

从文档数组中删除最旧的 N 个元素

[英]Remove oldest N elements from document array

我的 mongodb 中有一个文档,其中包含一个非常大的数组(大约 10k 个项目)。 我试图只保留数组中最新的 1k(因此删除前 9k 个元素)。 该文件看起来像这样:

    {
        "_id" : 'fakeid64',
        "Dropper" : [
            {
                "md5" : "fakemd5-1"
            },
            {
                "md5" : "fakemd5-2"
            },
            ...,
            {
                "md5": "fakemd5-10000"
            }
        ]
    }

我如何做到这一点?

此处要执行的正确操作实际上涉及使用$each$slice修饰符的$push运算符。 使用$push从数组中“删除”项目最初可能看起来违反直觉,但当您看到预期的操作时,实际用例就很清楚了。

db.collection.update(
  { "_id": "fakeid64" },
  { "$push": { "Dropper": { "$each": [], "$slice": -1000 } }
)

实际上,您可以将整个集合运行为:

db.collection.update(
  { },
  { "$push": { "Dropper": { "$each": [], "$slice": -1000 } },
  { "multi": true }
)

这里发生的事情是$each的修饰符在$push操作中接受一组要“添加”的项目,在这种情况下,我们将其留空,因为我们实际上并不想添加任何内容。 给定“负”值的$slice修饰符实际上是说在执行更新时保留数组中存在的“最后 n”个元素,这正是您要问的。

一般的“预期”情况是在添加新元素时使用$slice以“最大”给定长度“维护”数组,在这种情况下为 1000。所以你通常会与实际“添加”新元素一起使用像这样的项目:

db.collection.update(
  { "_id": "fakeid64" },
  { "$push": { "Dropper": { "$each": [{ "md5": "fakemd5-newEntry"}], "$slice": -1000 } }
)

这将附加$each提供的新项目,同时还从数组的“开始”中删除任何项目,其中给定的总长度大于 1000。


在其他地方错误地说明您将使用$pullAll并提供文档中已存在的数组内容列表,但该操作实际上是对数据库的两次请求。

误解是请求被发送为“一个”,但实际上不是,并且基本上被解释为较长的形式(正确使用.slice() ):

var md5s = db.collection.findOne({ "_id": "fakeid64" }).Dropper.slice(-1000);

db.collection.update(
  { "_id": "fakeid64" },
  { "$pullAll": { "Dropper": md5s } }
)

因此,您可以看到这不是很有效,并且实际上非常危险,当您考虑到文档中的数组状态“可能”在数组内容的“读取”和实际的“写入”操作之间“可能”发生变化时更新,因为它们分别发生。

这就是为什么 MongoDB 为$push$slice提供原子操作符的原因,正如演示的那样。 由于它不仅效率更高,而且还考虑了实际修改发生时正在修改的文档的实际“状态”。

假设您使用 python/pymongo 驱动程序,您可以使用$pullAll运算符:

yourcollection.update_one(
 {'_id': fakeid64}, 
 {'$pullAll': {'Dropper': yourcollection.find_one({'_id': 'fakeid64'})['Dropper'][:9000]}}
)

或在 mongo shell 中:

db.yourcollection.update(
  { _id: 'fakeid64'}, 
  {$pullAll: {'Dropper': db.yourcollection.findOne({'_id' : 'fakeid64'})['Dropper'].slice(0,9000)}}
)

(*) 话说如果你一开始就不允许你的文档增长这么多会更好

这只是查询的表示。 基本上你可以放松限制和跳过,然后使用光标 foreach 删除如下项目:

db.your_collection.aggregate([
    { $match : { _id : 'fakeid64' } },
    { $unwind : "$Dropper"},
    { $skip : 1000},
    { $limit : 9000}
]).forEach(function(doc){
    db.your_collection.update({ _id : doc._id}, { $pull : { Dropper : doc.Dropper} }); 
});

来自 mongo 文档

 db.students.update( { _id: 1 }, { $push: { scores: { $each: [ { attempt: 3, score: 7 }, { attempt: 4, score: 4 } ], $sort: { score: 1 }, $slice: -3 } } } )

以下更新使用 $push 运算符:

将 $each 修饰符附加到数组 2 个新元素, $sort 修饰符按升序 (1) 分数对元素进行排序,以及 $slice 修饰符保留有序数组的最后 3 个元素。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM