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 une requête:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Ou
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Retourne le document correspondant (Document 1) , mais toujours avec TOUS les éléments de tableau dans shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
Cependant, je voudrais obtenir le document (Document 1) uniquement avec le tableau contenant color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
Comment puis-je faire ceci?
Le nouvel opérateur $elemMatch
de projection de MongoDB 2.2 offre un autre moyen de modifier le document renvoyé afin qu'il ne contienne que l'élément premier correspondant shapes
:
db.test.find(
{"shapes.color": "red"},
{_id: 0, shapes: {$elemMatch: {color: "red"}}});
Résultats:
{"shapes" : [{"shape": "circle", "color": "red"}]}
En 2.2, vous pouvez également le faire en utilisant $ projection operator
, où $
dans un nom de champ d'objet de projection représente l'index du premier élément de tableau correspondant du champ à partir de la requête. Ce qui suit 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 nouvel opérateur $filter
pour filtrer un tableau pendant la projection, ce qui présente l’avantage d’inclure les correspondances tout au lieu du premier.
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"
}
]
}
]
Le nouveau Aggregation Framework dans MongoDB 2.2+ constitue une alternative à Map/Reduce. L'opérateur $unwind
peut être utilisé pour séparer votre tableau shapes
en un flux de documents pouvant correspondre:
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ésulte en:
{
"result" : [
{
"_id" : ObjectId("504425059b7c9fa7ec92beec"),
"shapes" : {
"shape" : "circle",
"color" : "red"
}
}
],
"ok" : 1
}
Attention: Cette réponse fournit une solution pertinente à cette époque avant que les nouvelles fonctionnalités de MongoDB 2.2 et versions ultérieures soient 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, mais uniquement le tableau entier. J'ai essayé d'utiliser l'opérateur $ positional , mais cela n'a pas fonctionné.
Le plus simple consiste à filtrer les formes dans le client.
Si vous avez vraiment besoin la sortie correcte directement à partir de MongoDB, vous pouvez utiliser une carte-réduction 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 méthode intéressante consiste à utiliser $ redact , qui est l’une des nouvelles fonctionnalités d’agrégation de MongoDB 2.6. Si vous utilisez la version 2.6, vous n'avez pas besoin d'un $ $ déroulement, ce 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
"restreint le contenu des documents en fonction des informations stockées dans les documents eux-mêmes". Ainsi, il n’exécutera que à l’intérieur du document. En gros, il analyse 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 conserve le contenu ($$DESCEND
) ou le supprime ($$Prune
).
Dans l'exemple ci-dessus, le premier $match
renvoie le tableau entier shapes
et $ redact le réduit au résultat attendu.
Notez que {$not:"$color"}
est nécessaire, car il numérisera également le document supérieur et si $redact
ne trouve pas de champ color
au niveau supérieur, cela renverra false
qui pourrait supprimer tout le document que nous ne souhaitons pas.
Mieux, vous pouvez interroger un élément de tableau correspondant à l'aide de $slice
. Est-il utile de renvoyer 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 que vous souhaitez parfois quel que soit l'élément de 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})
LES SORTIES
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
La syntaxe de find dans mongodb est
db.<collection name>.find(query, projection);
et la deuxième requête que vous avez écrite, c'est
db.test.find(
{shapes: {"$elemMatch": {color: "red"}}},
{"shapes.color":1})
vous avez utilisé pour cela l'opérateur $elemMatch
dans la partie requête, alors que si vous utilisez cet opérateur dans la partie projection, vous obtenez le résultat souhaité. Vous pouvez écrire votre requête en tant que
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
Cela vous donnera le résultat souhaité.
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"
}
]}
Vous avez juste besoin d'exécuter la requête
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
la sortie de cette requête est
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
comme vous vous y attendiez, le champ exact du tableau qui correspond à la couleur est «rouge».
avec $ project, il sera plus approprié que d’autres éléments de correspondance judicieux soient associés à d’autres éléments du document.
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : {
"shapes.color": "red"
}},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})