sql >> Database >  >> NoSQL >> MongoDB

MongoDB-projectie van geneste arrays

2017-update

Zo'n goed gestelde vraag verdient een modern antwoord. Het soort array-filtering dat wordt gevraagd, kan feitelijk worden gedaan in moderne MongoDB-releases na 3.2 via eenvoudig $match en $project pijplijnfasen, net zoals de oorspronkelijke gewone query-operatie bedoeld is.

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

Dit maakt gebruik van de $filter en $map operators om alleen de elementen uit de arrays terug te geven die aan de voorwaarden zouden voldoen, en is veel beter voor de prestaties dan het gebruik van $unwind . Aangezien de pijplijnfasen effectief de structuur van "query" en "project" weerspiegelen van een .find() bediening, de prestaties hier zijn in principe vergelijkbaar met die en de bediening.

Merk op dat waar het de bedoeling is om daadwerkelijk "over documenten" te werken om details samen te brengen uit "meerdere" documenten in plaats van "één", dan zou dit meestal een soort $unwind vereisen om dit te doen, zodat de array-items toegankelijk zijn voor "groeperen".

Dit is eigenlijk de aanpak:

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

Dit is "arrayfiltering" op meer dan een enkele overeenkomst die de basisprojectiemogelijkheden van .find() kan niet.

Je hebt "geneste" arrays, daarom moet je $unwind . verwerken tweemaal. Samen met de andere bewerkingen.



  1. hoe een afbeelding in redis op te slaan met python / PIL

  2. ServiceStack Entiteiten Id veldnaam

  3. mongodb get _id als string in zoekquery

  4. MongoDB / Pymongo-query met Datetime