So handhabt MongoDB die grundlegende Projektion mit Array-Elementen. Während Sie so etwas tun können:
Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {
})
Und das würde nur das "Upvotes"-Feld aus den Unterdokumenten des Kommentar-Arrays für alle Dokumente zurückgeben, die der Bedingung und allen Array-Elementen entsprechen, Sie können dies natürlich nicht mit einer ausgewählten Positionsprojektion kombinieren, indem Sie positionaler $
Operator. Dies ergibt sich im Grunde aus der "Theorie" das allgemein Sie möchten eigentlich das gesamte Array zurückgeben. So hat es also immer funktioniert und wird sich wahrscheinlich nicht bald ändern.
Um das zu bekommen, was Sie wollen, benötigen Sie die erweiterten Möglichkeiten zur Dokumentenbearbeitung, die von Aggregationsframework . Dadurch haben Sie mehr Kontrolle darüber, wie die Dokumente zurückgegeben werden:
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) {
}
);
Oder in modernen Versionen von MongoDB seit 2.6 können Sie sogar dies tun:
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) {
}
)
Und das verwendet den $map
und $setDifference
Operatoren, um eine "Inline"-Filterung des Array-Inhalts durchzuführen, ohne zuerst ein $unwind
Stufe.
Wenn Sie also mehr Kontrolle darüber haben möchten, wie das Dokument zurückgegeben wird, dann ist das Aggregations-Framework der richtige Weg, wenn Sie mit eingebetteten Dokumenten arbeiten.