【问题标题】:Is it possible to retrieve a 'time span' from a MongoDB query, using the timestamp within an ObjectId?是否可以使用 ObjectId 中的时间戳从 MongoDB 查询中检索“时间跨度”?
【发布时间】:2014-08-14 14:20:00
【问题描述】:

我们有一个基本的查询管理工具,用于在我们的管理套件中跟踪一些网站查询,并且我们使用查询集合中每个文档的 ObjectId 来按添加日期对查询进行排序。

{
    "_id" :      ObjectId("53a007db144ff47be1000003"),
    "comments" : "This is a test enquiry. Please ignore. We'll delete it shortly.",
    "customer" : {
        "name" :      "Test Enquiry",
        "email" :     "test@test.com",
        "telephone" : "07890123456",
        "mobile" :    "07890123456",
        "quote" :     false,
        "valuation" : false
    },
    "site" : [],
    "test" : true,
    "updates" : [ 
        {
            "_id" :          ObjectId("53a007db144ff47be1000001"),
            "status" :       "New",
            "status_id" :    ObjectId("537de7c3a5e6e668ffc2335c"),
            "status_index" : 100,
            "substatus" :    "New Web Enquiry",
            "substatus_id" : ObjectId("5396bb9fa5e6e668ffc23388"),
            "notes" :        "New enquiry received from website.",
        }, 
        {
            "_id" :          ObjectId("53a80c977d299cfe91bacf81"),
            "status" :       "New",
            "status_id" :    ObjectId("537de7c3a5e6e668ffc2335c"),
            "status_index" : 100,
            "substatus" :    "Attempted Contact",
            "substatus_id" : ObjectId("53a80e06a5e6e668ffc2339e"),
            "notes" :        "In this test, we pretend that we've not managed to get hold of the customer on the first attempt.",
        }, 
        {
            "_id" :          ObjectId("53a80e539b966b8da5c40c36"),
            "status" :       "Approved",
            "status_id" :    ObjectId("52e77a49d85e95f00ebf6c72"),
            "status_index" : 200,
            "substatus" :    "Enquiry Confirmed",
            "substatus_id" : ObjectId("53901f1ba5e6e668ffc23372"),
            "notes" :        "In this test, we pretend that we've got hold of the customer after failing to contact them on the first attempt.",
        }
    ]
}

在每个查询中都有一个更新对象数组,这些对象也有一个 ObjectId 作为它们的主要身份字段。我们使用$unwind$group 聚合来提取第一个和最新的更新,以及更新的计数,确保我们只在有多个更新的情况下进行查询(因为当更新时自动插入一个查询):

db.enquiries.aggregate([
    {
        $match: {
            "test": true
        }
    },
    {
        $unwind: "$updates"
    },
    {
        $group: {
            "_id": "$_id",
            "latest_update_id": {
                $last: "$updates._id"
            },
            "first_update_id": {
                $first: "$updates._id"
            },
            "update_count": {
                $sum: 1
            }
        }
    },
    {
        $match: {
            "update_count": {
                $gt: 1
            }
        }
    }
])

这会产生以下输出:

{
    "result" : [ 
        {
            "_id" : ObjectId("53a295ad122ea80200000005"),
            "latest_update_id" : ObjectId("53a80bdc7d299cfe91bacf7e"),
            "first_update_id" : ObjectId("53a295ad122ea80200000003"),
            "update_count" : 2
        }, 
        {
            "_id" : ObjectId("53a007db144ff47be1000003"),
            "latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
            "first_update_id" : ObjectId("53a007db144ff47be1000001"),
            "update_count" : 3
        }
    ],
    "ok" : 1
}

然后将其传递给我们的代码(在本例中为 node.js),我们对其执行一些操作,然后在仪表板上显示一些信息。

理想情况下,我想在查询中添加另一个$group 管道聚合,这将从latest_update_id 的时间戳中减去first_update_id 的时间戳,得到一个时间跨度,然后我们可以使用$avg开。

谁能告诉我这是否可能? (谢谢!)

【问题讨论】:

  • 目前不适用于聚合框架。如您所知,您可以从 ObjectId 获取时间戳。因此,要么在客户端代码中执行此操作,要么投降给可以调用该函数以获取时间戳值的 mapReduce。取决于您的输出数据大小。我对聚合管道中的更多“数学”函数投一票。
  • @NeilLunn 使用 MapReduce 不会是世界末日(正如您可能收集到的那样,这主要是一个统计练习),因为我们可以将其安排为一次性 - 它主要是一个问题,看看它是否可能在聚合框架内。如果能够在查询中访问 ObjectId 字段的属性/功能,那就太好了。
  • @abitgone 我很好奇,你会按 _id : null 分组以获得所有文档的平均值吗?
  • @ChristianP:当然,但我需要否定没有 2 个或更多更新的查询。此外,所有文档的平均值是否会以正确保留时间戳的方式作用于 ObjectId 字段?
  • @abitgone 查看我的编辑。我想它会回答你的问题。

标签: mongodb mapreduce timestamp aggregation-framework


【解决方案1】:

正如 Neil 已经指出的那样,您无法从聚合框架中的 ObjectId 获取时间戳。

你说速度不重要,所以使用 MapReduce 可以得到你想要的:

var map  = function() {
    if (this.updates.length > 1) {

        var first = this.updates[0];
        var last = this.updates[this.updates.length - 1];

        var diff = last._id.getTimestamp() - first._id.getTimestamp();

        var val = {
            latest_update_id : last._id,
            first_update_id : first._id,
            update_count : this.updates.length,
            diff: diff 
        }

        emit(this._id, val);
    } 
};

var reduce = function() { };

db.runCommand(
    {
        mapReduce: "enquiries",
        map: map,
        reduce: reduce,
        out: "mrresults",
        query: { test : true}
    }
);

这是结果:

{
    "_id" : ObjectId("53a007db144ff47be1000003"),
    "value" : {
        "latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
        "first_update_id" : ObjectId("53a007db144ff47be1000001"),
        "update_count" : 3,
        "diff" : 525944000
    }
}

编辑:

如果您想获得所有文档的平均差异,您可以这样做:

var map  = function() {
    if (this.updates.length > 1) {

        var first = this.updates[0];
        var last = this.updates[this.updates.length - 1];

        var diff = last._id.getTimestamp() - first._id.getTimestamp();

        emit("1", {diff : diff});
    } 
};

var reduce = function(key, values) {
    var reducedVal = { count: 0, sum: 0 };

    for (var idx = 0; idx < values.length; idx++) {
        reducedVal.count += 1;
        reducedVal.sum += values[idx].diff;
    }

    return reducedVal;
};

var finalize = function (key, reducedVal) {
    reducedVal.avg = reducedVal.sum/reducedVal.count;

    return reducedVal;
};

db.runCommand(
    {
        mapReduce: "y",
        map: map,
        reduce: reduce,
        finalize : finalize,
        out: "mrtest",
        query: { test : true}
    }
);

示例输出:

> db.mrtest.find().pretty()
{
    "_id" : "1",
    "value" : {
        "count" : 2,
        "sum" : 1051888000,
        "avg" : 525944000
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 2020-06-25
    • 1970-01-01
    相关资源
    最近更新 更多