2015-12-04 3 views
4

Im пытается вытащить данные о наборе документов, который выглядит как:вхождения количества массивов во всех документах с Монго

[ 
    { 
    name: 'john', 
    sex: 'male', 
    hobbies: ['football', 'tennis', 'swimming'] 
    }, 
    { 
    name: 'betty' 
    sex: 'female', 
    hobbies: ['football', 'tennis'] 
    }, 
    { 
    name: 'frank' 
    sex: 'male', 
    hobbies: ['football', 'tennis'] 
    } 
] 

Я пытаюсь использовать структуру агрегации для представления данных, Раздельное по полу, считая самые распространенные увлечения. Результаты должны выглядеть примерно так.

{ _id: 'male', 
    total: 2, 
    hobbies: { 
    football: 2, 
    tennis: 2, 
    swimming: 1 
    } 
}, 
{ _id: 'female', 
    total: 1, 
    hobbies: { 
     football: 1, 
     tennis: 1 
    } 
} 

До сих пор я могу получить в общей сложности каждого пола, но я не знаю, как я мог бы использовать раскручивать, чтобы получить итоговые данные массива хобби.

Мой код до сих пор:

collection.aggregate([ 
     { 
      $group: { 
       _id: '$sex', 
       total: { $sum: 1 } 
      } 
     } 
    ]) 

ответ

7

Лично я не большой поклонник преобразования «данных», как имена ключей в результате. Основополагающие принципы агрегирования имеют тенденцию к увеличению, так как эта операция не поддерживается.

Так что личные предпочтения, чтобы сохранить «данные» как «данные» и признать, что обрабатываемый выход на самом деле лучше и логичнее последовательного проектирования объекта:

db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]) 

который производит результат, как это:

[ 
    { 
      "_id" : "female", 
      "total" : 1, 
      "hobbies" : [ 
       { 
        "name" : "tennis", 
        "count" : 1 
       }, 
       { 
        "name" : "football", 
        "count" : 1 
       } 
      ] 
    }, 
    { 
     "_id" : "male", 
     "total" : 2, 
     "hobbies" : [ 
      { 
       "name" : "swimming", 
       "count" : 1 
      }, 
      { 
       "name" : "tennis", 
       "count" : 2 
      }, 
      { 
       "name" : "football", 
       "count" : 2 
      } 
     ] 
    } 
] 

Таким образом, начальная $group делает подсчет за «пол» и складывает на хобби в массив массивов. Затем, чтобы де-нормировать вас $unwind дважды, чтобы получить особые предметы, $group, чтобы получить итоговые значения за каждое хобби под каждым сексом и, наконец, перегруппируйте массив для каждого пола в одиночку.

Это те же данные, что и согласованная и органическая структура, которую легко обрабатывать, а MongoDB и структура агрегации были весьма счастливы в производстве этого выхода.

Если вы действительно должны преобразовать свои данные в имена ключей (и я по-прежнему рекомендую вам не делать это, так как это не является хорошим шаблоном для проектирования), то выполнение такой трансформации из конечного состояния довольно тривиально для клиента обработки кода. В качестве основного примера JavaScript подходящий для оболочки:

var out = db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]).toArray(); 

out.forEach(function(doc) { 
    var obj = {}; 
    doc.hobbies.sort(function(a,b) { return a.count < b.count }); 
    doc.hobbies.forEach(function(hobby) { 
     obj[hobby.name] = hobby.count; 
    }); 
    doc.hobbies = obj; 
    printjson(doc); 
}); 

И тогда вы в основном обработки каждого результата курсор в желаемую форму выходного, который на самом деле не является функцией агрегации, что действительно необходимо на сервере в любом случае:

В этом случае должно быть довольно сложно реализовать такое манипулирование в потоковой обработке результата курсора, чтобы преобразовать его по мере необходимости, поскольку это в основном одна и та же логика.

С другой стороны, вы всегда можете реализовать все манипуляции на сервере с помощью MapReduce вместо:

db.people.mapReduce(
    function() { 
     emit(
      this.sex, 
      { 
       "total": 1, 
       "hobbies": this.hobbies.map(function(key) { 
        return { "name": key, "count": 1 }; 
       }) 
      } 
     ); 
    }, 
    function(key,values) { 
     var obj = {}, 
      reduced = { 
       "total": 0, 
       "hobbies": [] 
      }; 

     values.forEach(function(value) { 
      reduced.total += value.total; 
      value.hobbies.forEach(function(hobby) { 
       if (!obj.hasOwnProperty(hobby.name)) 
        obj[hobby.name] = 0; 
       obj[hobby.name] += hobby.count; 
      }); 
     }); 

     reduced.hobbies = Object.keys(obj).map(function(key) { 
      return { "name": key, "count": obj[key] }; 
     }).sort(function(a,b) { 
      return a.count < b.count; 
     }); 

     return reduced; 
    }, 
    { 
     "out": { "inline": 1 }, 
     "finalize": function(key,value) { 
      var obj = {}; 
      value.hobbies.forEach(function(hobby) { 
       obj[hobby.name] = hobby.count; 
      }); 
      value.hobbies = obj; 
      return value; 
     } 
    } 
) 

Где MapReduce имеет свой особый стиль вывода, но одни и те же принципы используются в накоплении и манипуляции , если скорее всего, не так эффективно, как в рамках агрегации можно сделать:

"results" : [ 
     { 
      "_id" : "female", 
      "value" : { 
       "total" : 1, 
       "hobbies" : { 
        "football" : 1, 
        "tennis" : 1 
       } 
      } 
     }, 
     { 
      "_id" : "male", 
      "value" : { 
       "total" : 2, 
       "hobbies" : { 
        "football" : 2, 
        "tennis" : 2, 
        "swimming" : 1 
       } 
      } 
     } 
    ] 

в конце концов, я до сих пор говорят, что первый вид обработки является наиболее эффективным и обеспечивает на мой взгляд, наиболее естественным и последовательным работы e, даже не пытаясь преобразовать точки данных в имена ключей. Вероятно, лучше всего рассмотреть следующий шаблон, но если вам действительно нужно, тогда есть способы манипулировать результатами в желаемой форме в различных подходах к обработке.

+0

Просто хочу отказаться от «спасибо» за ваш ответ ... хотя я не задавал вопрос :) Я очень помог мне! – chrisdennig

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