2014-02-12 4 views
-2

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

Так вот пример двух разных документов в коллекции:

{ 
    "_id" : "Tvq444454j", 
    "name" : "Jim", 
    "forms" : [ 
     { 
      "name" : "Jorney", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Women", 
      "status" : "void" 
     }, 
     { 
      "name" : "Child", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Farm", 
      "status" : "closed" 
     } 
    ] 
}, 

{ 
    "_id" : "Tvq579754r", 
    "name" : "Tom", 
    "forms" : [ 
     { 
      "name" : "PreOp", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Alert", 
      "status" : "closed" 
     }, 
     { 
      "name" : "City", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Country", 
      "status" : "closed" 
     } 
    ] 
} 

и ожидаемый результат:

{ 
    "_id" : "Tvq579754r", 
    "name" : "Tom", 
    "forms" : [ 
     { 
      "name" : "PreOp", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Alert", 
      "status" : "closed" 
     }, 
     { 
      "name" : "City", 
      "status" : "closed" 
     }, 
     { 
      "name" : "Country", 
      "status" : "closed" 
     } 
    ] 
} 

Поскольку не существует стандартного оператора запроса в соответствии все из элементов массива при этом условии, решение было найдено с использованием агрегации. Это вернет _id документов в коллекции, которые имеют все свои элементы «формы», установленные в статус «закрыто».

db.forms.aggregate([ 
    {$unwind: "$forms" }, 
    {$group: { _id: "$_id", status: {$addToSet: "$forms.status" }}}, 
    {$unwind: "$status"}, 
    {$sort: { _id: 1, status: -1 }}, 
    {$group: {_id: "$_id", status: {$first: "$status"}}}, 
    {$match:{ status: "closed" }} 
]) 

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

Учитывая это, можно ли каким-либо образом вернуть исходные документы из агрегирования точно в том же виде, что и в коллекции, при этом все еще выполняя этот тип фильтрации?

ответ

5

Падение под категорию глупые агрегирующие трюки - это небольшая техника, которая часто пропускается.

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

{$project: { 
     _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"} 
    }, 

Если это делается, все, что свернуто _id, сохраняет документ в его первоначальной форме. В конце всех других стадий агрегации, выдают окончательный $project, чтобы восстановить истинный первоначальный вид документа:

{$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}} 

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

Кроме того, в таком случае, когда вы знаете, что вы ищете только для набора результатов, которые собираются, чтобы соответствовать определенному набору условий, используйте оператор $match как первой стадии трубопровода агрегации. Это не только полезно для уменьшения размера рабочего набора, но и для только этап, на котором вы можете использовать индекс и где вы можете значительно увеличить производительность запросов.

Весь процесс вместе:

db.forms.aggregate([ 
    {$match: { "forms.status": "closed" } }, 
    {$project: { 
     _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"} 
    }, 
    {$unwind: "$forms"}, 
    {$group: { _id: "$_id", status: {$addToSet: "$forms.status"}}}, 
    {$unwind: "$status"}, 
    {$sort: { _id: 1, status: -1} }, 
    {$group: { _id: "$_id", status: {$first: "$status"} }}, 
    {$match: { status: "closed"}}, 
    {$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}} 
]) 
+0

Там должен быть оператор '*' или что-то в ближайшее время, что сможет вернуть исходный документ в полном виде в агрегации трубопровода – Sammaye

+0

@Sammaye Я считаю, что вы ссылаясь на $$ ROOT, который предназначен для другой цели, но может быть использован здесь. Это должно быть доступно в версии 2.6. Это один из методов, который можно использовать ** сейчас **, и все же, возможно, ускользнул от многих людей. –

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