web-dev-qa-db-fra.com

Comment trouver un document et un sous-document correspondant aux critères donnés dans la collection MongoDB

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
            }
    ]
}
13
evgeniy44

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.

31
Neil Lunn

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.

0
Jared Alessandroni