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

MongoDB-projektion af indlejrede arrays

2017-opdatering

Sådan et godt stillet spørgsmål fortjener et moderne svar. Den slags array-filtrering, der anmodes om, kan faktisk udføres i moderne MongoDB-udgivelser efter 3.2 via blot $match og $project pipeline-stadier, ligesom den oprindelige almindelige forespørgselsoperation har til hensigt.

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

Dette gør brug af $filter og $map operatører til kun at returnere elementerne fra arrays, som ville opfylde betingelserne, og det er langt bedre for ydeevnen end at bruge $unwind . Da pipelinestadierne effektivt afspejler strukturen af ​​"forespørgsel" og "projekt" fra en .find() drift, er ydelsen her stort set på niveau med sådan og drift.

Bemærk, at hvor hensigten er faktisk at arbejde "på tværs af dokumenter" for at samle detaljer ud af "flere" dokumenter i stedet for "én", så ville dette normalt kræve en form for $unwind operation for at gøre det, som sådan gør det muligt for array-elementerne at være tilgængelige for "gruppering".

Dette er grundlæggende tilgangen:

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

Dette er "array-filtrering" på mere end et enkelt match, som de grundlæggende projektionsfunktioner i .find() ikke kan gøre.

Du har "indlejrede" arrays, derfor skal du behandle $unwind to gange. Sammen med de andre operationer.



  1. Forstå og administrere diskplads på din MongoDB-server

  2. Hvordan $set Update Operator fungerer i MongoDB

  3. Næste stop – Opbygning af en datapipeline fra Edge til Insight

  4. phpredis på fedora 12