Предположим, у вас есть следующие документы в моей коллекции:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"},
{
"shape":"circle",
"color":"red"}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"},
{
"shape":"circle",
"color":"green"}
]
}
Сделать запрос:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Или же
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Возвращает соответствующий документ (Документ 1), но всегда со всеми элементами массива в shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
Тем не менее, я хотел бы получить документ (Документ 1) только с массивом, который содержит color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
Как я могу это сделать?
MongoDB 2.2 новый $elemMatch
Оператор проекции предоставляет другой способ изменить возвращаемый документ, чтобы он содержал только первый соответствие shapes
элемент:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Возвращает:
{"shapes" : [{"shape": "circle", "color": "red"}]}
В 2.2 вы также можете сделать это, используя $ projection operator
, где $
в имени объекта проекции имя поля представляет индекс первого соответствующего элемента массива поля из запроса. Следующее возвращает те же результаты, что и выше:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Обновление
Начиная с версии 3.2, вы можете использовать новый $filter
оператор агрегирования для фильтрации массива во время проецирования, который имеет преимущество в том числе все совпадения, а не только первый.
db.test.aggregate([
// Get just the docs that contain a shapes element where color is 'red'
{$match: {'shapes.color': 'red'}},
{$project: {
shapes: {$filter: {
input: '$shapes',
as: 'shape',
cond: {$eq: ['$$shape.color', 'red']}
}},
_id: 0
}}
])
Результаты:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"}
]
}
]
Новый Структура агрегации в MongoDB 2.2+ предоставляет альтернативу Map / Reduce. $unwind
оператор может быть использован для разделения вашего shapes
массив в поток документов, которые могут быть сопоставлены:
db.test.aggregate(
// Start with a $match pipeline which can take advantage of an index and limit documents processed
{ $match : {
"shapes.color": "red"}},
{ $unwind : "$shapes" },
{ $match : {
"shapes.color": "red"}}
)
Результаты в:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"}
}
],
"ok" : 1
}
Внимание: Этот ответ предоставляет решение, которое было актуально в это время, до того, как были представлены новые функции MongoDB 2.2 и выше. Смотрите другие ответы, если вы используете более свежую версию MongoDB.
Параметр селектора поля ограничен полными свойствами. Его нельзя использовать для выбора части массива, только всего массива. Я пытался использовать $ позиционный оператор, но это не сработало.
Самый простой способ — просто отфильтровать фигуры. в клиенте.
Если ты действительно необходимость Правильный вывод напрямую из MongoDB вы можете использовать карту-уменьшить фильтровать формы.
function map() {
filteredShapes = [];
this.shapes.forEach(function (s) {
if (s.color === "red") {
filteredShapes.push(s);
}
});
emit(this._id, { shapes: filteredShapes });
}
function reduce(key, values) {
return values[0];
}
res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })
db[res.result].find()
Еще один интересный способ заключается в использовании $ красноломкий, которая является одной из новых функций агрегации MongoDB 2.6. Если вы используете 2.6, вам не нужен $ unwind, который может вызвать проблемы с производительностью, если у вас большие массивы.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"}
}}]);
$redact
«ограничивает содержание документов на основе информации, хранящейся в самих документах». Так будет бегать только внутри документа. Он в основном сканирует ваш документ сверху вниз и проверяет, совпадает ли он с вашим документом. if
состояние, которое находится в $cond
, если есть совпадение, оно либо сохранит содержимое ($$DESCEND
) или удалить ($$PRUNE
).
В приведенном выше примере сначала $match
возвращает целое shapes
массив, и $ redact сокращает его до ожидаемого результата.
Обратите внимание, что {$not:"$color"}
необходимо, потому что он будет сканировать верхний документ, а если $redact
не находит color
поле на верхнем уровне это вернется false
это может лишить весь документ, который мы не хотим.
Лучше вы можете запросить в соответствующем элементе массива, используя $slice
полезно ли возвращать значимый объект в массиве.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
полезно, когда вы знаете индекс элемента, но иногда вы хотите
какой элемент массива соответствует вашим критериям. Вы можете вернуть соответствующий элемент
с $
оператор.
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
ВЫХОДЫ
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"}
]
}
Синтаксис для поиска в mongodb:
db.<collection name>.find(query, projection);
и второй запрос, который вы написали, то есть
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
в этом вы использовали $elemMatch
оператор в части запроса, тогда как если вы используете этот оператор в части проекции, то вы получите желаемый результат. Вы можете записать свой запрос как
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
Это даст вам желаемый результат.
Здесь я просто хочу добавить более сложное использование.
// Document
{
"_id" : 1
"shapes" : [
{"shape" : "square", "color" : "red"},
{"shape" : "circle", "color" : "green"}
]
}
{
"_id" : 2
"shapes" : [
{"shape" : "square", "color" : "red"},
{"shape" : "circle", "color" : "green"}
]
}
// The Query
db.contents.find({
"_id" : ObjectId(1),
"shapes.color":"red"},{
"_id": 0,
"shapes" :{
"$elemMatch":{
"color" : "red"}
}
})
//And the Result
{"shapes":[
{
"shape" : "square",
"color" : "red"}
]}