Récupérez seulement l'élément interrogé dans un tableau d'objets de la collection MongoDB

supposons que vous ayez les documents suivants dans ma collection:

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

Faire requête:

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

ou

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

Returns matched document (Document 1) , mais toujours avec tous les éléments du tableau dans shapes :

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

cependant, je voudrais obtenir le document (Document 1) seulement avec le tableau qui contient color=red :

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

Comment faire?

301
demandé sur Neil Lunn 2010-10-21 11:31:16

10 réponses

MongoDB 2.2's new $elemMatch "une autre façon de modifier le document retourné pour qu'il ne contienne que l'élément premier apparié shapes :

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

Retourne:

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

en 2.2 vous pouvez également le faire en utilisant le $ projection operator , où le $ dans un nom de champ d'objet de projection représente l'index du premier élément du tableau de correspondance de la requête. La suivante renvoie les mêmes résultats que ci-dessus:

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

MongoDB 3.2 Mise À Jour

à partir de la version 3.2, vous pouvez utiliser le nouveau $filter opérateur d'agrégation pour filtrer un tableau pendant la projection, qui a l'avantage d'inclure toutes les correspondances , au lieu de seulement les première.

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
    }}
])

Résultats:

[ 
    {
        "shapes" : [ 
            {
                "shape" : "circle",
                "color" : "red"
            }
        ]
    }
]
324
répondu JohnnyHK 2018-09-25 18:04:43

le nouveau cadre D'agrégation en MongoDB 2.2+ offre une alternative au Map/Reduce. L'opérateur $unwind peut être utilisé pour séparer votre tableau shapes en un flux de documents pouvant être appariés:

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"
  }}
)

résultats dans:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}
91
répondu Stennie 2015-01-28 08:41:36

attention: cette réponse fournit une solution qui était pertinente à l'époque , avant que les nouvelles caractéristiques de MongoDB 2.2 et plus aient été introduites. Voir les autres réponses, si vous utilisez une version plus récente de MongoDB.

le paramètre du sélecteur de champ est limité aux propriétés complètes. Il ne peut pas être utilisé pour sélectionner une partie d'un tableau, seulement le tableau entier. J'ai essayé d'utiliser le $ opérateur de position , mais ça n'a pas marché.

la manière la plus simple est de simplement filtrer les formes dans le client .

si vous vraiment besoin la sortie correcte directement à partir de MongoDB, vous pouvez utiliser une carte-réduire pour filtrer les formes.

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()
28
répondu Niels van der Rest 2016-07-19 18:42:45

un Autre intéressant est d'utiliser $biffer , qui est l'une des nouvelles fonctions d'agrégation de MongoDB 2.6 . Si vous utilisez 2.6, vous n'avez pas besoin d'un $unwind qui pourrait vous causer des problèmes de performance si vous avez de grands tableaux.

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

$redact "restreint le contenu des documents sur la base des informations stockées dans les documents eux-mêmes" . Il ne fonctionnera donc que à l'intérieur du document . Il scanne votre document de haut en bas, et vérifie s'il correspond à votre condition if qui est dans $cond , s'il y a correspondance il conservera le contenu( $$DESCEND ) ou supprimera( $$PRUNE ).

dans l'exemple ci-dessus, d'abord $match renvoie l'ensemble du tableau shapes , et $redact le renvoie vers le bas au résultat attendu.

notez que {$not:"$color"} est nécessaire, car il va scanner le document supérieur aussi, et si $redact ne trouve pas un champ color sur le niveau supérieur cela retournera false qui pourrait rayer le document entier que nous ne voulons pas.

27
répondu anvarik 2014-06-04 08:31:35

mieux vous pouvez interroger dans l'élément de tableau correspondant en utilisant $slice est-il utile de retourner l'objet significatif dans un tableau.

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

$slice est utile lorsque vous connaissez l'index de l'élément, mais parfois vous voulez selon l'élément de tableau correspondant à vos critères. Vous pouvez retourner l'élément correspondant avec l'opérateur $ .

16
répondu Naren Dran 2014-09-18 09:14:37

la syntaxe de find en mongodb est

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

et la deuxième requête que vous avez écrit, c'est

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

dans ce cas, vous avez utilisé l'opérateur $elemMatch dans la partie requête, alors que si vous utilisez cet opérateur dans la partie projection, vous obtiendrez le résultat désiré. Vous pouvez écrire votre requête comme

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

Cela vous donnera le résultat souhaité.

11
répondu Vicky 2014-01-31 08:19:11
 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

sorties

{

   "shapes" : [ 
       {
           "shape" : "circle",
           "color" : "red"
       }
   ]
}
11
répondu Viral Patel 2016-12-07 06:25:02

merci à JohnnyHK .

ici, je veux juste ajouter un usage plus complexe.

// 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
répondu Eddy 2017-05-23 11:54:50

Vous avez juste besoin d'exécuter la requête

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

sortie de cette requête est

{
    "_id" : ObjectId("562e7c594c12942f08fe4192"),
    "shapes" : [ 
        {"shape" : "circle", "color" : "red"}
    ]
}

comme vous vous y attendiez, Il donnera le champ exact du tableau qui correspond à la couleur: "rouge".

5
répondu Vaibhav Patil 2016-11-10 07:50:37

avec $projet, il sera plus approprié d'autres sages éléments correspondants seront matraqués avec d'autres éléments dans le document.

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
2
répondu shakthydoss 2013-02-09 15:45:13