2014-02-07 3 views
10

У меня есть следующий список документов:MongoDB рамки агрегация матч вложенными документами

{ 
    "_id" : "Tvq579754r", 
    "name": "Tom", 
    "forms": { 
      "PreOp":{ 
      "status":"closed"   
      }, 

      "Alert":{ 
      "status":"closed"   
      }, 

      "City":{ 
       "status":"closed"   
      }, 

      "Country":{ 
      "status":"closed"   
      } 
    } 
}, 
.... 
{ 
    "_id" : "Tvq444454j", 
    "name": "Jim", 
    "forms": { 
      "Jorney":{ 
      "status":"closed"   
      }, 

      "Women":{ 
      "status":"void"    
      }, 

     "Child":{ 
      "status":"closed"   
     }, 

     "Farm":{ 
      "status":"closed"    
     } 
    } 
} 

Я хочу, чтобы фильтровать их по полю («forms.name_of_form.status») «статус». Мне нужно получить все документы, у которых нет 'forms.name_of_form.status' equal 'void'.

Ожидаемый результат (документ без аннулированы статуса вида):

{ 
    "_id" : "Tvq579754r", 
    "name": "Tom", 
    "forms": { 
      "PreOp":{ 
      "status":"closed"   
      }, 

      "Alert":{ 
      "status":"closed"   
      }, 

      "City":{ 
       "status":"closed"   
      }, 

      "Country":{ 
      "status":"closed"   
      } 
    } 
} 
+0

Вы должны получить все документы или все _ids? Кроме того, вам нужно отфильтровать документы, которые имеют определенную форму со статусом «void» или ** any ** status «void»? – Jinxcat

+0

Я хочу получить все документы, где каждый документ имеет все формы в «закрытом» статусе не в «void» –

+0

Является ли список имен форм конечным (указанным)? – Jinxcat

ответ

21

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

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

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

Вот что я имею в виду по пути. Для того, чтобы получить в состоянии в любом элементе вы должны сделать следующие

формы ->PreOp -> Статус

Формы ->оповещения -> Статус

Со вторым элемент постоянно меняется. Существует нет пути до wildcard что-то вроде этого, поскольку именование считается явным.

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

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

Так структура документа изменяется, чтобы сделать forms элемент является массивом, и вместо того, поместить поле статуса под ключ, который называет «поле формы» мы имеем каждый член Массив в качестве поддокумента, содержащего «поле формы» name и status. Таким образом, и идентификатор, и статус все еще соединены вместе, но представлены только как суб-документ.Это самое главное изменяет путь доступа к этим клавишам, так как теперь для как название поля и его статус мы можем сделать

формы ->статус

или

формы ->имя

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

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

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

db.forms.find({ "forms.status": "void" },{ _id: 1}) 

db.forms.find({ _id: $not: { $in: [<Object1>,<Object2>,<Object3>,... ] } }) 

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

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

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 для документов, которые соответствуют, но вы можете оформить запрос с $ в и вернуть все совпадения документы. Это лучше, чем оператор исключения, используемый ранее, и теперь мы можем использовать индекс, чтобы избежать полного сканирования коллекции.

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

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


EDIT:

Помимо изменения документа, чтобы иметь статус мастера, быстрый форму запроса на измененную структуру на самом деле:

db.forms.find({ "forms": { "$not": { "$elemMatch": { "status": "void" } } } }) 
+2

Одна вещь, которую я пропустил (facepalm) при использовании подхода агрегации, вы можете ** $ project ** весь документ как '_id', поскольку он уже уникален, и это ключ для группировки. Таким образом, добавив окончательный ** $ project ** и повторно установив ключи из '_id' в исходной форме, вы можете восстановить документ (ы) в исходное состояние и удалить необходимость выпуска другого запроса с помощью' _id' (ы). Забыл, потому что я слишком привык к трансформации в агрегации. –

+3

благодарит вас. Я потратил много времени на поиск такого подхода. –

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