【问题标题】:Mongodb grouping data - mapReduce or aggregation?Mongodb分组数据-mapReduce还是聚合?
【发布时间】:2016-03-08 01:10:27
【问题描述】:

我有这样的文件:

{
    "_id" : ObjectId("565e906bc2209d91c4357b59"),
    "userEmail" : "abc@example.com",
    "subscription" : {
        "project1" : {
            "subscribed" : false
        },
        "project2" : {
            "subscribed" : true
        }
    }
}

{
    "_id" : ObjectId("565e906bc2209d91c4357b59"),
    "userEmail" : "mno@example.com",
    "subscription" : {
        "project1" : {
            "subscribed" : true
        },
        "project2" : {
            "subscribed" : true
        },
        "project3" : {
            "subscribed" : true
        }
    }
}

我想将subscribed 标志设置为true 的项目列表按用户分组。

例如,我期望的是:

abc@example.com - project2
mno@example.com - project1,project2,project3

我将有一个 cron 作业,它将订阅项目的相应详细信息发送到相应的邮件 ID。

我尝试过聚合,但聚合需要指定的键。在我的情况下,关键(即 Project1、Project2 等)是动态的。所以我在某处读到mapReduce 是正确的选择。但是我没有使用mapReduce的经验。

请帮助我解决这个问题,并通过更多解释让我理解如何处理这个问题。

【问题讨论】:

  • 也许$redact 可以帮助这里删除带有subscribed: false 的数组条目。
  • 如果您将 subscription 更改为数组 [{project:"project1", subscribed:true},...],则可以使用聚合框架轻松完成。否则,您必须使用 map-reduce 或一些客户端代码。

标签: mongodb mapreduce mongodb-query aggregation-framework


【解决方案1】:

根据您文档的当前结构,您需要使用mapReduce

db.subscription.mapReduce(
    function() {
        var project = [];
        for (key in this.subscription) {     
            if(Object.prototype.hasOwnProperty.call(this.subscription, key) && this.subscription[key]['subscribed']) 
            { project.push(key); } 
        } 
        emit(this.userEmail, project); 
    }, 
    function(key, values) {}, 
    { out: {'inline': 1 } }
)

返回:

{
        "results" : [
                {
                        "_id" : "abc@example.com",
                        "value" : [
                                "project2"
                        ]
                },
                {
                        "_id" : "mno@example.com",
                        "value" : [
                                "project1",
                                "project2",
                                "project3"
                        ]
                }
        ],
        "timeMillis" : 28,
        "counts" : {
                "input" : 2,
                "emit" : 2,
                "reduce" : 0,
                "output" : 2
        },
        "ok" : 1
}

您应该考虑更改文档结构。为此,您需要更新您的文档并使用"bulk" 操作将“订阅”更改为子文档数组,以获得最大效率。

var bulk = db.subscription.initializeOrderedBulkOp();
var count = 0;

db.subscription.find().forEach(function(doc) {
    var newSubscriptions = [];
    var subscription = doc.subscription;
    for (key in subscription) {     
            if(Object.prototype.hasOwnProperty.call(subscription, key)) 
                newSubscriptions.push( { 'project': key, 'subscribed': subscription[key]['subscribed'] });
    }
    bulk.find( { '_id': doc._id } ).updateOne( {
        '$set': { subscription': newSubscriptions }
    });
    count++;
    if (count % 500 === 0) {
        bulk.execute();
        db.subscription.initializeOrderedBulkOp();
    }
})

// clean up queues
if (count > 0) 
    bulk.execute();

执行此操作后,您的文档如下所示:

{
        "_id" : ObjectId("566041212729b51edb5871d4"),
        "userEmail" : "abc@example.com",
        "subscription" : [
                {
                        "project" : "project1",
                        "subscribed" : false
                },
                {
                        "project" : "project2",
                        "subscribed" : true
                }
        ]
}
{
        "_id" : ObjectId("565e906bc2209d91c4357b59"),
        "userEmail" : "mno@example.com",
        "subscription" : [
                {
                        "project" : "project1",
                        "subscribed" : true
                },
                {
                        "project" : "project2",
                        "subscribed" : true
                },
                {
                        "project" : "project3",
                        "subscribed" : true
                }
        ]
}

您可以使用.aggregate() 方法提供对聚合管道的访问:

db.subscription.aggregate([
    { '$project': {
        'userEmail': 1,
        'projects': {
            '$setDifference': [
                { '$map': {
                    'input': '$subscription', 
                    'as': 'srpt',
                    'in': { '$cond': [ '$$srpt.subscribed', '$$srpt.project', false ] } 
                }}, 
                [false]
            ]
        }
    }}
])

产量:

{
        "_id" : ObjectId("566041212729b51edb5871d4"),
        "userEmail" : "abc@example.com",
        "projects" : [
                "project2"
        ]
}
{
        "_id" : ObjectId("565e906bc2209d91c4357b59"),
        "userEmail" : "mno@example.com",
        "projects" : [
                "project1",
                "project2",
                "project3"
        ]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 2012-12-12
    • 2023-03-07
    相关资源
    最近更新 更多