J'ai une collection de produits. Chaque produit contient un tableau d’articles.
> db.products.find().pretty()
{
"_id" : ObjectId("54023e8bcef998273f36041d"),
"shop" : "shop1",
"name" : "product1",
"items" : [
{
"date" : "01.02.2100",
"purchasePrice" : 1,
"sellingPrice" : 10,
"count" : 15
},
{
"date" : "31.08.2014",
"purchasePrice" : 10,
"sellingPrice" : 1,
"count" : 5
}
]
}
Alors, pouvez-vous s'il vous plaît me donner un conseil, comment je peux interroger MongoDB pour récupérer tous les produits avec un seul élément dont la date est égale à la date que je passe à interroger en paramètre.
Le résultat pour "31.08.2014" doit être:
{
"_id" : ObjectId("54023e8bcef998273f36041d"),
"shop" : "shop1",
"name" : "product1",
"items" : [
{
"date" : "31.08.2014",
"purchasePrice" : 10,
"sellingPrice" : 1,
"count" : 5
}
]
}
Ce que vous recherchez, c’est le $
positionnel opérateur et "projection". Pour un seul champ, vous devez faire correspondre l'élément de tableau requis à l'aide de la "notation par points". Pour plusieurs champs, utilisez $elemMatch
:
db.products.find(
{ "items.date": "31.08.2014" },
{ "shop": 1, "name":1, "items.$": 1 }
)
Ou le $elemMatch
pour plus d'un champ correspondant:
db.products.find(
{ "items": {
"$elemMatch": { "date": "31.08.2014", "purchasePrice": 1 }
}},
{ "shop": 1, "name":1, "items.$": 1 }
)
Celles-ci ne fonctionnent que pour un seul élément de tableau et un seul sera renvoyé. Si vous souhaitez que plusieurs éléments de tableau soient renvoyés à partir de vos conditions, vous avez besoin d'une gestion plus avancée avec la structure d'agrégation.
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$unwind": "$items" },
{ "$match": { "items.date": "31.08.2014" } },
{ "$group": {
"_id": "$_id",
"shop": { "$first": "$shop" },
"name": { "$first": "$name" },
"items": { "$Push": "$items" }
}}
])
Ou peut-être sous une forme plus courte/plus rapide depuis MongoDB 2.6 où votre tableau d’éléments contient des entrées uniques:
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$project": {
"shop": 1,
"name": 1,
"items": {
"$setDifference": [
{ "$map": {
"input": "$items",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.date", "31.08.2014" ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
])
Ou éventuellement avec $redact
, mais un peu artificiel:
db.products.aggregate([
{ "$match": { "items.date": "31.08.2014" } },
{ "$redact": {
"$cond": [
{ "$eq": [ { "$ifNull": [ "$date", "31.08.2014" ] }, "31.08.2014" ] },
"$$DESCEND",
"$$Prune"
]
}}
])
Cela dépend donc simplement de savoir si vous vous attendez toujours à ce qu'un élément unique corresponde à plusieurs éléments, puis quelle approche est la meilleure. Mais lorsque cela est possible, la méthode .find()
sera généralement plus rapide car elle n’aura pas à supporter les frais généraux des autres opérations, qui, dans ces dernières formes, ne sont pas si en retard.
En passant, vos "dates" sont représentées sous forme de chaînes, ce qui n’est pas une très bonne idée pour l’avenir. Pensez à les remplacer par les types d'objet Date appropriés, ce qui vous aidera grandement dans l'avenir.
Mongo prend en charge la notation par points pour les sous-requêtes.
Voir: http://docs.mongodb.org/manual/reference/glossary/#term-dot-notation
En fonction de votre pilote, vous souhaitez quelque chose comme:
db.products.find({"items.date":"31.08.2014"});
Notez que l'attribut est entre guillemets pour la notation par points, même si généralement votre pilote n'en a pas besoin.