查询所有数组项大于指定条件的位置

时间:2016-11-01 13:22:46

标签: arrays node.js mongodb mongoose mongodb-query

我有一个包含日期数组的模型。我使用 $gte 运算符作为查询集合的条件,其中日期数组中的所有元素都是 $gte 给定日期

例如我有这个文件:

{ dates: [
    ISODate("2016-10-24T22:00:00.000+0000"), 
    ISODate("2017-01-16T23:00:00.000+0000")]
} 

当我运行此查询{dates: {$gte: new Date()}}时,它会为我提供整个文档。但我希望得到一个结果,其中每个数组项都匹配我的查询,而不只是一个。

2 个答案:

答案 0 :(得分:3)

您可以使用$not和比较条件的反转来执行此操作:

db.test.find({dates: {$not: {$lt: new Date()}}})

所以这匹配文档,而不是dates元素的值小于当前时间的情况;换句话说,所有dates值都是> =当前时间。

答案 1 :(得分:2)

您还可以将聚合框架与 $redact 管道运算符结合使用,该运算符允许您使用 $cond 运算符来处理逻辑条件,使用特殊操作 $$KEEP 来“保留”逻辑条件为真的文档或 $$PRUNE 以“删除”文档所在的条件是假的。

此操作类似于具有 $project 管道,该管道选择集合中的字段并创建一个新字段,其中包含逻辑条件查询的结果,然后是后续的 $match ,但 $redact 使用效率更高的单个管道阶段。

至于逻辑条件,可以使用Set Operators,因为它们允许表达式对数组执行set操作,将数组视为集合。这些运算符中的这些运算符即 $allElementTrue $map 运算符可以用作逻辑条件表达式,因为它们的工作方式是数组中的所有元素实际上都是 $gte 指定的日期,那么这是一个真正的匹配,文档是“保留”的。否则它被“修剪”并丢弃。

考虑以下展示上述概念的例子:

填充测试集

db.test.insert([
    { dates: [
        ISODate("2016-10-24T22:00:00.000+0000"), 
        ISODate("2017-01-16T23:00:00.000+0000")]
    } ,
    { dates: [
        ISODate("2017-01-03T22:00:00.000+0000"), 
        ISODate("2017-01-16T23:00:00.000+0000")]
    } 
])
使用 $redact

$setEquals

db.test.aggregate([
    { "$match": { "dates": { "$gte": new Date() } } },
    {
        "$redact": {
            "$cond": [
                { 
                    "$allElementsTrue": {
                        "$map": {
                            "input": "$dates",
                            "as": "date",
                            "in": { "$gte": [ "$$date", new Date() ] }
                        }
                    } 
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

示例输出

{
    "_id" : ObjectId("581899dda450d81cb7d87d3a"),
    "dates" : [ 
        ISODate("2017-01-03T22:00:00.000Z"), 
        ISODate("2017-01-16T23:00:00.000Z")
    ]
}

另一种不那么优雅的方法是使用 $where (作为最后的手段)使用 Array.prototype.every() 方法:

db.test.find({ "$where": function(){ 
    return this.dates.every(function(date) {
        return date >= new Date();
    })}
})