Questo è il modo in cui MongoDB gestisce la proiezione di base con elementi di array. Mentre puoi fare qualcosa del genere:
Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {
})
E ciò restituirebbe semplicemente il campo "voti positivi" dall'interno dei documenti secondari dell'array dei commenti per tutti i documenti che soddisfano la condizione e tutti gli elementi dell'array, ovviamente, non puoi combinarlo con una proiezione posizionale selezionata usando posizionale $
operatore. Questo fondamentalmente deriva dalla "teoria" che generalmente in realtà vuoi restituire l'intero array. Quindi è così che ha sempre funzionato e non è probabile che cambi presto.
Per ottenere ciò che desideri, hai bisogno delle funzionalità estese per la manipolazione dei documenti offerte da struttura di aggregazione . Questo ti dà un maggiore controllo su come vengono restituiti i documenti:
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) {
}
);
Oppure nelle versioni moderne di MongoDB dalla 2.6 puoi anche farlo:
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 questo utilizza $map
e $setDifference
operatori per eseguire un filtraggio "in linea" del contenuto dell'array senza prima elaborare un $unwind
fase.
Quindi, se desideri un maggiore controllo su come viene restituito il documento, il framework di aggregazione è il modo per farlo quando lavori con i documenti incorporati.