MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

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

Ce que vous recherchez est le positionnel $ opérateur et "projection". Pour un seul champ, vous devez faire correspondre l'élément de tableau requis en utilisant la "notation par points", pour plus d'un champ, 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 }
)

Ceux-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 le cadre 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"
         ]
    }}
])

Plus moderne, vous utiliseriez $filter :

db.products.aggregate([
  { "$match": { "items.date": "31.08.2014" } },
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { "$eq": [ "$$this.date", "31.08.2014" ] }
    }
  }}
])

Et avec plusieurs conditions, le $elemMatch et $and dans le $filter :

db.products.aggregate([
  { "$match": { 
    "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
  }},
  { "$addFields": {
    "items": {
      "input": "$items",
      "cond": { 
        "$and": [
          { "$eq": [ "$$this.date", "31.08.2014" ] },
          { "$eq": [ "$$this.purchasePrice", 1 ] }
        ]
      }
    }
  }}
])

Cela dépend donc simplement de savoir si vous vous attendez toujours à ce qu'un seul élément corresponde ou à plusieurs éléments, puis quelle approche est la meilleure. Mais si possible, le .find() La méthode sera généralement plus rapide car elle n'a pas la surcharge des autres opérations, qui dans ces derniers formulaires ne sont pas du tout en retard.

En remarque, vos "dates" sont représentées sous forme de chaînes, ce qui n'est pas une très bonne idée à l'avenir. Envisagez de les remplacer par la bonne Date types d'objets, ce qui vous aidera grandement à l'avenir.