Question

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

Do requête:

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

ou

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

retour document trouvé (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) uniquement avec le tableau qui contient color=red:

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

Comment puis-je faire?

Était-ce utile?

La solution

MongoDB 2.2 est nouvel opérateur de projection $elemMatch fournit une autre façon de modifier le document retourné à ne contenir que les first apparié élément shapes:

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

Renvoie:

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

Dans 2.2, vous pouvez aussi le faire en utilisant le $ projection operator , où le $ dans un nom de domaine de l'objet de projection représente l'indice de premier élément de tableau de correspondance de la requête du champ. Les rendements en suivant 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 la nouvelle $filter agrégation opérateur pour filtrer un réseau lors de la projection, ce qui a l'avantage d'inclure tous matches, au lieu de seulement la 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"
            }
        ]
    }
]

Autres conseils

La nouvelle Agrégation Framework dans MongoDB 2.2+ offre une alternative à la carte / Reduce. L'opérateur $unwind peut être utilisé pour séparer votre tableau de shapes dans un flux de documents qui peuvent être matched:

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
}
  

Attention: Cette réponse fournit une solution qui était pertinente à ce moment-là , avant que les nouvelles fonctionnalités de MongoDB 2.2 ou ont été introduites. Voir les autres réponses si vous utilisez une version plus récente de MongoDB.

Le paramètre de sélection de champ est limité à des propriétés complètes. Il ne peut pas être utilisé pour sélectionner une partie d'un tableau, seul le tableau entier. J'ai essayé d'utiliser le $ opérateur de position , mais cela n'a pas travail.

Le plus simple est de filtrer seulement les formes dans le client .

Si vous avez 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()

Une autre façon interesing est d'utiliser expurger $ , qui est l'une des nouvelles fonctionnalités d'agrégation de MongoDB 2.6 . Si vous utilisez 2.6, vous n'avez pas besoin d'un dérouleur $ qui pourrait vous causer des problèmes de performances 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 "limite le contenu des documents sur la base des informations stockées dans les documents eux-mêmes" . Donc, il ne fonctionnera que à l'intérieur du document . Il scanne essentiellement votre document haut en bas, et vérifie si elle correspond à votre état de if qui est en $cond, s'il y a correspondre gardera soit le contenu ($$DESCEND) ou supprimer ($$PRUNE).

Dans l'exemple ci-dessus, les premiers retours $match l'ensemble tableau de shapes et $ expurger bandes vers le bas pour le résultat attendu.

Notez que {$not:"$color"} est nécessaire, car il numérise le document de haut ainsi, et si $redact ne trouve pas un champ de color au niveau supérieur ce sera de retour false qui pourrait dépouiller tout le document que nous ne voulons pas.

Mieux, vous pouvez interroger en correspondant à un élément de tableau en utilisant $slice est-il utile de retourner l'objet important dans un tableau.

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

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

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

Outputs

{

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

La syntaxe de trouver dans MongoDB est

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

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

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

dans ce que vous avez utilisé l'opérateur $elemMatch dans la requête partie, alors que si vous utilisez cet opérateur dans la partie de projection alors vous obtiendrez le résultat souhaité. 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é.

Merci JohnnyHK .

Ici, je veux juste ajouter un peu plus complexe d'utilisation.

// 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"
    }
]}

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

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

résultat de cette requête est

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

comme prévu il va donne le champ exact de tableau qui: Modifie les couleurs. « Rouge »

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

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top