2016-04-14 2 views
0

Я пытаюсь выполнить поиск в поддокументах. Это моя структура моего документа:MongoDB - поиск вспомогательных документов

{ 
    _id: <ObjectID>, 
    email: ‘[email protected]’, 
    password: ‘12345’, 
    images: [ 
     { 
      title: ‘Broken Hand’, 
      description: ‘Here is a full description’, 
      comments: [ 
       { 
        comment: ‘Looks painful’, 
       } 
      ], 
      tags: [‘hand’, ‘broken’] 
     } 
    ] 
} 

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

db.site_users.find({'images.tags': "broken"}, {images: 1, images: {$elemMatch: { 'tags': 'broken'}}}).pretty() 

Может кто-нибудь указать мне в правильном направлении, как я могу получить все изображения?

+0

Ваше элементовое совпадение, похоже, находится в секции проекта, а не в секции фильтра (но я могу ошибаться). Попробуйте: 'db.site_users.find ({'images.tags':" broken ", {images: {$ elemMatch: {'tags': 'broken'}}}, {images: 1}). Pretty()' ? –

ответ

0

Вы можете использовать aggregation framework для этого:

db.site_users.aggregate([ 
    {$unwind: "$images"}, 
    {$match:{ 
     "images.tags": "broken" 
    }} 
]) 
0

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

В идеале это сделать с MongoDB 3.2, используя $filter внутри $project:

db.site_users.aggregate([ 
    { "$match": { "images.tags": "broken" }}, 
    { "$project": { 
     "email": 1, 
     "password": 1, 
     "images": { 
      "$filter": { 
       "input": "$images", 
       "as": "image", 
       "cond": { 
        "$setIsSubSet": [["broken"], "$$image.tags"] 
       } 
      } 
     } 
    }} 
]) 

или, возможно, с помощью $map и $setDifference, который также совместим с MongoDB 2.6, до тех пор, как «образы» содержание «уникальным» для каждая запись. Это происходит из-за «набор» операции, в которой «наборы» являются «уникальными»:

db.site_users.aggregate([ 
    { "$match": { "images.tags": "broken" }}, 
    { "$project": { 
     "email": 1, 
     "password": 1, 
     "images": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$images", 
        "as": "image", 
        "in": { 
         "$cond": { 
          "if": { "$setIsSubSet": [["broken"], "$$image.tags" ] }, 
          "then": "$$image", 
          "else": false 
         } 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 

Это может быть сделано в более ранних версиях MongoDB, но, возможно, лучше избегать из-за стоимости обработки $unwind на массив:

db.site_users.aggregate([ 
    { "$match": { "images.tags": "broken" }}, 
    { "$unwind": "$images" }, 
    { "$match": { "images.tags": "broken" }}, 
    { "$group": { 
     "_id": "$_id", 
     "email": { "$first": "$email" }, 
     "password": { "$first": "$password" }, 
     "images": { "$push": "$images" } 
    }}  
]) 

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

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

Если у вас нет современной версии, тогда получите ее. Эти особенности имеют значение для практического и практичного.

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