在聚合中使用动态值

时间:2017-10-11 21:30:47

标签: mongodb mongodb-query aggregation-framework

我有这样的文档结构:

{
    "_id" : ObjectId("59d7cd63dc2c91e740afcdb"),
    "dateJoined": ISODate("2014-12-28T16:37:17.984Z"),
    "dateActivated": ISODate("2015-02-28T16:37:17.984Z"), 
    "enrolled" : [
        { "month":-10, "enrolled":'00'},
        { "month":-9, "enrolled":'00'},
        { "month":-8, "enrolled":'01'},
        //other months
        { "month":8, "enrolled":'11'},
        { "month":9, "enrolled":'11'},
        { "month":10, "enrolled":'00'}
    ]
}

"一个月"注册中的值相对于dateJoined,范围从预先填充的-X到+ X.

我想计算一下注册价值为' 01'的文件数量。对于满足条件的每个子文档 - 例如"激活前5个月和激活后2个月#34;所有子文档项必须与条件匹配才能计为1. [是的,可以在激活之前注册:)]

由于月份值不是基于dateActivated,我应该能够为每个文档动态计算它。

我正在尝试使用MongoDB聚合框架,但不确定如何动态。

db.getCollection("enrollments").aggregate(
    { $match:{ //matching condition }},
    { $project: {
            enrollments: {
                $filter: {
                    input: "$enrolled",
                    as: "enrollment",
                    cond: {
                        $eq: ['$$enrolled.enroll', '01']
                        //how can I check for month value here?
                    }
                }
            }
    }}
)

2 个答案:

答案 0 :(得分:1)

这里的一般问题是要考虑"month"值的范围,而不是"大于" -5个月"之前"和"小于" +2个月""记录在"enrolled"数组条目中。

问题在于,由于这些值基于"dateJoined",因此需要根据"dateJoined""dateActivated"之间的正确间隔进行调整。这有效地表达了表达式:

monthsDiff = (yearActivated - yearJoined)*12 + (monthActivated - monthJoined)

where month >= ( startRange + monthsDiff ) and month <= ( endRange + monthsDiff )
and enrolled = "01"

或逻辑上表达&#34;表达范围之间的月份由加入和激活之间的月数差异调整&#34;

正如评论中所述,您需要首先将这些日期值存储为BSON Date,而不是将其显示为&#34;字符串&#34;值。完成后,您可以应用以下聚合来计算与提供日期的差异,并在计算之前相应地从数组中过滤调整后的范围:

var rangeStart = -5,
    rangeEnd = 2;

db.getCollection('enrollments').aggregate([
  { "$project": {
    "enrollments": {
      "$size": {
        "$filter": {
          "input": "$enrolled",
          "as": "e",
          "cond": {
            "$let": {
              "vars": {
                "monthsDiff": {
                  "$add": [
                    { "$multiply": [
                      { "$subtract": [
                        { "$year": "$dateActivated" },
                        { "$year": "$dateJoined" }
                      ]},
                      12
                    }},
                    { "$subtract": [
                      { "$month": "$dateActivated" },
                      { "$month": "$dateJoined" }
                    ]}
                  ]
                }
              },
              "in": {
                "$and": [
                  { "$gte": [ { "$add": [ rangeStart, "$$monthsDiff" ] }, "$$e.month" ] },
                  { "$lte": [ { "$add": [ rangeEnd, "$$monthsDiff" ] }, "$$e.month" ] },
                  { "$eq": [ "$$e.enrolled", "01" ] }
                ]
              }
            }
          } 
        }
      }
    }
  }}
])

因此,这会对您尝试的数组应用相同的$filter,但现在也要考虑过滤的月份范围内的调整值。

为了使这更容易阅读,我们应用$let,它允许计算在变量中实现的$$monthsDiff获得的公共值。以下是应用最初解释的表达式的位置,使用$year$month从存储的日期中提取这些数值。

使用其他数学运算符$add$subtract$multiply,您可以计算月份差异,也可以稍后应用于调整&#34;范围&#34;具有$gte$lte的逻辑条件中的值。

最后,因为$filter只发出一个只包含与条件匹配的条目的数组,以便计算&#34;计数&#34;我们应用$size返回&#34;过滤&#34;的长度。数组,这是&#34; count&#34;比赛。

根据您的预期目的,整个表达式也可以在$sum作为$group累加器的参数中提供,如果那时确实是意图。

答案 1 :(得分:1)

如果您存储的是天数而不是几个月,则可以尝试以下聚合。

天差异以计算dateActivateddateJoined之间的天数,以抵消相对于dateActivated的入场天数。

daysdiff与以下值进行比较。

enrollmentdateActivated

之后的-120-0天

enrollmentdateActivated

之前的0-150天

$or上述条件&amp;值$and的{​​{1}}。

enrolled