sql >> Databáze >  >> NoSQL >> MongoDB

MongoDB projekce vnořených polí

Aktualizace pro rok 2017

Takto dobře položená otázka si zaslouží moderní odpověď. Požadovaný druh filtrování pole lze ve skutečnosti provést v moderních vydáních MongoDB po 3.2 jednoduše pomocí $match a $project fáze potrubí, podobně jako zamýšlí původní operace prostého dotazu.

db.accounts.aggregate([
  { "$match": {
    "email" : "[email protected]",
    "groups": {
      "$elemMatch": { 
        "name": "group1",
        "contacts.localId": { "$in": [ "c1","c3", null ] }
      }
    }
  }},
  { "$addFields": {
    "groups": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$groups",
            "as": "g",
            "in": {
              "name": "$$g.name",
              "contacts": {
                "$filter": {
                  "input": "$$g.contacts",
                  "as": "c",
                  "cond": {
                    "$or": [
                      { "$eq": [ "$$c.localId", "c1" ] },
                      { "$eq": [ "$$c.localId", "c3" ] }
                    ]
                  } 
                }
              }
            }
          }
        },
        "as": "g",
        "cond": {
          "$and": [
            { "$eq": [ "$$g.name", "group1" ] },
            { "$gt": [ { "$size": "$$g.contacts" }, 0 ] }
          ]
        }
      }
    }
  }}
])

To využívá $filter a $map operátory vracejí pouze prvky z polí, které by splňovaly podmínky, a je mnohem lepší pro výkon než použití $unwind . Protože fáze potrubí účinně zrcadlí strukturu „dotazu“ a „projektu“ z .find() provoz, výkon je zde v podstatě na stejné úrovni jako takový a provoz.

Všimněte si, že tam, kde je záměrem skutečně pracovat "napříč dokumenty" chcete-li shromáždit podrobnosti z "více" dokumentů namísto "jedného", pak by to obvykle vyžadovalo nějaký typ $unwind operace, aby tak bylo možné učinit, což umožňuje, aby položky pole byly přístupné pro "seskupení".

Toto je v podstatě přístup:

db.accounts.aggregate([
    // Match the documents by query
    { "$match": {
        "email" : "[email protected]",
        "groups.name": "group1",
        "groups.contacts.localId": { "$in": [ "c1","c3", null ] },
    }},

    // De-normalize nested array
    { "$unwind": "$groups" },
    { "$unwind": "$groups.contacts" },

    // Filter the actual array elements as desired
    { "$match": {
        "groups.name": "group1",
        "groups.contacts.localId": { "$in": [ "c1","c3", null ] },
    }},

    // Group the intermediate result.
    { "$group": {
        "_id": { "email": "$email", "name": "$groups.name" },
        "contacts": { "$push": "$groups.contacts" }
    }},

    // Group the final result
    { "$group": {
        "_id": "$_id.email",
        "groups": { "$push": {
            "name": "$_id.name",
            "contacts": "$contacts" 
        }}
    }}
])

Toto je "filtrování pole" na více než jedné shodě, což jsou základní možnosti projekce .find() nemůže udělat.

Máte "vnořená" pole, proto musíte zpracovat $unwind dvakrát. Spolu s ostatními operacemi.



  1. Chování Redis s více souběžnými programy, které provádějí čtení/odstranění na stejném hash klíči

  2. Jak zachytit výjimku při vytváření instance MongoClient

  3. Mongodb - agregace $push, pokud je podmíněná

  4. arrayFilters v mongodb