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?

War es hilfreich?

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

aufschreiben
     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

laufen
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'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})
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top