it-swarm.com.de

Rufen Sie nur das abgefragte Element in einem Objektarray in der MongoDB-Sammlung ab

Angenommen, Sie haben folgende Dokumente in meiner Sammlung:

{  
   "_id":ObjectId("562e7c594c12942f08fe4192"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"blue"
      },
      {  
         "shape":"circle",
         "color":"red"
      }
   ]
},
{  
   "_id":ObjectId("562e7c594c12942f08fe4193"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"black"
      },
      {  
         "shape":"circle",
         "color":"green"
      }
   ]
}

Fragen Sie ab:

db.test.find({"shapes.color": "red"}, {"shapes.color": 1})

Oder

db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})

Gibt das übereinstimmende Dokument (Dokument 1) zurück, aber immer mit ALL-Feldelementen in shapes:

{ "shapes": 
  [
    {"shape": "square", "color": "blue"},
    {"shape": "circle", "color": "red"}
  ] 
}

Ich möchte jedoch das Dokument (Document 1) nur mit dem Array erhalten, das color=red enthält:

{ "shapes": 
  [
    {"shape": "circle", "color": "red"}
  ] 
}

Wie kann ich das machen?

313
Sebtm

Der neue $elemMatch -Projektionsoperator von MongoDB 2.2 bietet eine weitere Möglichkeit, das zurückgegebene Dokument so zu ändern, dass es nur das first - übereinstimmende shapes-Element enthält:

db.test.find(
    {"shapes.color": "red"}, 
    {_id: 0, shapes: {$elemMatch: {color: "red"}}});

Kehrt zurück:

{"shapes" : [{"shape": "circle", "color": "red"}]}

In 2.2 können Sie dies auch mithilfe von $ projection operator durchführen, wobei der $ in einem Projektionsobjektfeldnamen den Index des ersten übereinstimmenden Feldelements des Felds aus der Abfrage darstellt. Das Folgende liefert die gleichen Ergebnisse wie oben:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2 Update

Ab Version 3.2 können Sie den neuen Aggregatoperator $filter verwenden, um ein Array während der Projektion zu filtern. Dies hat den Vorteil, dass all Matches anstelle des ersten Arrays verwendet werden.

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"
            }
        ]
    }
]
343
JohnnyHK

Das neue Aggregation Framework in MongoDB 2.2+ bietet eine Alternative zu Map/Reduce. Mit dem Operator $unwind können Sie Ihr shapes-Array in einen Dokumentenstrom unterteilen, der abgeglichen werden kann:

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
}
94
Stennie

Achtung: Diese Antwort bietet eine Lösung, die zu diesem Zeitpunkt ​​relevant war, bevor die neuen Funktionen von MongoDB 2.2 und höher eingeführt wurden . Sehen Sie sich die anderen Antworten an, wenn Sie eine neuere Version von MongoDB verwenden.

Der Feldauswahlparameter ist auf vollständige Eigenschaften beschränkt. Es kann nicht verwendet werden, um einen Teil eines Arrays auszuwählen, sondern nur das gesamte Array. Ich habe versucht, den $ positional operator zu verwenden, aber das hat nicht funktioniert.

Am einfachsten ist es, die Formen im Client zu filtern.

Wenn Sie wirklich brauchen die richtige Ausgabe direkt aus MongoDB, können Sie ein Map-Reduce verwenden, 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()
29

Ein weiterer interessanter Weg ist die Verwendung von $ redact , einem der neuen Aggregationsfunktionen von MongoDB 2.6. Wenn Sie 2.6 verwenden, benötigen Sie keine $ Abwicklung, was bei großen Arrays zu Leistungsproblemen führen kann. 

db.test.aggregate([
    { $match: { 
         shapes: { $elemMatch: {color: "red"} } 
    }},
    { $redact : {
         $cond: {
             if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
             then: "$$DESCEND",
             else: "$$Prune"
         }
    }}]);

$redact"beschränkt den Inhalt der Dokumente auf der Grundlage von Informationen, die in den Dokumenten selbst gespeichert sind". Es wird also nur innerhalb des Dokuments ausgeführt. Es scannt Ihr Dokument im Wesentlichen von oben nach unten und prüft, ob es mit Ihrer if-Bedingung in $cond übereinstimmt. Wenn Übereinstimmung vorliegt, wird der Inhalt entweder beibehalten ($$DESCEND) oder entfernt ($$Prune).

Im obigen Beispiel gibt first $match das gesamte shapes-Array zurück, und $ redact entfernt es auf das erwartete Ergebnis. 

Beachten Sie, dass {$not:"$color"} erforderlich ist, da auch das oberste Dokument gescannt wird. Wenn $redact auf der obersten Ebene kein color-Feld findet, wird false zurückgegeben, das möglicherweise das gesamte Dokument entfernt, das wir nicht möchten. 

27
anvarik

Besser Sie können mit $slice in passenden Array-Elementen abfragen, ist es hilfreich, das signifikante Objekt in einem Array zurückzugeben.

db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})

$slice ist hilfreich, wenn Sie den Index des Elements kennen. Manchmal möchten Sie jedoch __. welches Arrayelement Ihren Kriterien entspricht. Sie können das entsprechende Element Mit dem Operator $ zurückgeben.

18
Narendran
 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

AUSGÄNGE

{

   "shapes" : [ 
       {
           "shape" : "circle",
           "color" : "red"
       }
   ]
}
12
Viral Patel

Die Syntax für find in mongodb lautet 

    db.<collection name>.find(query, projection);

und die zweite Abfrage, die Sie geschrieben haben

    db.test.find(
    {shapes: {"$elemMatch": {color: "red"}}}, 
    {"shapes.color":1})

in diesem haben Sie den $elemMatch-Operator im Abfrageteil verwendet. Wenn Sie diesen Operator im Projektionsteil verwenden, erhalten Sie das gewünschte Ergebnis. Sie können Ihre Anfrage als aufschreiben 

     db.users.find(
     {"shapes.color":"red"},
     {_id:0, shapes: {$elemMatch : {color: "red"}}})

Dadurch erhalten Sie das gewünschte Ergebnis.

11
Vicky

Danke an JohnnyHK.

Hier möchte ich nur etwas komplexere Verwendung hinzufü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"
    }
]}
7
Eddy

Sie müssen nur die Abfrage ausführen

db.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 haben, wird das exakte Feld aus dem Feld, das mit der Farbe übereinstimmt, angegeben: "Rot".

5
Vaibhav Patil

zusammen mit $ project wird es sinnvoller sein, andere passende Elemente werden zusammen mit anderen Elementen im Dokument zusammengefügt. 

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
2
shakthydoss
db.test.find( {"shapes.color": "red"}, {_id: 0})
0
Poonam Agrawal