Mongo $减法日期在聚合$ match块中不起作用

时间:2015-03-09 08:56:12

标签: mongodb mongodb-query aggregation-framework nosql

我正在创建一个mongo聚合查询,它在$ match块中使用$ subtract运算符。如下面的代码所述。

此查询无效:

db.coll.aggregate(
[
    {
        $match: {
            timestamp: {
                $gte: {
                    $subtract: [new Date(), 24 * 60 * 60 * 1000]
                }
            }
        }
    },
    {
        $group: {
            _id: {
                timestamp: "$timestamp"
            },
            total: {
                $sum: 1
            }
        }
    },
    {
        $project: {
            _id: 0,
            timestamp: "$_id.timestamp",
            total: "$total",
        }
    },
    {
        $sort: {
            timestamp: -1
        }
    }
]
)

但是,第二个查询工作:

db.coll.aggregate(
[
    {
        $match: {
            timestamp: {
                $gte: new Date(new Date() - 24 * 60 * 60 * 1000)
            }
        }
    },
    {
        $group: {
            _id: {
                timestamp: "$timestamp"
            },
            total: {
                $sum: 1
            }
        }
    },
    {
        $project: {
            _id: 0,
            timestamp: "$_id.timestamp",
            total: "$total",
        }
    },
    {
        $sort: {
            timestamp: -1
        }
    }
]
)

我需要在$subtract块上使用$match,因此我无法使用上一个查询。

3 个答案:

答案 0 :(得分:2)

$subtract运算符是投影运算符。它仅在$project步骤中可用。所以你的选择是:

  • (不推荐)在$ match-step之前添加$ project-step,以转换以下匹配步骤的所有文档的timestamp字段。我不建议您这样做,因为需要对数据库上的每个文档执行此操作,并阻止数据库在时间戳字段上使用索引,因此可能会花费很多性能。
  • (推荐)在应用程序的shell /中生成要匹配的日期。生成一个新的Date()对象,将其存储在变量中,从中减去24小时,然后使用该变量执行第二次查询。

答案 1 :(得分:2)

嗯,你做不到这一点,你也不打算这样做。另一个有效的方面是你说“需要”这样做,但实际上你真的没有。

aggregation operators之外的所有常规pipeline operators实际上只在$project$group管道阶段有效。大部分在$project范围内,但肯定不在其他范围内。

$match管道与普通"query"操作实际上是相同的,因此唯一有效的内容是query operators

对于“需要”的情况,在将BSON表示发送到实际管道之前,需要在实际管道之外评估在聚合管道内提交的任何“值”,特别是在$match内。服务器

唯一的例外是在文档中定义变量的符号,特别是“{1}}中的”字段名称“,然后才真正在"$fieldname"$project中。所以这意味着“引用”文档的现有值,这是在任何类型的“查询”文档表达式中无法完成的事情。

如果您需要使用文档中其他字段的值,则首先使用$group进行处理,如下所示:

$project

出于任何其他目的,您真的想要评估管道“外部”的值。


上面回答了你问的问题,但这回答了你没有问的问题。

您的管道没有任何意义,因为单独对“时间戳”进行分组不太可能对任何内容进行分组,因为这些值的精确度为毫秒级,并且对于非常活跃的系统而言,最多可能不会超过几个

看起来你正在寻找按“天”分组的数学,你可以这样做:

db.collection.aggregate([
    { "$project": {
        "fieldMath": { "$subtract": [ "$fieldOne", "$fieldTwo" ] }
    }},
    { "$match": { "fieldMath": { "$gt": 2 } }}
])

将您的时间戳值“舍入”到一天,并且比其他方式更有可能“聚合”某些东西。

或者你可以使用"date aggregation operators"对复合键做同样的事情。


因此,如果您想“查询”,那么它会在外部进行评估。如果要处理“文档内”的值,则必须在db.collection.aggregate([ { "$group": { "_id": { "$subtract": [ { "$subtract": [ "$timestamp", new Date(0) ] }, { "$mod": [ { "$subtract": [ "$timestamp", new Date(0) ] }, 1000 * 60 * 60 * 24 ]} ] }, "total": { "$sum": "$total" } }} ]) $project管道阶段执行此操作。

答案 2 :(得分:0)

从mongodb 3.6开始,您可以在$ match阶段通过$ expr使用$ subtract。这是文档:https://docs.mongodb.com/manual/reference/operator/query/expr/

我可以通过$ expr和mongodb 4.2中一个名为$$ NOW的新系统变量来获取类似您所描述的查询。这是我的查询,它向我提供最近4个小时内创建的订单:

create table user_chapters( 
  chapter_id serial primary key, 
  chapter_content varchar)