Mongo 查询按非重复计数排序

Mongo query to sort by distinct count

我有两个字段 'company' 和 'url'。我想按不同 '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 家公司),然后显示与不同公司相对应的三个 URL(如果不同公司的数量至少为三个)。类似于:

{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"}}

其他公司也是如此

这确实(仍然)最好由多个查询处理,因为 MongoDB 实际上 "still" 还没有真正有效的运算符来执行此操作。

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

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" 内容的 all 添加到分组数组中。这有可能超过 16MB 的 BSON 限制。可能不会,但是当您只想要 "three" 内容时添加 "all" 内容仍然有点浪费。

因此,即便如此,在前 10 个结果中分别实际查询 "urls" 可能更实用。

这是 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
                }
            );
        }
     )

});

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

真正的 解决方案包含在 SERVER-9377 - Extend $push or $max to allow collecting "top" N values per _id key in $group phase 中。这具有良好的 "In Progress" 状态,因此正在积极进行中。

一旦解决了这个问题,那么单个聚合语句就变得可行了,从那时起您就可以 "limit" 初始 $push 中的结果 "urls" 到三个条目,而不是在事后删除除三个之外的所有内容。