通过在嵌入式对象中搜索键范围在mongodb中进行聚合

普拉蒂克·博特拉(Pratik Bothra)

Mongodb网站https://docs.mongodb.org/ecosystem/use-cases/pre-aggregated-reports/提供了此文档示例,可以在每日,每月级别上快速查找。

{
    _id: "201010/site-1/apache_pb.gif",
    metadata: {
        date: ISODate("2000-10-00T00:00:00Z"),
        site: "site-1",
        page: "/apache_pb.gif" },
    daily: {
        "1": {"sessions": 300, "bounces": 10}
        "2": {"sessions": 100, "bounces": 5},
        "3": {"sessions": 10},
        "4": {"sessions": 100, "bounces": 4},
        ... }
}

例如,检索特定日期的数据

   db.stats.monthly.find_one({ },{ 'daily.1': 1 'metadata': 1})

上面的模式对我来说以及所记录的大多数用例都非常有用,因为它本质上只是查找。

在少数情况下,我们可能会遇到一些自定义日期范围的问题,因此,如果用户搜索1月1日至1月3日=>我理想地希望得到此结果。

[{
        _id: "201010/site-1/apache_pb.gif",
        metadata: {
            date: ISODate("2000-10-00T00:00:00Z"),
            site: "site-1",
            page: "/apache_pb.gif" },
        result: {
           "sessions": 410, "bounces": 15 }
    }, {

}, {
        _id: "201010/site-1/apache_new.gif",
        metadata: {
            date: ISODate("2000-05-00T00:00:00Z"),
            site: "site-1",
            page: "/apache_new.gif" },
        result: {
           "sessions": 310, "bounces": 8 }
    }, {

}...
]

我知道我们需要在此处进行聚合,但是如果甚至有可能通过在嵌入式对象中提供键范围来进行聚合,则完全感到困惑。

我是否必须重组我的架构才能做到这一点?我真的很喜欢高效的查询,它们可以为我们的用例提供80-90%的服务。

布雷克七世

通常,您当前的数据存储格式在聚合框架或MongoDB查询中不能很好地发挥作用。核心问题是您的“每日”对象仅包含每个项目的命名键。这意味着,为了访问任何内容,需要为MongoDB提供指向该密钥的特定路径。比如"daily.1",就像你提到。

如前所述,聚合框架和常规MongoDB操作无法“遍历对象的键”,因此您将需要服务器端JavaScript才能从所有键中收集数据。

更符合MongoDB优化功能的一种方法是将“每日”数据存储在数组中:

{
    _id: "201010/site-1/apache_pb.gif",
    metadata: {
        date: ISODate("2000-10-00T00:00:00Z"),
        site: "site-1",
        page: "/apache_pb.gif"
    },
    daily: [
        { "day": 1, "sessions": 300, "bounces": 10},
        { "day": 2, "sessions": 100, "bounces": 5},
        { "day": 3, "sessions": 10},
        { "day": 4, "sessions": 100, "bounces": 4}
    ]
}

然后,您可以非常简单地对内容进行汇总:

db.colllection.aggregate([
    // Match relevant objects 
    { "$match": {
        "daily": { 
            "$elemMatch": { 
                "day": { "$gte": 1, "$lte": 3 } 
            }
        }
    }},

    // Unwind to denormalize array
    { "$unwind": "$daily" },

    // Filter the required results
    { "$match": {
        "daily.day": { "$gte": 1, "$lte": 3 }
    }},
    // Group data and sum totals
    { "$group": {
        "_id": "$_id",
        "metadata": { "$first": "$metadata" },
        "resultSessions": { "$sum": "$daily.sessions" },
        "resultBounces": { "$sum": "$daily.bounces" }
    }},

    // Optionally project to desired format
    { "$project": {
        "metadata": 1,
        "result": {
            "sessions": "$resultSessions",
            "bounces": "$resultBounces"
        }
    }}
])

或者更好的是,在展开之前对数组进行预过滤:

