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

MongoDB Zapytanie o przecięcie tablicy zagnieżdżonej

Można to zrobić na kilka sposobów za pomocą struktury agregacji

Tylko prosty zestaw danych, na przykład:

{
    "_id" : ObjectId("538181738d6bd23253654690"),
    "movies": [
        { "_id": 1, "rating": 5 },
        { "_id": 2, "rating": 6 },
        { "_id": 3, "rating": 7 }
    ]
},
{
    "_id" : ObjectId("538181738d6bd23253654691"),
    "movies": [
        { "_id": 1, "rating": 5 },
        { "_id": 4, "rating": 6 },
        { "_id": 2, "rating": 7 }
    ]
},
{
    "_id" : ObjectId("538181738d6bd23253654692"),
    "movies": [
        { "_id": 2, "rating": 5 },
        { "_id": 5, "rating": 6 },
        { "_id": 6, "rating": 7 }
    ]
}

Korzystając z pierwszego „użytkownika” jako przykładu, teraz chcesz sprawdzić, czy którykolwiek z pozostałych dwóch użytkowników ma co najmniej dwa takie same filmy.

W przypadku MongoDB 2.6 i nowszych możesz po prostu użyć $setIntersection operator wraz z $size operator:

db.users.aggregate([

    // Match the possible documents to reduce the working set
    { "$match": {
        "_id": { "$ne": ObjectId("538181738d6bd23253654690") },
        "movies._id": { "$in": [ 1, 2, 3 ] },
        "$and": [
            { "movies": { "$not": { "$size": 1 } } }
        ]
    }},

    // Project a copy of the document if you want to keep more than `_id`
    { "$project": {
        "_id": {
            "_id": "$_id",
            "movies": "$movies"
        },
        "movies": 1,
    }},

    // Unwind the array
    { "$unwind": "$movies" },

    // Build the array back with just `_id` values
    { "$group": {
        "_id": "$_id",
        "movies": { "$push": "$movies._id" }
    }},

    // Find the "set intersection" of the two arrays
    { "$project": {
        "movies": {
            "$size": {
                "$setIntersection": [
                   [ 1, 2, 3 ],
                   "$movies"
                ]
            }
        }
    }},

    // Filter the results to those that actually match
    { "$match": { "movies": { "$gte": 2 } } }

])

Jest to nadal możliwe we wcześniejszych wersjach MongoDB, które nie mają tych operatorów, wystarczy wykonać kilka dodatkowych kroków:

db.users.aggregate([

    // Match the possible documents to reduce the working set
    { "$match": {
        "_id": { "$ne": ObjectId("538181738d6bd23253654690") },
        "movies._id": { "$in": [ 1, 2, 3 ] },
        "$and": [
            { "movies": { "$not": { "$size": 1 } } }
        ]
    }},

    // Project a copy of the document along with the "set" to match
    { "$project": {
        "_id": {
            "_id": "$_id",
            "movies": "$movies"
        },
        "movies": 1,
        "set": { "$cond": [ 1, [ 1, 2, 3 ], 0 ] }
    }},

    // Unwind both those arrays
    { "$unwind": "$movies" },
    { "$unwind": "$set" },

    // Group back the count where both `_id` values are equal
    { "$group": {
        "_id": "$_id",
        "movies": {
           "$sum": {
               "$cond":[
                   { "$eq": [ "$movies._id", "$set" ] },
                   1,
                   0
               ]
           }
        } 
    }},

    // Filter the results to those that actually match
    { "$match": { "movies": { "$gte": 2 } } }
])

Szczegóły

To może być trochę do ogarnięcia, więc możemy przyjrzeć się każdemu etapowi i podzielić je, aby zobaczyć, co robią.

$match :Nie chcesz operować na każdym dokumencie w kolekcji, więc jest to okazja do usunięcia elementów, które prawdopodobnie nie są zgodne, nawet jeśli nadal jest więcej do zrobienia, aby znaleźć dokładne te. Tak więc oczywiste jest wykluczenie tego samego „użytkownika”, a następnie dopasowanie tylko dokumentów, które zawierają co najmniej jeden taki sam film, jaki został znaleziony dla tego „użytkownika”.

