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 un documento coincidente (Documento 1) , pero siempre con TODOS los elementos de la matriz en shapes
:
{ "shapes": [ {"shape": "square", "color": "blue"}, {"shape": "circle", "color": "red"} ] }
Sin embargo, me gustaría obtener el documento (Documento 1) solo con la matriz que contiene color=red
:
{ "shapes": [ {"shape": "circle", "color": "red"} ] }
¿Cómo puedo hacer esto?
El nuevo operador de proyección $elemMatch
MongoDB 2.2 proporciona otra forma de alterar el documento devuelto para que contenga solo el primer elemento de shapes
coincidentes:
db.test.find( {"shapes.color": "red"}, {_id: 0, shapes: {$elemMatch: {color: "red"}}});
Devoluciones:
{"shapes" : [{"shape": "circle", "color": "red"}]}
En 2.2 también puede hacer esto usando el $ projection operator
, donde $
en un nombre de campo de objeto de proyección representa el índice del primer elemento de matriz coincidente del campo de la consulta. A continuación se muestran los mismos resultados que arriba:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
Actualización de MongoDB 3.2
Comenzando con la versión 3.2, puede usar el nuevo operador de agregación $filter
para filtrar una matriz durante la proyección, que tiene el beneficio 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" } ] } ]
El nuevo Framework de Agregación en MongoDB 2.2+ proporciona una alternativa a Map / Reduce. El operador $unwind
se puede usar para separar su matriz de shapes
en una secuencia de documentos que se pueden combinar:
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 }
Otra forma interesante es usar $ redact , que es una de las nuevas funciones de agregación de MongoDB 2.6 . Si está utilizando 2.6, no necesita un $ unwind que podría causarle problemas de rendimiento si tiene matrices 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 según la información almacenada en los documentos” . Por lo tanto, se ejecutará solo dentro del documento . Básicamente escanea la parte superior de su documento hasta la parte inferior, y comprueba si coincide con su condición if
que está en $cond
, si hay coincidencia guardará el contenido ( $$DESCEND
) o eliminará ( $$PRUNE
).
En el ejemplo anterior, el primer $match
devuelve el conjunto de shapes
enteras, y $ redact lo reduce al resultado esperado.
Tenga en cuenta que {$not:"$color"}
es necesario, ya que escaneará también el documento superior, y si $redact
no encuentra un campo de color
en el nivel superior, esto arrojará un valor false
que podría borrar todo el documento que no quiero
Precaución: esta respuesta proporciona una solución que era relevante en ese momento , antes de que se presentaran las nuevas características de MongoDB 2.2 y posteriores. Vea las otras respuestas si está usando una versión más reciente de MongoDB.
El parámetro del selector de campo está limitado a las propiedades completas. No se puede usar para seleccionar parte de una matriz, solo la matriz completa. Intenté usar el operador 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 desde 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()
Mejor puede consultar en el elemento de matriz coincidente usando $slice
Es útil devolver el objeto significativo en una matriz.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
es útil cuando conoce el índice del elemento, pero a veces desea cualquier elemento de matriz que coincida con sus criterios. Puede devolver el elemento coincidente con el operador $
.
La syntax para encontrar en mongodb es
db..find(query, projection);
y la segunda consulta que ha escrito, es decir
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 usa 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 te dará el resultado deseado.
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
SALIDAS
{ "shapes" : [ { "shape" : "circle", "color" : "red" } ] }
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" } ]}
Solo necesita ejecutar la consulta
db.test.find( {"shapes.color": "red"}, {shapes: {$elemMatch: {color: "red"}}});
salida de esta consulta es
{ "_id" : ObjectId("562e7c594c12942f08fe4192"), "shapes" : [ {"shape" : "circle", "color" : "red"} ] }
como esperaba, dará el campo exacto de la matriz que coincide con el color: ‘rojo’.
junto con $ project, será más apropiado que otros elementos coincidentes se apalanquen junto con otros elementos en el documento.
db.test.aggregate( { "$unwind" : "$shapes" }, { "$match" : { "shapes.color": "red" }}, {"$project":{ "_id":1, "item":1 }} )