从Array找到最近的比赛

时间:2017-06-28 12:49:10

标签: mongodb mongodb-query aggregation-framework

我正在创建一个"模拟程序"我在MongoDB中保存了大约700个移动对象的位置。我保存集合中的所有位置,每个文档都是一个"移动对象"。至于好。

现在我的问题: 我想阅读700" Objects"中的所有位置。一个查询但位置的时间戳是可变的......

示例文档:

{
    "_id" : ObjectId("59522479707c4fbf16b2f91b"),
    "PuckId" : 0,
    "Positions" : [ 
        {
            "time" : 0,
            "x_pos" : 0,
            "y_pos" : 0,
            "state" : 1
        }, 
        {
            "time" : 10,
            "x_pos" : 5,
            "y_pos" : 5,
            "state" : 1
        }, 
        {
            "time" : 20,
            "x_pos" : 0,
            "y_pos" : 20,
            "state" : 1
        }, 
        {
            "time" : 25,
            "x_pos" : 5,
            "y_pos" : 10,
            "state" : 1
        }
    ]
}
{
    "_id" : ObjectId("59522660707c4fbf16b38701"),
    "PuckId" : 1,
    "Positions" : [ 
        {
            "time" : 1,
            "x_pos" : 1,
            "y_pos" : 0,
            "state" : 1
        }, 
        {
            "time" : 5,
            "x_pos" : 5,
            "y_pos" : 10,
            "state" : 1
        }, 
        {
            "time" : 8,
            "x_pos" : 0,
            "y_pos" : 2,
            "state" : 1
        }, 
        {
            "time" : 22,
            "x_pos" : 5,
            "y_pos" : 0,
            "state" : 1
        }
    ]
}

这是我到目前为止所获得的: (对于此示例,我试图在Timesamp 10中查询对象的位置)

db.getCollection('LogFileV2').aggregate([
    {
        $project: {
            Positions:{
                $filter: {
                    input: "$Positions",      //{$slice: ["$Positions",1]}, *see below
                    as: "pos",

                    cond: { 
                        $or: [{
                            $lt: ["$$pos.time", 10+1] //at Timestamp 10
                        }]
                    }
                }
            }
        }
    }
]);
  • 如果我使用`$ slice'在这个位置它只返回第一个位置。但我只需要最新的位置,这取决于我输入的时间戳(此处为10)。

那么我需要做些什么才能得到这个结果:

/* 1 */
{
    "_id" : ObjectId("59522479707c4fbf16b2f91b"),
    "Positions" : [ 
        {
            "time" : 10,
            "x_pos" : 5,
            "y_pos" : 5,
            "state" : 1
        }
    ]
}
/* 2 */
{
    "_id" : ObjectId("59522660707c4fbf16b38701"),
    "Positions" : [ 
        {
            "time" : 8,
            "x_pos" : 0,
            "y_pos" : 2,
            "state" : 1
        }
    ]
}

1 个答案:

答案 0 :(得分:0)

对于最近的我会使用这样的东西:

db.getCollection('LogFileV2').aggregate([
  { "$match": { "Positions.time": { "$lte": 10 } } },
  { "$project": {
    "Positions": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$Positions",
            "as": "pos",
            "cond": { "$lte": [ "$$pos.time", 10 ]  }
          }
        },
        "as": "pos",
        "in": {
          "time": "$$pos.time",
          "x_pos": "$$pos.x_pos",
          "y_pos": "$$pos.y_pos",
          "state": "$$pos.state",
          "diff": { "$subtract": [ 10, "$$pos.time" ] }
        }
      }
    }
  }},
  { "$unwind": "$Positions" },
  { "$sort": { "_id": 1, "Positions.diff": 1 } },
  { "$group": {
    "_id": "$_id",
    "Positions": { "$first": "$Positions" }
  }},
  { "$sort": { "_id": 1 } }
])

这给了你:

/* 1 */
{
    "_id" : ObjectId("59522479707c4fbf16b2f91b"),
    "Positions" : {
        "time" : 10.0,
        "x_pos" : 5.0,
        "y_pos" : 5.0,
        "state" : 1.0,
        "diff" : 0.0
    }
}

/* 2 */
{
    "_id" : ObjectId("59522660707c4fbf16b38701"),
    "Positions" : {
        "time" : 8.0,
        "x_pos" : 0.0,
        "y_pos" : 2.0,
        "state" : 1.0,
        "diff" : 2.0
    }
}

因此除了查询以消除不符合条件的文件之外,基本的改变是使用$map以获得"差异"来自查询的价值。

然后我们基本上想要$sort数组结果,并且只选择差异最小的那个。这是通过$first管道阶段{/ 3}}获得的。

或者,您可以再次针对$group值在$let$filter内定义数组。在支持的情况下,这会更快:

db.getCollection('LogFileV2').aggregate([
  { "$match": { "Positions.time": { "$lte": 10 } } },
  { "$project": {
    "Positions": {
      "$let": {
        "vars": {
          "filtered": {
            "$map": {
              "input": {
                "$filter": {
                  "input": "$Positions",
                  "as": "pos",
                  "cond": { "$lte": [ "$$pos.time", 10 ]  }
                }
              },
              "as": "pos",
              "in": {
                "time": "$$pos.time",
                "x_pos": "$$pos.x_pos",
                "y_pos": "$$pos.y_pos",
                "state": "$$pos.state",
                "diff": { "$subtract": [ 10, "$$pos.time" ] }
              }
            }
          }
        },
        "in": {
          "$arrayElemAt": [
            { "$filter": {
              "input": "$$filtered",
              "as": "f",
              "cond": { "$eq": [ "$$f.diff", { "$min": "$$filtered.diff" } ] }
            }},
            0
          ]
        }   
      }
    }
  }}
])