Ответ 1
То, что вы ищете, это позиционный оператор $
и "проекция". Для одного поля вам нужно сопоставить требуемый элемент массива с помощью "точечной нотации", для более чем одного поля используйте $elemMatch
:
db.products.find(
{ "items.date": "31.08.2014" },
{ "shop": 1, "name":1, "items.$": 1 }
)
Или $elemMatch
для более чем одного совпадающего поля:
db.products.find(
{ "items": {
"$elemMatch": { "date": "31.08.2014", "purchasePrice": 1 }
}},
{ "shop": 1, "name":1, "items.$": 1 }
)
Они работают только для одного элемента массива и только один будет возвращен. Если вы хотите, чтобы из ваших условий было возвращено более одного элемента массива, вам потребуется более сложная обработка с помощью структуры агрегации.
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$unwind": "$items" },
{ "$match": { "items.date": "31.08.2014" } },
{ "$group": {
"_id": "$_id",
"shop": { "$first": "$shop" },
"name": { "$first": "$name" },
"items": { "$push": "$items" }
}}
])
Или, возможно, в более короткой/быстрой форме, начиная с MongoDB 2.6, где ваш массив элементов содержит уникальные записи:
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$project": {
"shop": 1,
"name": 1,
"items": {
"$setDifference": [
{ "$map": {
"input": "$items",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.date", "31.08.2014" ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
])
Или, возможно, с $redact
, но немного надуманным:
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$redact": {
"$cond": [
{ "$eq": [ { "$ifNull": [ "$date", "31.08.2014" ] }, "31.08.2014" ] },
"$$DESCEND",
"$$PRUNE"
]
}}
])
Более современный, вы бы использовали $filter
:
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$addFields": {
"items": {
"input": "$items",
"cond": { "$eq": [ "$$this.date", "31.08.2014" ] }
}
}}
])
И с несколькими условиями, $elemMatch
и $and
внутри $filter
:
db.products.aggregate([
{ "$match": {
"$elemMatch": { "date": "31.08.2014", "purchasePrice": 1 }
}},
{ "$addFields": {
"items": {
"input": "$items",
"cond": {
"$and": [
{ "$eq": [ "$$this.date", "31.08.2014" ] },
{ "$eq": [ "$$this.purchasePrice", 1 ] }
]
}
}
}}
])
Так что это зависит только от того, ожидаете ли вы, что один элемент совпадет с несколькими, и какой подход лучше. Но там, где это возможно, метод .find()
, как правило, будет быстрее, поскольку в нем отсутствуют издержки других операций, которые в последних формах не сильно отстают.
Как примечание, ваши "даты" представлены в виде строк, что не очень хорошая идея в будущем. Подумайте о том, чтобы изменить их на надлежащие типы объектов Date, что очень поможет вам в будущем.