MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Come trovare documenti e singoli documenti secondari che corrispondono a determinati criteri nella raccolta MongoDB

Quello che stai cercando è il $ posizionale operatore e "proiezione". Per un singolo campo è necessario abbinare l'elemento dell'array richiesto utilizzando la "notazione del punto", per più di un campo utilizzare $elemMatch :

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

O il $elemMatch per più di un campo corrispondente:

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

Questi funzionano solo per un singolo elemento dell'array e ne verrà restituito solo uno. Se desideri che più di un elemento dell'array venga restituito dalle tue condizioni, hai bisogno di una gestione più avanzata con il framework di aggregazione.

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

O possibilmente in forma più breve/più veloce da MongoDB 2.6 in cui la tua matrice di elementi contiene voci univoche:

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

O possibilmente con $redact , ma un po' forzato:

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

Più moderno, useresti $filter :

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

E con più condizioni, il $elemMatch e $and all'interno del $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 ] }
        ]
      }
    }
  }}
])

Quindi dipende solo dal fatto che ti aspetti sempre che un singolo elemento corrisponda o più elementi, e quindi quale approccio è migliore. Ma dove possibile il .find() il metodo sarà generalmente più veloce poiché manca il sovraccarico delle altre operazioni, che in quelle ultime ai moduli non è affatto in ritardo.

Come nota a margine, le tue "date" sono rappresentate come stringhe, il che non è una buona idea per il futuro. Prendi in considerazione la possibilità di modificarli in Data tipi di oggetti, che ti saranno di grande aiuto in futuro.