É assim que o MongoDB lida com a projeção básica com elementos de matriz. Enquanto você pode fazer algo assim:
Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {
})
E isso apenas retornaria o campo "upvotes" de dentro dos subdocumentos da matriz de comentários para todos os documentos que correspondam à condição e a todos os elementos da matriz, é claro, você não pode combinar isso com uma projeção posicional selecionada usando o
$
posicional
operador. Isso basicamente decorre da "teoria" que geralmente você realmente deseja retornar o array inteiro. Portanto, é assim que sempre funcionou e não é provável que mude em breve. Para obter o que deseja, você precisa dos recursos estendidos para manipulação de documentos oferecidos pelo estrutura de agregação . Isso lhe dá mais controle sobre como os documentos são devolvidos:
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) {
}
);
Ou nas versões modernas do MongoDB desde 2.6 você pode até fazer isso:
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) {
}
)
E isso usa o
$map
e $setDifference
operadores façam uma filtragem "in-line" do conteúdo do array sem primeiro processar um $unwind
palco. Portanto, se você deseja mais controle sobre como o documento é retornado, a estrutura de agregação é a maneira de fazer isso ao trabalhar com documentos incorporados.