2016-10-03 1 views
2

Предположим, у меня есть документ, как это:Как подсчитать количество ключей в поддокументе с использованием конвейера агрегации?

{ 
"_id" : ObjectId("57eb386e37b4842ff5f386c9"), 
"lesson_id" : ObjectId("57e27cd190e6993e393f5c74"), 
"student_id" : ObjectId("57d3c3f590e6995fe8de7932"), 
"answer_records" : { 
    "1" : { 
     "answer" : [ 
      "A" 
     ] 
    }, 
    "3" : { 
     "answer" : [ 
      "C" 
     ] 
    } 
} 

Я хочу, чтобы подсчитать количество записей ответов в коллекции. По-видимому, этот документ содержит две записи ответов, которые являются «1» и «3». Итак, мой вопрос заключается в том, как достичь этого, используя конвейер агрегации.

ответ

1

Для MongoDB 3.6 и новее используйте оператор $objectToArray в конвейере агрегации для преобразования документа в массив. Возвращаемый массив содержит элемент для каждой пары field/value в исходном документе. Каждый элемент в возвращаемом массиве представляет собой документ, который содержит два поля: k и v.

Получив массив, вы можете использовать использование $addFields ступени конвейера, чтобы создать поле, которое удерживает отсчеты и фактический отсчет происходят с использованием оператора в $size.

Все это можно сделать в одном трубопроводе путем вложения выражения следующим образом:

db.collection.aggregate([ 
    { 
     "$addFields": { 
      "answers_count": { 
       "$size": { 
        "$objectToArray": "$answer_records" 
       } 
      } 
     } 
    }  
]) 

Пример вывода

{ 
    "_id" : ObjectId("57eb386e37b4842ff5f386c9"), 
    "lesson_id" : ObjectId("57e27cd190e6993e393f5c74"), 
    "student_id" : ObjectId("57d3c3f590e6995fe8de7932"), 
    "answer_records" : { 
     "1" : { 
      "answer" : [ 
       "A" 
      ] 
     }, 
     "3" : { 
      "answer" : [ 
       "C" 
      ] 
     } 
    }, 
    "answers_count": 2 
} 

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

Идеальная конструкция следующим образом:

{ 
    "_id" : ObjectId("57eb386e37b4842ff5f386c9"), 
    "lesson_id" : ObjectId("57e27cd190e6993e393f5c74"), 
    "student_id" : ObjectId("57d3c3f590e6995fe8de7932"), 
    "answer_records" : [ 
     { "id": "1", "answer": "A" } 
     { "id": "3", "answer": "C" } 
    ] 
} 

, который вы можете просто применить $project трубопровода агрегации, которая использует $size оператора вернуть длину массива answer_records в документ:

db.collection.aggregate([ 
    { 
     "$project": { 
      "lesson_id": 1, 
      "student_id": 1, 
      "count": { "$size": "$answer_records" } 
     } 
    } 
]) 

Если вы хотите получить общее количество записей ответов для всей коллекции, добавьте еще $group трубопровода, чтобы получить накопленную сумму для всех документов с использованием _id нуль:

db.collection.aggregate([ 
    { 
     "$project": {   
      "count": { "$size": "$answer_records" } 
     } 
    }, 
    { 
     "$group": { 
      "_id": null, 
      "total_answers": { "$sum": "$count" } 
     } 
    } 
]) 

В противном случае с текущим дизайном единственным вариантом является MapReduce, который намного медленнее:

db.collection.mapReduce(
    function() { 
     emit(this._id, Object.keys(this.answer_records).length); 
    }, 
    function() { }, 
    { "out": { "inline": 1 } } 
) 

Выход для образца:

{ 
    "results" : [ 
     { 
      "_id" : ObjectId("57eb386e37b4842ff5f386c9"), 
      "value" : 2 
     } 
    ], 
    .... 
} 

Чтобы получить общее для всех документов в коллекции, то запустить эту операцию MapReduce:

db.collection.mapReduce(
    function() { 
     emit(null, Object.keys(this.answer_records).length); 
    }, 
    function(key, values) { 
     return Array.sum(values); 
    }, 
    { "out": { "inline": 1 } } 
) 
2

В вашем случае, это гораздо проще просто использовать JS.

На Монго оболочки:

var json=db.sof.findOne().answer_records; 

Object.keys(json).length; 

печать 2 для числа записей ответа в указанном документе.

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