MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Projekcja MongoDB zagnieżdżonych tablic

Aktualizacja 2017

Tak dobrze postawione pytanie zasługuje na współczesną odpowiedź. Żądany rodzaj filtrowania tablic można faktycznie wykonać w nowoczesnych wydaniach MongoDB post 3.2 za pomocą prostego $match i $project etapy potoku, podobnie jak pierwotna operacja zwykłego zapytania.

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

Wykorzystuje to $filter i $map operatorów, aby zwracały tylko elementy z tablic, które spełniają warunki, i są znacznie lepsze pod względem wydajności niż użycie $unwind . Ponieważ etapy potoku skutecznie odzwierciedlają strukturę „zapytania” i „projektu” z .find() działanie, wydajność tutaj jest w zasadzie na równi z taką i działaniem.

Zwróć uwagę, że tam, gdzie intencją jest faktycznie praca „w różnych dokumentach” aby zebrać szczegóły z "wielu" dokumentów, a nie "jednego", to zwykle wymagałoby to pewnego rodzaju $unwind w tym celu, umożliwiając tym samym dostęp do elementów tablicy w celu „grupowania”.

Oto podejście:

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

Jest to "filtrowanie tablic" na więcej niż jednym dopasowaniu, które są podstawowymi możliwościami projekcji .find() nie mogę tego zrobić.

Masz "zagnieżdżone" tablice, dlatego musisz przetworzyć $unwind dwa razy. Wraz z innymi operacjami.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Indeks w MongoDB

  2. Pozwól, aby nowy ClusterControl zabezpieczył Twoje wdrożenia MongoDB

  3. Zapytanie z mangustą i daktylami

  4. Jak przekonwertować z ciągu na typ danych?

  5. [Infografika] Porównanie Cassandry i MongoDB