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?
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"
}
]
}
]
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
}
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()
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.
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 $
.
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é.
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
sorties
{
"shapes" : [
{
"shape" : "circle",
"color" : "red"
}
]
}
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"
}
]}
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".
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
}}
)