mongodb按数组中的条件元素排序

时间:2015-04-20 04:16:57

标签: mongodb sorting

我的需求看起来有点复杂:

我有一个集合,其中文档有一个名为startTimeArray的数组,存储了事件的timeStamps。如:

"startTimeArr" : [
    NumberLong("1425340800000"),
    NumberLong("1425513600000"),
    NumberLong("1426032000000"),
    NumberLong("1425427200000")
]

我希望查找结果按大于当前时间的最小时间戳排序(即时间尚未过去,最小值开启)。例如,如果当前时间戳以毫秒为单位1425513500000,参与此文档排序操作的时间戳为1425513600000,这是startTimeArr数组的最小标记,大于1425513500000

所以startTimeArr参与排序操作的元素应该是:

  1. 大于当前时间。
  2. 与#1
  3. 见面的最小人数

    我可以通过以下聚合来完成此操作:

     db.event.aggregate(
        {
          $match:{
             startTimeArr:{
                  $gt: 1425513500000 // presume the current time is 1425513500000
               }
          }
        },
        {
          $group:{
              _id:"$_id", minTimeStamp:{?????} // and I don't know what to write here.
          }
        },
        {
          $sort:{minTimeStamp:1}
        }
     )
    

    但是我无法弄清楚如何编写minTimeStamp计算。

    你能告诉我怎么做吗?或者,如果还有其他方法可以做到这一点,也将不胜感激。


    由于问题有点过于复杂,我只会根据需要提供一些样本和输出。

    如果我们有3个这样的文件:

    {
      "_id" : ObjectId("537d98c20cf2264603faf0eb"),
      "name": "Let's go to LA",
      "fee": 500,
      "duration":2,
      "startTimeArr":[
        NumberLong("1425310000000"),
        NumberLong("1425320000000"),
        NumberLong("1425330000000"),
        NumberLong("1425380000000")
       ]
    }
    {
      "_id" : ObjectId("537caa7e0cf2264603faf0e9"),
      "name": "Let's go to NewYork",
      "fee": 800,
      "duration":3,
      "startTimeArr":[
        NumberLong("1425320000000"),
        NumberLong("1425330000000"),
        NumberLong("1425340000000"),
        NumberLong("1425350000000")
       ]
    }
    {
      "_id" : ObjectId("537c5ec50cf2264603faf0e7"),
      "name": "Let's go to Washington",
      "fee": 700,
      "duration":2,
      "startTimeArr":[
        NumberLong("1425350000000"),
        NumberLong("1425360000000"),
        NumberLong("1425370000000"),
        NumberLong("1425390000000")
       ]
    }
    

    如果当前时间为1425300000000,则参与排序的时间为:

     LA         1425310000000
     NewYork    1425320000000
     Washington 1425350000000
    

    因此,这三个文档的查找结果将按顺序LA, NewYork, Washington

    排序

    但是当时间继续前进并且当前时间为142534000000时,参与排序程序的时间将是:

    LA         1425380000000
    NewYork    1425350000000
    Washington 1425350000000
    

    因此,预期查找结果将按顺序NewYork, Washington, LA排序。

2 个答案:

答案 0 :(得分:1)

看起来您缺少的聚合运算符是$min,可以在聚合中的$group步骤中使用。在分组之前,您还需要$unwind开始时间数组:

var timeNow = 1425300000000;  // set the time cut off for testing

db.event.aggregate(
    // Find documents with matching events
    // Note: could take advantage of an index to limit results
    { $match: {
        startTimeArr:{
            $gt: timeNow
        }
    }},

    // Convert start time array to stream of docs
    { $unwind: "$startTimeArr" },

    // Limit to matching array elements
    { $match: {
        startTimeArr:{
            $gt: timeNow
        }
    }},

    // Find the minimum timestamp in each matching document
    { $group:{
        _id:"$_id",
        name: { $first: "$name" },
        minTimeStamp:{ $min: "$startTimeArr" }
    }},

    // Sort in order of minimum timestamp (ascending)
    { $sort: { "minTimeStamp" : 1 }}
)

示例输出:

{
  "result": [
    {
      "_id": ObjectId("537d98c20cf2264603faf0eb"),
      "name": "Let's go to LA",
      "minTimeStamp": NumberLong("1425310000000")
    },
    {
      "_id": ObjectId("537caa7e0cf2264603faf0e9"),
      "name": "Let's go to NewYork",
      "minTimeStamp": NumberLong("1425320000000")
    },
    {
      "_id": ObjectId("537c5ec50cf2264603faf0e7"),
      "name": "Let's go to Washington",
      "minTimeStamp": NumberLong("1425350000000")
    }
  ],
  "ok": 1
}

答案 1 :(得分:0)

您需要在$ group管道阶段使用$min聚合运算符,以便从startTimeArr数组值中获取最小时间戳。但是,您需要首先从过滤的输入文档中解构startTimeArr数组字段(使用$match运算符),以使用$unwind运算符为每个元素输出文档。在$group管道阶段,$first运算符使您可以恢复分组中的其他字段,以便稍后在项目中进行投影。因此,您的聚合管道将如下所示:

var date = new Date(),
    timestamp = +date;

db.event.aggregate([
    {
      "$match": {
         "startTimeArr": {
              "$gt": 1425300000000 // or use the timestamp variable to get the actual current timestamp
           }
      }
    },
    {
        "$unwind": "$startTimeArr"
    },
    {
      "$group": {
          "_id":"$_id", 
          "minTimeStamp": {
              "$min": "$startTimeArr"
          },
         "name": { "$first": "$name" },         
         "fee": { "$first": "$fee" },
         "duration": { "$first": "$duration" },
      }
    },
    {
        "$project": {
            "_id": 0,
            "name": 1,
            "minTimeStamp" : 1,
            "fee": 1,
            "duration": 1
        }
    },
    {
      "$sort": { "minTimeStamp": 1 }
    }
]);

<强>输出

 /* 0 */
{
    "result" : [ 
        {
            "minTimeStamp" : NumberLong(1425310000000),
            "name" : "Let's go to LA",
            "fee" : 500,
            "duration" : 2
        }, 
        {
            "minTimeStamp" : NumberLong(1425320000000),
            "name" : "Let's go to NewYork",
            "fee" : 800,
            "duration" : 3
        }, 
        {
            "minTimeStamp" : NumberLong(1425427200000),
            "name" : "Let's go to Washington",
            "fee" : 700,
            "duration" : 2
        }
    ],
    "ok" : 1
}