Así es como MongoDB maneja la proyección básica con elementos de matriz. Si bien puedes hacer algo como esto:
Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {
})
Y eso solo devolvería el campo "votos a favor" desde dentro de los subdocumentos de la matriz de comentarios para todos los documentos que coincidan con la condición y todos los elementos de la matriz, por supuesto, no puede combinar esto con una proyección posicional seleccionada usando posicional $
operador. Esto básicamente se deriva de la "teoría" que generalmente en realidad desea devolver toda la matriz. Así es como ha funcionado siempre y no es probable que cambie pronto.
Para obtener lo que desea, necesita las capacidades extendidas para la manipulación de documentos que ofrece marco de agregación . Esto le da más control sobre cómo se devuelven los documentos:
Model.aggregate(
[
// Match the document containing the array element
{ "$match": { "comments._id" : oid } },
// Unwind to "de-normalize" the array content
{ "$unwind": "$comments" },
// Match the specific array element
{ "$match": { "comments._id" : oid } },
// Group back and just return the "upvotes" field
{ "$group": {
"_id": "$_id",
"comments": { "$push": { "upvotes": "$comments.upvotes" } }
}}
],
function(err,docs) {
}
);
O en las versiones modernas de MongoDB desde 2.6, incluso puede hacer esto:
Model.aggregate(
[
{ "$match": { "comments._id" : oid } },
{ "$project": {
"comments": {
"$setDifference": [
{ "$map": {
"input": "$comments",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el._id", oid ] },
{ "upvotes": "$$el.upvotes" },
false
]
}
}},
[false]
]
}}
}}
],
function(err,docs) {
}
)
Y eso usa el $map
y $setDifference
operadores para realizar un filtrado "en línea" del contenido de la matriz sin procesar primero un $unwind
escenario.
Entonces, si desea tener más control sobre cómo se devuelve el documento, entonces el marco de agregación es la forma de hacerlo cuando se trabaja con documentos incrustados.