MongoDB: несколько $elemMatch

У меня есть документы MongoDB, структурированные следующим образом:

{_id: ObjectId("53d760721423030c7e14266f"),
fruit: 'apple',
vitamins: [
    {
     _id: 1,
     name: 'B7',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 2,
     name: 'A1',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 3,
     name: 'A1',
     usefulness: 'useful',
     state: 'non_free',
    }
  ]
}
{_id: ObjectId("53d760721423030c7e142670"),
fruit: 'grape',
vitamins: [
    {
     _id: 4,
     name: 'B6',
     usefulness: 'useful',
     state: 'free',
    }
    {
     _id: 5,
     name: 'A1',
     usefulness: 'useful',
     state: 'non_free',
    }
    {
     _id: 6,
     name: 'Q5',
     usefulness: 'non_useful',
     state: 'non_free',
    }
  ]
}

Я хочу запросить и получить все фрукты, которые имеют как {name: 'A1', state: 'non_free'}, так и {name: 'B7', state: 'free'}. В худшем случае я хочу, по крайней мере, подсчитать эти записи, если их получение невозможно, и если существует эквивалентный код для pymongo, чтобы знать, как его написать.

В данном примере я хочу получить только первый (яблочный) документ.

Если я использую $elemMatch, он работает только для одного условия, но не для обоих. Например. если я запрошу find({'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'}, '$elemMatch': {'name': 'B7', 'state': 'free'}}}), он получит все фрукты с {'name': 'B7', 'state': 'free'}.


person gevra    schedule 29.07.2014    source источник


Ответы (2)


В этом случае вы можете использовать $and-operator .

Попробуйте этот запрос:

find({
    $and: [
         {'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'} } },
         {'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'} } }
    ]
});

Чтобы объяснить, почему вы получили только результат, соответствующий второму критерию: объекты внутри каждого {}, который вы передаете в MongoDB, представляют собой пары ключ/значение. Каждый ключ может существовать только один раз для каждого объекта. Когда вы пытаетесь дважды присвоить значение одной и той же клавише, второе назначение переопределит первое. В вашем случае вы присвоили ключу $elemMatch два разных значения в одном и том же объекте, поэтому первое было проигнорировано. Запрос, который фактически поступил в MongoDB, был просто find({'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'}}}).

Всякий раз, когда вам нужно дважды применить один и тот же оператор к одной и той же клавише, вам нужно использовать $or или $and.

person Philipp    schedule 29.07.2014
comment
но мне нужно, чтобы во фруктах были оба витамины, а не ни один из них - person gevra; 29.07.2014
comment
@gevra Плохо, в этом случае используйте $and вместо $or. Обновленный ответ. - person Philipp; 29.07.2014
comment
используйте $and вместо $or - person Barno; 29.07.2014
comment
@Филипп, ты так быстро :) - person Barno; 29.07.2014
comment
Можно ли сделать что-то подобное динамически для неизвестного количества $elemMatch? - person Puma; 23.04.2015
comment
@chillinpuma Вы можете динамически создавать массив, который вы передаете $and в приложении. - person Philipp; 23.04.2015

person    schedule
comment
пожалуйста объясните свой ответ - person Mo.; 29.07.2014
comment
Запрос находит документы, которые удовлетворяют следующим условиям: 1) Точное совпадение вложенного документа для имени: B7 и состояния: бесплатно 2) Точное совпадение вложенного документа для имени: A1 и состояния: non_free, и массив должен удовлетворять обоим условиям, которые я использовал $все оператор - person Karthik Bashyam; 29.07.2014
comment
Согласно stackoverflow, вы должны объяснить ответ - person Mo.; 29.07.2014
comment
Примечание. $all эквивалентно операции $and для указанных значений из $все документы оператора - person ppython; 14.12.2017