如何伪造MongoDB中的多项$ pop?

时间:2015-03-18 12:55:40

标签: javascript mongodb mongodb-query

来自Mongo的新人的快速提问。

我有一组文件(简化)如下:

{"_id":<objectID>, "name":"fakeName", "seeds":[1231,2341,0842,1341,3451, ...]}

我真正需要的是一个$ pop,从我的种子列表中弹出2或3个项目,但$ pop目前仅适用于one项目,所以我试图寻找另一种方式来完成同样的事情。

我看到的第一件事是使用空的&#34;每个&#34;来执行$ push / $ each / $ slice,如:

update: { $push: { order: { $each: [ ], $slice: ?}}}

这里的问题是我不知道我想要新切片的确切时间(我希望它是&#34;当前大小 - 我弹出的种子数量#34;)。如果$ slice修饰符像$ slice投影一样工作,这很容易,我可以做$ slice:[#of seeds,]但是它没有这样做,所以它不起作用。

我接下来要看的是获取数组的一侧并将其用作$ slice的输入,如:

update: { $push: { seeds: { $each: [ ], $slice: {$subtract: [{$size:"$seeds"}, <number of seeds to pop>]}}}}

但是Mongo告诉我&$ 34; $ slice的值必须是数值而不是Object&#34;,所以显然$ subtract的结果是Object而不是数字。

然后我试着看看能否&#34;删除&#34;数组中的项目基于带有$ limit的空查询,但显然限制会在管道中稍后应用,因此我无法设法使其工作。

还有其他建议吗,或者我运气不好,需要回到绘图板上?

非常感谢您的帮助/输入。

1 个答案:

答案 0 :(得分:1)

MongoDB目前没有任何方法可以在单个更新语句中引用现有的字段值。唯一的例外是$inc$mul之类的运算符,它们可以对当前值起作用并根据设定规则对其进行更改。

这部分是由于&#34;措辞&#34;的兼容性。对多个文件采取行动的行动,无论是否属实。但你要求的是一些&#34;变量&#34;允许&#34;长度&#34;要测试和使用的数组,如输入参数&#34;另一种方法。这不受支持。

所以你能做的最好就是阅读文档内容并在代码中测试数组的长度,然后在你第一次推测时执行$slice更新,或者你也可以使用聚合框架来计算&#34;可能的长度&#34;数组(假设有很多重复),然后工作&#34;多&#34;更新符合条件的文档,当然假设您要对多个文档执行此操作。

第一种形式:

var bulk = db.collection.initializeOrderedBulkOp();
var count = 0;

db.collection.find().forEach(function(doc) {
    if ( doc.order.length > 2 ) {
        bulk.find({ "_id": doc._id })
            .updateOne({ 
                "$push": {
                    "order": { "$each": [], "$slice": doc.order.length - 2 }
                }
            });
        count ++;
    }

    if ( (count % 1000) == 0 && ( count > 1 ) ) {
        bulk.execute();
        bulk = db.collection.initializeOrderedBulkOp();
    }
});

if ( count % 1000 != 0 )
    bulk = db.collection.initializeOrderedBulkOp();

第二种形式:

var bulk = db.collection.initializeOrderedBulkOp();
var count = 0;

db.collection.aggregate([
    { "$group": { "_id": { "$size": "$order" } }},
    { "$match": { "$_id": { "$gt": 2 } }}
]).forEach(function(doc) {

   bulk.find({ "order": { "$size": doc._id } })
       .update(
            "$push": {
                "order": { "$each": [], "$slice": doc._id - 2 }
            }
        });
        count ++;

   if ( count % 1000 == 0 ) {
       bulk.execute();
       bulk = db.collection.initializeOrderedBulkOp();
   }
});

if ( count % 1000 != 0 )
    bulk = db.collection.initializeOrderedBulkOp();

注意到在这两种情况下都有一些逻辑要考虑数组的长度,以免“#34;空”&#34;它们,或产生不希望的$slice操作。

另一种可能的替代方法是在查询中使用$slice的投影形式来获取最后n个元素,然后$pull来自数组的匹配元素。当然,用于此类操作的标识符必须是&#34;唯一的&#34;,但它是确保唯一性的有效案例。

因此,无论您的情况如何,如果不事先了解要修改的文档的当前状态,则无法在单个更新语句中执行此操作。不同的列表虽然为您提供了接近&#34;模拟&#34;这虽然不在一个声明中。