MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

So finden Sie Dokumente und einzelne Unterdokumente, die bestimmten Kriterien in der MongoDB-Sammlung entsprechen

Was Sie suchen, ist der positionelle $ Operator und "Projektion". Für ein einzelnes Feld müssen Sie das erforderliche Array-Element mit "Punktnotation" abgleichen, für mehr als ein Feld verwenden Sie $elemMatch :

db.products.find(
    { "items.date": "31.08.2014" },
    { "shop": 1, "name":1, "items.$": 1 }
)

Oder das $elemMatch für mehr als ein passendes Feld:

db.products.find(
    { "items":  { 
        "$elemMatch": { "date": "31.08.2014",  "purchasePrice": 1 }
    }},
    { "shop": 1, "name":1, "items.$": 1 }
)

Diese funktionieren jedoch nur für ein einzelnes Array-Element und es wird nur eines zurückgegeben. Wenn Sie möchten, dass mehr als ein Array-Element von Ihren Bedingungen zurückgegeben wird, benötigen Sie eine erweiterte Handhabung mit dem Aggregations-Framework.

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

Oder möglicherweise in kürzerer/schnellerer Form seit MongoDB 2.6, wo Ihr Array von Elementen eindeutige Einträge enthält:

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]
            ]
        }
    }}
])

Oder möglicherweise mit $redact , aber etwas gekünstelt:

db.products.aggregate([
    { "$match": { "items.date": "31.08.2014" } },
    { "$redact": {
        "$cond": [
             { "$eq": [ { "$ifNull": [ "$date", "31.08.2014" ] }, "31.08.2014" ] },
             "$$DESCEND",
             "$$PRUNE"
         ]
    }}
])

Moderner würden Sie $filter verwenden :

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

Und bei mehreren Bedingungen der $elemMatch und $and innerhalb des $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 ] }
        ]
      }
    }
  }}
])

Es kommt also nur darauf an, ob Sie immer erwarten, dass ein einzelnes Element oder mehrere Elemente übereinstimmen, und welcher Ansatz dann besser ist. Aber wo möglich die .find() -Methode ist im Allgemeinen schneller, da ihr der Overhead der anderen Operationen fehlt, die in diesen letzten beiden Formen überhaupt nicht so weit zurückbleiben.

Als Randnotiz werden Ihre "Daten" als Zeichenfolgen dargestellt, was für die Zukunft keine sehr gute Idee ist. Erwägen Sie, diese in das richtige Datum zu ändern Objekttypen, die Ihnen in Zukunft sehr helfen werden.