Następną rzeczą, która ma sens, jest rozważenie tego, gdy chcesz dopasować n wpisy to tylko dokumenty, które mają tablicę "movies" większą niż n-1 może faktycznie zawierać dopasowania. Użycie $and tutaj wygląda śmiesznie i nie jest to wymagane specjalnie, ale jeśli wymagane dopasowania to 4 wtedy ta część oświadczenia wyglądałaby tak:

        "$and": [
            { "movies": { "$not": { "$size": 1 } } },
            { "movies": { "$not": { "$size": 2 } } },
            { "movies": { "$not": { "$size": 3 } } }
        ]

Więc zasadniczo „wykluczasz” tablice, które nie są wystarczająco długie, aby mieć n mecze. Zauważając, że ten $size operator w formularzu zapytania różni się od $size dla ram agregacji. Nie można na przykład użyć tego z operatorem nierówności, takim jak $gt jego celem jest dokładne dopasowanie żądanego „rozmiaru”. Stąd ten formularz zapytania, aby określić wszystkie możliwe rozmiary, które są mniejsze niż.

$projekt :W tym oświadczeniu jest kilka celów, z których niektóre różnią się w zależności od posiadanej wersji MongoDB. Po pierwsze i opcjonalnie kopia dokumentu jest przechowywana pod _id wartość, aby te pola nie były modyfikowane przez pozostałe kroki. Druga część to zachowanie tablicy „movies” u góry dokumentu jako kopii do następnego etapu.

To, co dzieje się również w wersji przedstawionej dla wersji wcześniejszych niż 2.6, to dodatkowa tablica reprezentująca _id wartości dla „filmów” do dopasowania. Użycie $cond operator tutaj jest po prostu sposobem na stworzenie „dosłownej” reprezentacji tablicy. Co zabawne, MongoDB 2.6 wprowadza operator znany jako $literal aby zrobić dokładnie to bez zabawnego sposobu, w jaki używamy $cond właśnie tutaj.

$odpręż się :Aby zrobić cokolwiek dalej, tablica movies musi zostać rozwinięta, ponieważ w obu przypadkach jest to jedyny sposób na odizolowanie istniejącego _id wartości dla wpisów, które muszą być dopasowane do „zestawu”. Tak więc w wersji wcześniejszej niż 2.6 musisz "rozwinąć" obie obecne tablice.

$grupa :W przypadku MongoDB 2.6 i nowszych po prostu grupujesz z powrotem do tablicy, która zawiera tylko _id wartości filmów z usuniętymi „ocenami”.

Przed wersją 2.6, ponieważ wszystkie wartości są prezentowane „obok siebie” (z dużą ilością duplikatów), porównujesz te dwie wartości, aby sprawdzić, czy są takie same. Gdzie to jest true , to mówi $cond instrukcja operatora, aby zwrócić wartość 1 lub 0 gdzie warunek to false . To jest bezpośrednio przekazywane z powrotem przez $sum aby zsumować liczbę pasujących elementów w tablicy do wymaganego „zestawu”.

$projekt :Inną częścią MongoDB 2.6 i nowszych jest to, że odesłałeś tablicę "filmów" _id wartości, których następnie używasz $setIntersection aby bezpośrednio porównać te tablice. Wynikiem tego jest tablica zawierająca te same elementy, która jest następnie pakowana w $size operatora, aby określić, ile elementów zostało zwróconych w tym pasującym zestawie.

$match :Jest to ostatni etap, który został tutaj zaimplementowany, który polega na dopasowywaniu tylko tych dokumentów, których liczba przecinających się elementów była większa lub równa wymaganej liczbie.

Końcowy

W zasadzie tak to robisz. Wcześniejsze niż 2.6 są nieco bardziej zawiłe i wymagają nieco więcej pamięci ze względu na rozszerzenie, które odbywa się poprzez zduplikowanie każdego elementu tablicy, który znajduje się we wszystkich możliwych wartościach zestawu, ale nadal jest to poprawny sposób na zrobienie tego.

Wszystko, co musisz zrobić, to zastosować to z większym n pasujące wartości, aby spełnić Twoje warunki i oczywiście upewnij się, że oryginalne dopasowanie użytkownika ma wymagany n możliwości. W przeciwnym razie po prostu wygeneruj to na n-1 z długości tablicy „filmów” użytkownika.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Całkowity rozmiar pamięci MongoDB

  2. Replikacja MongoDB ulega awarii i nie chce się ponownie uruchomić

  3. Heroku nie kompiluje wstępnie moich zasobów, aby przygotować moją aplikację do potoku zasobów

  4. MongoDB:migracja schematu, aktualizacja lub wstawienie

  5. Jak napisać bson zapytanie mongo w golangu?