Abrufen nur die abgefragten Element in einem Objektarray in MongoDB Sammlung
-
10-10-2019 - |
Frage
Angenommen, Sie die folgenden Dokumente in meiner Sammlung haben:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do-Abfrage:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
oder
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns angepasst Dokument (Dokument 1) , aber immer mit ALLEN Array-Elementen in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
Allerdings würde Ich mag das Dokument erhalten (Dokument 1) nur mit dem Array, das color=red
enthält:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
Wie kann ich das tun?
Lösung
MongoDB 2.2 ist neu $elemMatch
Projektionsoperator bietet eine weitere Möglichkeit zu verändern das zurückgegebene Dokument enthält nur die erste angepasst shapes
Element:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Returns:
{"shapes" : [{"shape": "circle", "color": "red"}]}
In 2.2 können Sie dies auch mit der $ projection operator
, wobei das $
in einem Namen Objektfeld Projektion den Index des Feldes ersten Anpassungsfeldelementes aus der Abfrage darstellt. Der folgende Code gibt die gleichen Ergebnisse wie oben:
db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});
MongoDB 3.2 Update
Beginnend mit der Version 3.2 können Sie den neuen verwenden $filter
Aggregation Betreiber ein Array während der Projektion zu filtern, was den Vorteil mit hat alle Streichhölzer, statt nur die ersten.
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
}}
])
Ergebnisse:
[
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
]
Andere Tipps
Der neue Aggregation Rahmen in MongoDB 2.2+ eine Alternative zur Karte bietet / Verkleinern. Der $unwind
Operator kann verwendet werden, um Ihre shapes
Array in einen Strom von Dokumenten zu trennen, das sein kann, Ergebnisse:
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"
}}
)
Ergebnisse in:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
Achtung: Diese Antwort stellt eine Lösung bereit, die relevant war damals , bevor die neuen Funktionen von MongoDB 2.2 oder höher eingeführt wurde. Die anderen Antworten, wenn Sie eine neuere Version von MongoDB verwenden.
Der Feldwähler Parameter auf komplette Eigenschaften beschränkt. Es kann nicht wählen Teil eines Arrays verwendet werden, nur das gesamte Array. Ich habe versucht, die $ Positions Operator verwenden, aber das hat nicht Arbeit.
Der einfachste Weg ist es, nur die Formen filtert im Client .
Wenn Sie wirklich Notwendigkeit die korrekte Ausgabe direkt von MongoDB, können Sie verwenden Karten reduzieren , um die Formen zu filtern.
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()
Ein weiterer interesing Weg $ Redact zu verwenden ist, das ist eines der neuen Aggregation bietet von MongoDB 2.6 . Wenn Sie 2.6 verwenden, müssen Sie nicht ein $ Abroller, die Sie Performance-Probleme verursachen könnten, wenn Sie große Arrays haben.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
„schränkt die Inhalte der Dokumente anhand der Informationen in den Dokumenten gespeichert sich“ . So wird es läuft nur innerhalb des Dokuments . Es scannt im Grunde das Dokument von oben nach unten, und überprüft, ob es mit Ihrem if
Zustand übereinstimmt, die in $cond
ist, wenn es passen wird entweder den Inhalt halten ($$DESCEND
) oder remove ($$PRUNE
).
In dem obigen Beispiel zuerst $match
kehrt der ganze shapes
Array und $ Redact Streifen es zu dem erwarteten Ergebnis nach unten.
Beachten Sie, dass {$not:"$color"}
notwendig ist, weil es das oberste Dokument auch scannen, und wenn $redact
dies kein color
Feld auf der obersten Ebene finden kehrt false
, die das gesamte Dokument Streifen könnten, die wir nicht wollen.
Besser können Sie abfragen, in passenden Array-Elemente mit $slice
ist es hilfreich, das signifikante Objekt in einem Array zurück.
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
$slice
ist hilfreich, wenn Sie den Index des Elements kennen, aber manchmal Sie wollen
je nachdem, welches Array-Element mit Ihren Kriterien. Sie können das passende Element zurückgeben
mit dem $
Operator.
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
OUTPUT
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
Die Syntax für find in mongodb ist
db.<collection name>.find(query, projection);
und die zweite Abfrage, die Sie geschrieben haben, die
ist db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
in diesen Sie den $elemMatch
Operator in Abfrageteil verwendet haben, während, wenn Sie diesen Operator im Projektionsteil verwenden, dann werden Sie das gewünschte Ergebnis erhalten. Sie können Ihre Abfrage als
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
Dies wird Ihnen das gewünschte Ergebnis.
Dank JohnnyHK .
Hier möchte ich nur einige komplexere Nutzung hinzuzufügen.
// 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"
}
]}
Sie müssen nur Abfrage
laufendb.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
Ausgabe dieser Abfrage ist
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
, wie Sie es erwartet'LL gibt die genaue Feld von Array, dass Streichhölzer Farbe:. ‚Rot‘
zusammen mit $ Projekt wird es geeignetere andere wiesen Anpassungselemente zusammen mit anderen Elementen in dem Dokument geprügelt werden werden.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})