无法按日期过滤MongoDB中Collection的数组字段

时间:2014-08-14 08:26:51

标签: mongodb aggregation-framework

我正在努力研究MongoDb以获得理想的结果。

我的收藏品如下:

{
 _id: ...
 place: 1
 city: 6
 user: 306
 createDate: 2014-08-10 12:20:21,
 lastUpdate: 2014-08-14 10:11:01,
 data: [
   {
     customId4: 4,
     entryDate: 2014-07-12 12:01:11,
     exitDate: 2014-07-12 13:12:12
   },
   {
     customId4: 4,
     entryDate: 2014-07-14 00:00:01,
   },
   {
     customId4: 5,
     entryDate: 2014-07-15 11:01:11,
     exitDate: 2014-07-15 11:05:15
   },
   {
     customId4: 5,
     entryDate: 2014-07-22 21:01:11,
     exitDate: 2014-07-22 21:23:22
   },
   {
     customId4: 4,
     entryDate: 2014-07-23 14:00:11,
   },
   {
     customId4: 4,
     entryDate: 2014-07-29 22:00:11,
     exitDate: 2014-07-29 23:00:12
   },
   {
     customId4: 5,
     entryDate: 2014-08-12 12:01:11,
     exitDate: 2014-08-12 13:12:12
   },
 ]
}

所以我想要实现的是满足特定间隔要求且同时设置了entryDate和exitDate值的数组数据。

例如,如果我按间隔" 2014-07-23 00:00:00到2014-08-31 00:00:00"我希望结果如下:

{
  result: [
    {
       _id: {
           place: 1,
           user: 306
       },
       city: 6,
       place: 1,
       user: 306,
       data: [
          {
            customMap: 4,
            entryDate: 2014-07-22 21:01:11,
            exitDate: 2014-07-22 21:23:22
          },
          {
            customId4: ,
            entryDate: 2014-07-29 22:00:11,
            exitDate: 2014-07-29 23:00:12
          },
       ]
    }
  ],
  ok: 1
}

我的自定义mongodb查询看起来像(from,to和placeIds是正确配置的变量)

db.myColl.aggregate(
    { $match: { 
        'user': 1, 
        'data.entryDate': { $gte: from, $lte: to },
        'place': { $in: placeIds },
    }},
    { $unwind : "$data" },
    { $project: {
        'city': 1, 
        'place': 1,
        'user': 1,
        'lastUpdate': 1,
        'data.entryDate': 1,
        'data.exitDate': 1,
        'data.custom': 1,
        fromValid: { $gte: ["$'data.entryDate'", from]},
        toValid: { $lte: ["$'data.entryDate'", to]}}
    },
        { $group: {
        '_id': {'place': '$place', 'user': '$user'},
        'city': {'$first': '$city'},
        'place': {'$first': '$place'},
        'user': {'$first': '$user'},
        'data': { '$push': '$data'}
}}
)

但这并没有按照我想要的方式进行过滤,因为它会输出满足$ match操作数条件的每个文档,在$ project操作数内我无法定义条件(我不知道这是不是是如何在mongoDB中完成的)

提前致谢!

1 个答案:

答案 0 :(得分:1)

你走在正确的轨道上,但聚合“管道”可能会缺少的就是“|”在unix shell中的管道操作符可以将管道阶段“链接”在一起,就像链接命令一样。

所以实际上可以有第二个$match管道阶段为你做过滤:

db.myColl.aggregate([
    { "$match": { 
        "user": 1, 
        "data.entryDate": { "$gte": from, "$lte": to },
        "place": { "$in": "placeIds" },
    }},
    { "$unwind": "$data" },
    { "$match": { 
        "data.entryDate": { "$gte": from, "$lte": to },
    }},
    { "$group": {
        "_id": "$_id",
        "place": { "$first": "$place" },
        "city": { "$first": "$city" },
        "user": { "$first": "$user" },
        "data": { "$push": "$data" }
    }}
])

使用文档的实际_id作为分组键,假定您希望文档返回但只是使用过滤后的数组。

从MongoDB 2.6开始,只要匹配的数组元素是唯一的,您就可以使用$project $map 内执行相同的操作$setDifference**运营商:

db.myColl.aggregate([
    { "$match": { 
        "user": 1, 
        "data.entryDate": { "$gte": from, "$lte": to },
        "place": { "$in": "placeIds" },
    }},
    { "$project": {
        "place": 1,
        "city": 1,
        "user": 1,
        "data": {
           "$setDifference": [
               { "$map": {
                   "input": "$data",
                   "as": "el",
                   "in": {"$cond": [
                       { "$and": [
                           { "$gte": [ "$$el.entryDate", from ] },
                           { "$lte": [ "$$el.entryDate", to ] }
                       ]},
                       "$$el",
                       false
                   ]}
               }},
               [false]
           ]
        }
    }}
 ])

通过处理每个数组元素并评估它是否符合条件,这是相同的逻辑。如果是,则返回元素内容,否则返回false$setDifference会过滤掉所有false值,以便只保留匹配的值。