2016-03-10 3 views
1

У меня есть два поля 'компания' и 'url'. Я хочу сортировать его по количеству раз, когда происходит «компания», а затем отображается три «url», соответствующие этой конкретной компании. Данные хранятся следующим образом:Mongo запрос сортировать по разным подсчетам

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

Это действительно (по-прежнему) лучше всего обрабатываются несколькими запросами, так как 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 "в сгруппированный массив. Это может превысить предел BSON в 16 МБ. Возможно, это не так, но все равно немного расточительно добавлять «все» содержимое, когда вам нужно только «три» из них.

Так что даже тогда, вероятно, более практично просто запросить «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. У этого есть многообещающий статус «Прогресс», поэтому он активно работает.

После того, как это будет разрешено, один оператор агрегации станет жизнеспособным, так как тогда вы сможете «ограничить» полученные «URL» в intial $push только тремя записями, а не удалять все, кроме трех, после факта ,

+0

Большое спасибо. Ценить это. Очень хорошо объяснено. – dsl1990

Смежные вопросы