Mongo查询按不同计数排序

时间:2016-03-10 23:29:54

标签: mongodb subquery mongodb-query aggregation-framework

我有两个字段'company'和'url'。我想按照不同的“公司”发生次数对其进行排序,然后显示与该特定公司相对应的三个“网址”。数据存储如下:

{
    "_id" : ObjectId("56c4f73664af6f7305f3670f"),
    "title" : "Full Stack Software Developer",
    "url" : "http://www.indeed.com/cmp/Upside-Commerce,-Inc./jobs/Full-Stack-Software-Developer-6e93e36ea5d0e57e?sjdu=QwrRXKrqZ3CNX5W-O9jEvRQls7y2xdBHzhqWkvhd5FFfs8wS9wesfMWXjNNFaUXen2pO-kyc_Qbr7-_3Gf40AvyEQT3jn6IRxIwvw9-aFy8",
    "company" : "Upside Commerce, Inc."
}

以下查询计算不同公司的数量。

db.Books.aggregate({$group : { _id : '$company', count : {$sum : 1}}})

以下是输出:

{ "_id" : "Microsoft", "count" : 14 }
{ "_id" : "Tableau", "count" : 64 }
{ "_id" : "Amazon", "count" : 64 }
{ "_id" : "Dropbox", "count" : 64 }
{ "_id" : "Amazon Corporate LLC", "count" : 64 }
{ "_id" : "Electronic Arts", "count" : 64 }
{ "_id" : "CDK Global", "count" : 65 }
{ "_id" : "IDC Technologies", "count" : 64 }
{ "_id" : "Concur", "count" : 64 }
{ "_id" : "Microsoft", "count" : 14 }
{ "_id" : "Tableau", "count" : 64 }
{ "_id" : "Amazon", "count" : 64 }
{ "_id" : "Dropbox", "count" : 64 }
{ "_id" : "Amazon Corporate LLC", "count" : 64 }
{ "_id" : "Electronic Arts", "count" : 64 }
{ "_id" : "CDK Global", "count" : 65 }
{ "_id" : "IDC Technologies", "count" : 64 }
{ "_id" : "Concur", "count" : 64 }

然而,我希望它按不同公司的数量排序(将其限制为前10名最高的公司),然后显示对应于不同公司的三个网址(如果不同公司的计数至少为三)。类似的东西:

{for microsoft:
    {"url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=1071484607&utm_source=Indeed"}
    {"url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=1695844082&utm_source=Indeed" }
    { "url" : "https://careers.microsoft.com/jobdetails.aspx?jid=216571&memid=932148152&utm_source=Indeed"}}

其他公司也一样

1 个答案:

答案 0 :(得分:2)

这真的(仍)最好由多个查询处理,因为MongoDB真的"仍然"没有真正有效的运营商来做这件事。

你可以用MongoDB 3.2做这样的事情,但有明显的"捕获":

db.Books.aggregate([
    { "$group": {
        "_id": "$company",
        "count": { "$sum": 1 },
        "urls": {
            "$push": "$url"
        }
    }},
    { "$sort": { "count": -1 } },
    { "$limit": 10 },
    { "$project": {
        "count": 1,
        "urls": { "$slice": ["$urls",0, 3] }
    }}
])

显而易见的问题是,无论如何,你仍然在添加所有的" url"内容到分组数组中。这有可能超过16MB的BSON限制。它可能没有,但是添加"所有"它仍然有点浪费。当你只想要"三"他们。

所以即便如此,实际查询" urls"可能更实际。单独列出前10名结果中的每一项。

这里是node.js的列表,演示了:

var async = require('async'),
    mongodb = require('mongodb'),
    MongoClient = mongodb.MongoClient;

MongoClient.connect("mongodb://localhost/test",function(err,db) {

    if (err) throw err;

    // Get the top 10
    db.collection("Books").aggregate(
        [
            { "$group": {
                "_id": "$company",
                "count": { "$sum": 1 }
             }},
             { "$sort": { "count": -1 } },
             { "$limit": 10 }
        ],function(err,results) {
            if (err) throw err;

            // Query for each result and map query response as urls
            async.map(
                results,
                function(result,callback) {
                    db.collection("Books").find({ 
                       "company": result.company 
                    }).limit(3).toArray(function(err,items) {
                        result.urls = items.map(function(item) { 
                            return item.url;
                        });
                        callback(err,result);
                    })
                },
                function(err,results) {
                    if (err) throw err;
                    // each result entry has 3 urls
                }
            );
        }
     )

});

是的,它对数据库的更多调用,但实际上只有十个,因此不是真正的问题。

真实的解决方案包含在SERVER-9377 - Extend $push or $max to allow collecting "top" N values per _id key in $group phase中。这有希望的"进展中"状态,因此它正在积极开展工作。

一旦解决了这个问题,那么单个聚合语句就变得可行了,从那以后你就可以“限制”#34;由此产生的"网址"在初始$push只有三个条目,而不是在事后删除除了三个以外的所有条目。