MongoDB不是任何运算符

时间:2017-03-09 14:36:02

标签: mongodb mongoose mongodb-query aggregation-framework

mongodb-queries是否有类似非任何运算符的东西?

我有以下设置:

const ConstructQuestion = new Schema({
  answerType: String,
  text: String,
});

const Construct = new Schema({
  name: String,
  description: String,
  questions: [ConstructQuestion],
});

我还有一系列ConstructQuestion-ID。我现在尝试查询所有Constructs,其中包含_id不在我的ConstructQuestionID-Array中的任何问题。

换句话说:我希望所有包含_id 不是

的问题的构造

我可以使用$in来查询我想要的内容,_id查询所有构造,其中$nin 任何我的ID中的ID阵列。遗憾的是return this.construct.aggregate() .group({ _id: '$_id', questions_id: {$push: '$questions._id'}, construct: {$first: '$$ROOT'}, }) .unwind('$questions_id') .project({ construct: true, questions_id: true, check: { $cmp: [{ $size: {$setIntersection: ['$questions_id', usedQuestionIds]}, }, { $size: '$questions_id', }], }, }) .match({check: {$lt: 0}}) .exec() .then((results) => { const constructs = results.map((result) => { return result.construct; }); }) 不是答案,因为它与包含我的ID-Array中的所有问题的Construct不匹配

修改

非常精确:我正在寻找一个查询,它会在我的ID-Array

中为我提供至少包含一个ID为的问题的所有构造

结果:

Bertrand回答的帮助下,我的猫鼬解决方案现在看起来像这样:

group

改进:

在每次创建/更新后将questionIds作为数组直接添加到Construct后,我可以删除unwindreturn this.construct.aggregate({ $addFields: { check: { $cmp: [{ $size: {$setIntersection: ['$questionIds', usedQuestionIds]}, }, { $size: '$questionIds', }], }, }}) .match({check: {$lt: 0}}) .exec() .then((constructs) => { }); 阶段,因此我的代码现在看起来像这样:

$project

再次感谢Bertrand提示

改进2

同样,作为Bertrand的提示,$match$redact可以(在这种情况下)合并到return this.construct.aggregate({ $redact: { $cond: { if: { $lt: [ {$size: {$setIntersection: ['$questionIds', usedQuestionIds]}}, {$size: '$questionIds'} ] }, then: '$$KEEP', else: '$$PRUNE' } }}) .exec() .then((constructs) => { }); ,现在代码如下所示:

$size

改进3

我发现,我可以使用$setDifference和一个$setInsersection代替$size和两个return this.construct.aggregate({ $redact: { $cond: { if: { $gt: [{$size: {$setDifference: ['$questionIds', usedQuestionIds]}}, 0] }, then: "$$KEEP", else: "$$PRUNE" } }}) .exec() .then((constructs) => { }); 。代码现在看起来像这样:

package package_with_global is
   acc integer := 0;
end;

1 个答案:

答案 0 :(得分:1)

我认为您要找的是$not + $all

var input = ["58c20f6d95b16e3046ddd7e9", "58c20f6d95b16e3046ddd7eb", "58c20f6d95b16e3046ddd7ec"];

Data.find({
    "questions._id": {
        "$not": {
            "$all": input
        }
    }
}, function(err, res) {
    console.log(res);
});

它将返回所有未在输入input数组中指定所有问题的文档。如果选择了文档,则表示input数组中至少有一个问题不在文档中。

修改

如果您希望交集与questions文档完全匹配,您可以使用聚合中可用的setIntersection来确定两个数组共有的元素,并根据以下内容做出决定:

var array = [
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7e9"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7ea"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7eb"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7ec")
];

Const.aggregate([{
    $group: {
        "_id": "$_id",
        "questions_id": {
            $push: "$questions._id"
        },
        "document": { $first: "$$ROOT" }
    }
}, {
    $unwind: "$questions_id"
}, {
    $project: {
        document: 1,
        questions_id: 1,
        check: {
            $cmp: [{
                $size: {
                    $setIntersection: ["$questions_id", array]
                }
            }, {
                $size: "$questions_id"
            }]
        }
    }
}, {
    $match: {
        check: {
            "$lt": 0
        }
    }
}], function(err, res) {
    console.log(res);
});

第一个$group& $unwind负责构建questions._id数组。比较在下一个$project中执行,交叉点大小和questions._id的大小。

编辑2

您可以使用$redact简化$project + $match,以保证dcouments符合条件。使用新添加的questionIds字段,它更简单:

var array = [
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7e9"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7ea"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7eb"),
    new mongoose.mongo.ObjectId("58c20f6d95b16e3046ddd7ec")
];

Const.aggregate([{
    $redact: {
        $cond: {
            if: {
                $lt: [{
                    $size: {
                        $setIntersection: ["$questionIds", array]
                    }
                }, {
                    $size: "$questionIds"
                }]
            },
            then: "$$KEEP",
            else: "$$PRUNE"
        }
    }
}], function(err, res) {
    console.log(res);
});