Recuperar solo el elemento consultado en una matriz de objetos en la colección MongoDB
Supongamos que tiene los siguientes documentos en mi colección:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Hacer consulta:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
O
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Devuelve el documento coincidente (Document 1) , pero siempre con TODOS los elementos del array en shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
Sin embargo, me gustaría obtener el documento (Documento 1) solo con el array que contiene color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
¿Cómo puedo hacer esto?
10 answers
Nuevo MongoDB 2.2$elemMatch
operador de proyección proporciona otra forma de alterar el documento devuelto para que contenga primero elemento shapes
coincidente:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Devuelve:
{"shapes" : [{"shape": "circle", "color": "red"}]}
En 2.2 también puede hacer esto usando el $ projection operator
, donde el $
en un nombre de campo de objeto de proyección representa el índice del primer elemento de matriz coincidente del campo de la consulta. Lo siguiente devuelve los mismos resultados que arriba:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Actualización
A partir de la versión 3.2, puede utilizar la nueva $filter
operador de agregación para filtrar una matriz durante la proyección, que tiene la ventaja de incluir todas las coincidencias, en lugar de solo la primera.
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
}}
])
Resultados:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-25 18:04:43
El nuevo Marco de agregación en MongoDB 2.2+ proporciona una alternativa a Map/Reduce. Las $unwind
el operador se puede usar para separar su matriz shapes
en un flujo de documentos que se pueden emparejar:
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"
}}
)
Resultados en:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-28 08:41:36
Precaución: Esta respuesta proporciona una solución que era relevante en ese momento, antes de que se introdujeran las nuevas características de MongoDB 2.2 y posteriores. Consulte las otras respuestas si está utilizando una versión más reciente de MongoDB.
El parámetro selector de campo está limitado a propiedades completas. No se puede usar para seleccionar parte de una matriz, solo la matriz completa. Intenté usar el operador posi posicional, pero eso no funcionó.
La forma más fácil es simplemente filtrar las formas en el cliente.
Si realmente necesita la salida correcta directamente de MongoDB, puede usar un map-reduce para filtrar las formas.
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()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-19 18:42:45
Otra forma interesante es usar $redact, que es una de las nuevas características de agregación de MongoDB 2.6. Si está utilizando 2.6, no necesita un unwind unwind que podría causarle problemas de rendimiento si tiene arreglos grandes.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restringe el contenido de los documentos basándose en la información almacenada en los propios documentos" . Así que solo se ejecutará dentro del documento. Básicamente escanea su documento arriba a la parte inferior, y comprueba si coincide con su if
condición que está en $cond
, si hay coincidencia se va a mantener el contenido($$DESCEND
) o eliminar($$PRUNE
).
En el ejemplo anterior, primero $match
devuelve todo el array shapes
, y $redact lo reduce al resultado esperado.
Tenga en cuenta que {$not:"$color"}
es necesario, porque también escaneará el documento superior, y si $redact
no encuentra un campo color
en el nivel superior, devolverá false
que podría eliminar todo el documento lo cual no queremos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-04 08:31:35
Es mejor que pueda consultar en el elemento de matriz coincidente utilizando $slice
es útil para devolver el objeto significativo en una matriz.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
es útil cuando conoces el índice del elemento, pero a veces quieres
cualquier elemento de matriz coincidió con sus criterios. Puede devolver el elemento correspondiente
con el operador $
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-18 09:14:37
La sintaxis para buscar en mongodb es
db.<collection name>.find(query, projection);
Y la segunda pregunta que has escrito, es
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
En esto ha utilizado el operador $elemMatch
en la parte de consulta, mientras que si utiliza este operador en la parte de proyección, obtendrá el resultado deseado. Puede escribir su consulta como
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
Esto le dará el resultado deseado.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-31 08:19:11
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
SALIDAS
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-07 06:25:02
Gracias a JohnnyHK.
Aquí solo quiero agregar un uso más complejo.
// 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"
}
]}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:50
Solo necesita ejecutar query
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
La salida de esta consulta es
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
Como esperabas, dará el campo exacto de la matriz que coincide con el color:'red'.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-10 07:50:37
Junto con project project será más apropiado de otro modo los elementos coincidentes se agruparán junto con otros elementos en el documento.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-09 15:45:13