db.colllection.aggregate([
    { "$match": {
        "daily": { 
            "$elemMatch": { 
                "day": { "$gte": 1, "$lte": 3 } 
            }
        }
    }},
    { "$project": {
        "metadata": 1,
        "daily": {
            "$setDifference": [
                { "$map": {
                    "input": "$daily",
                    "as": "day",
                    "in": {
                        "$cond": [
                            { "$and": [
                                { "$gte": [ "$day.day", 1 ] },
                                { "$lte": [ "$day.day", 3 ] }
                            ]},
                            "$day",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }},
    { "$unwind": "$daily" },
    { "$group": {
        "_id": "$_id",
        "metadata": { "$first": "$metadata" },
        "resultSessions": { "$sum": "$daily.sessions" },
        "resultBounces": { "$sum": "$daily.bounces" }
    }},
    { "$project": {
        "metadata": 1,
        "result": {
            "sessions": "$resultSessions",
            "bounces": "$resultBounces"
        }
    }}
])

并且,请始终始终先$match相关对象,以减少正在处理的内容。

由于数据中的属性现在共享所有相同的路径,并且不依赖于外键,因此现在可以轻松地对其进行累积。

如果没有这种结构上的更改,则在服务器上进行聚合的唯一方法是使用mapReduce,它可以使用编码函数来迭代对象键:

db.collection.mapReduce(
    function() {
        var result = { "sessions": 0, "bounces": 0 };
        Object.keys(this.daily)
            .filter(function(key) {
               return ( key >= 1 && key <= 3 );
            })
            .forEach(function(key) {
                result.sessions += this.daily[key].sessions;
                result.bounces += this.daily[key].bounces;
            });
        emit(this._id,{ metadata: this.metadata, result: result });
   },
   function() {},  // won't be called for unique _id values
   { 
       "out": { "inline": 1 },
       "query": {
           "daily": { 
               "$elemMatch": { 
                   "day": { "$gte": 1, "$lte": 3 } 
               }
       }
    }},


   }
)

当然,在两种情况下,都根据您是否打算跨文档累积来调整分组。

当然,如果您根本没有真正在文档之间累积,那么在您自己的接收代码的客户端中进行相同类型的键遍历。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

通过MongoDB中的聚合框架将嵌入式对象作为文档检索

如何对MongoDB中的嵌入式对象进行排序

MongoDB嵌入式搜索

mongodb聚合中的项目嵌套嵌入式文档

嵌入式对象内部的MongoDB全文搜索

MongoDB模型未通过Schema嵌入式对象进行连接

基于范围查询MongoDB中嵌入式文档的数组

如何在mongodb中搜索嵌入式文档?

在MongoDB中搜索嵌入式数组(使用Java)

mongodb嵌入式文档搜索

更新mongodb中arrays对象的arrays的嵌入式文档

在数组mongodb中查找嵌入式对象

MongoDB聚合:通过动态字段路径从嵌入式文档添加字段

Hibernate HQL渴望在嵌入式对象中进行负载收集

通过嵌套对象键进行Mongodb聚合计数

如何在mongodb聚合中获取嵌入式集合的总和?

根据mongoDB聚合中的条件,项目嵌入式文档密钥值

MongoDB聚合:从嵌入式数组中删除某些元素(已编辑?)

在MongoDB聚合管道中,如何投影匹配的嵌入式数组的索引?

MongoDB 复杂聚合查询以求和嵌入式数组中的元素,有限制

MongoDB查询-聚合和嵌入式文档

嵌入式文档的$ divide元素-MongoDB聚合

查询MongoDB中的嵌入式文档

在mongodb中展开嵌入式文档

通过本地网络中的其他主机访问Django嵌入式服务器以在PyCharm中进行开发?

在Jackson中读取嵌入式对象

使用Java在MongoDB中的嵌入式文档中索引和搜索“数组”

如何在MongoDB中的嵌入式数组对象中插入json对象?

在嵌入式数组golang中检索范围时间mongodb之间的值

TOP 榜单

热门标签

归档