Как уже отмечалось, незначительное несоответствие в данных вопроса текущему заявленному конвейерному процессу, поскольку $unwind
может только использоваться на массивы и tags
, как указано в вопросе, не массив .
Для данных, представленных в вопросе, вы в основном хотите конвейер, подобный этому:
db.collection.aggregate([
{ "$addFields": {
"tags": { "$objectToArray": "$tags" }
}},
{ "$unwind": "$tags" },
{ "$unwind": "$tags.v" },
{ "$group": {
"_id": {
"tag": "$tags.k",
"phrase": "$tags.v.word"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.tag",
"tag_list": {
"$push": {
"count": "$count",
"phrase": "$_id.phrase"
}
}
}}
])
Опять же, согласно примечанию, поскольку tags
на самом деле является объектом , то, что вам действительно нужно для сбора данных на основе его субключей , поскольку вопрос спрашивать, это превратить это по существу в массив элементов.
Использование $replaceRoot
в вашем текущем конвейере, по-видимому, указывает на то, что $objectToArray
имеет правильного использования здесь, так как оно доступно из более поздние патчи MongoDB 3.4, являющиеся минимальной версией, которую вы должны использовать в работе прямо сейчас.
То, что $objectToArray
на самом деле в значительной степени выполняет то, что говорит имя, и создает массив (или «список», чтобы быть более pythonic ) записей, разбитых на ключ и значение пар. По сути, это «список» объектов (или «dict» записей), которые имеют ключи k
и v
соответственно. Вывод первого этапа конвейера будет выглядеть следующим образом в поставляемом документе:
{
"book_id": "5cf172220fb516f706d00591",
"tags": [
{
"k": "person",
"v": [
{
"start_match": 209,
"length_match": 6,
"word": "kimmel"
}
]
}, {
"k": "organization",
"v": [
{
"start_match": 107,
"length_match": 12,
"word": "philadelphia"
}, {
"start_match": 209,
"length_match": 13,
"word": "kimmel center"
}
]
}, {
"k": "location",
"v": [
{
"start_match": 107,
"length_match": 12,
"word": "philadelphia"
}
]
}
],
"deleted" : false
}
Таким образом, вы сможете увидеть, как теперь вы можете легко получить доступ к этим k
значениям и использовать их в группировке , и, конечно, v
также является стандартным массивом. Так что это просто две $unwind
ступени, как показано, а затем две $group
ступени. Будучи первым $group
для сбора по комбинации клавиш, а вторым собираем по основному ключу группировки и добавляем другие накопления к " list " в этой записи.
Конечно, вывод из приведенного выше списка не точно , как вы просили в вопросе, но данные в основном там. При желании можно добавить этап $addFields
или $project
, чтобы по существу переименовать ключ _id
в качестве конечного этапа агрегирования:
{ "$addFields": {
"_id": "$$REMOVE",
"tag": "$_id"
}}
Или просто сделать что-то pythonic с небольшим пониманием списка на выходе курсора:
cursor = db.collection.aggregate([
{ "$addFields": {
"tags": { "$objectToArray": "$tags" }
}},
{ "$unwind": "$tags" },
{ "$unwind": "$tags.v" },
{ "$group": {
"_id": {
"tag": "$tags.k",
"phrase": "$tags.v.word"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.tag",
"tag_list": {
"$push": {
"count": "$count",
"phrase": "$_id.phrase"
}
}
}}
])
output = [{ 'tag': doc['_id'], 'tag_list': doc['tag_list'] } for doc in cursor]
print({ 'response': output });
И окончательный вывод в виде "списка" , который вы можете использовать для response
:
{
"tag_list": [
{
"count": 1,
"phrase": "philadelphia"
}
],
"tag": "location"
},
{
"tag_list": [
{
"count": 1,
"phrase": "kimmel"
}
],
"tag": "person"
},
{
"tag_list": [
{
"count": 1,
"phrase": "kimmel center"
}, {
"count": 1,
"phrase": "philadelphia"
}
],
"tag": "organization"
}
Отметив, что при использовании подхода list у вас есть немного больший контроль над порядком "ключей" в качестве выходных данных, поскольку сама MongoDB просто добавляет новые имена ключей в проекции сохраняя существующие ключи в порядке. Если такие вещи важны для вас, это так. Хотя это действительно не должно быть, так как все структуры типа Object / Dict не должны рассматриваться как имеющие какой-либо установленный порядок ключей. Для этого предназначены массивы (или списки).