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

Znalezienie dwóch dokumentów w MongoDB, które mają wspólną wartość klucza

Podczas gdy stoję z uwagą, że nie sądzę, że sposób, w jaki formułujesz swoje pytanie, jest w rzeczywistości związany z konkretnym problemem, który masz, postaram się wyjaśnić idiomatyczny sposób SQL w rozwiązaniu typu MongoDB. Stoję na stanowisku, że Twoje rzeczywiste rozwiązanie byłoby inne, ale nie przedstawiłeś nam tego problemu, a jedynie SQL.

Dlatego rozważ następujące dokumenty jako przykładowy zestaw, usuwając pola _id z tej listy dla jasności:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
{ "name" : "z", "type" : "z" }

Gdybyśmy uruchomili prezentowany SQL na tych samych danych, otrzymalibyśmy następujący wynik:

a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c

Widzimy, że 2 dokumenty nie pasują do siebie, a następnie rozpracowujemy logikę operacji SQL. Innym sposobem powiedzenia tego jest „Które dokumenty otrzymały klucz o nazwie „name” do mieć więcej niż jeden możliwa wartość w kluczu "typ".

Biorąc to pod uwagę, przyjmując podejście mongo, możemy wyszukiwać przedmioty, które nie pasują do danego warunku. Tak więc skutecznie odwrotność wyniku:

db.sample.aggregate([

    // Store unique documents grouped by the "name"
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type" 
            }
        } 
    }},

    // Unwind the "set" results
    {$unwind: "$comp"},

    // Push the results back to get the unique count
    // *note* you could not have done this with alongside $addtoSet
    {$group: {
        _id: "$_id",
        comp: {
            $push: { 
                name: "$comp.name",
                type: "$comp.type" 
            }
        },
        count: {$sum: 1} 
    }},

    // Match only what was counted once
    {$match: {count: 1}},

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

    // Clean up to "name" and "type" only
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}

])

Ta operacja przyniesie następujące wyniki:

{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }

Teraz, aby uzyskać ten sam wynik, co zapytanie SQL, wzięlibyśmy te wyniki i skierowali je do innego zapytania:

db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })

Który pojawia się jako ostateczny wynik dopasowania:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }

To zadziała, jednak jedyną rzeczą, która może sprawić, że będzie to niepraktyczne, jest liczba porównywanych dokumentów jest bardzo duży, osiągnęliśmy limit roboczy na kompaktowanie tych wyników do tablicy.

Cierpi również trochę z powodu użycia negatywu w końcowej operacji wyszukiwania, która wymusiłaby skanowanie kolekcji. Ale uczciwie to samo można powiedzieć o zapytaniu SQL, które używa tego samego negatywu założenie.

Edytuj

Oczywiście nie wspomniałem, że jeśli zestaw wyników jest odwrotny i pasujesz więcej wyniki w wykluczonych elementach z agregacji, a następnie po prostu odwróć logikę, aby uzyskać żądane klucze. Po prostu zmień $match w następujący sposób:

{$match: {$gt: 1}}

I to będzie wynik, może nie faktyczne dokumenty, ale to jest wynik. Nie potrzebujesz więc kolejnego zapytania, aby dopasować negatywne przypadki.

I ostatecznie to była moja wina, ponieważ byłem tak skupiony na tłumaczeniu idiomatycznym, że nie czytałem ostatnia linijka w Twoim pytaniu, gdzie zrobić powiedz, że szukasz jednego dokument.

Oczywiście obecnie jeśli ten rozmiar wyniku jest większy niż 16 MB, utkniesz. Przynajmniej do 2.6 wydanie, w którym wyniki operacji agregacji to kursor , więc możesz to powtórzyć jak .find() .

Wprowadzono również w 2.6 to $size operator, który jest używany do znalezienia rozmiaru tablicy w dokumencie. Pomogłoby to usunąć drugi $unwind i $grupa które są używane w celu uzyskania długości zestawu. Zmienia to zapytanie na szybszą formę:

db.sample.aggregate([
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type"
            }
        } 
    }},
    {$project: { 
        comp: 1,
        count: {$size: "$comp"} 
    }},
    {$match: {count: {$gt: 1}}},
    {$unwind: "$comp"},
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])

A MongoDB 2.6.0-rc0 jest obecnie dostępny, jeśli robisz to tylko do użytku osobistego lub programowania/testowania.

Morał historii. Tak, możesz zrób to, Ale czy naprawdę? chcesz lub potrzebujesz zrobić to w ten sposób? Wtedy prawdopodobnie nie, a jeśli zadałeś inne pytanie dotyczące konkretnego przypadku biznesowego, możesz otrzymać inną odpowiedź. Ale z drugiej strony może to być dokładnie odpowiednie dla tego, czego chcesz.

Uwaga

Warto wspomnieć, że gdy spojrzysz na wyniki z SQL, błędnie duplikuje się kilka pozycji ze względu na inne dostępne opcje typu, jeśli nie użyłeś DISTINCT dla tych wartości lub zasadniczo innej grupy. Ale to jest wynik, który został wygenerowany przez ten proces przy użyciu MongoDB.

Dla Aleksandra

To jest wynik agregatu w powłoce z obecnych wersji 2.4.x:

{
    "result" : [
            {
                    "name" : "f",
                    "type" : "e"
            },
            {
                    "name" : "z",
                    "type" : "z"
            }
    ],
    "ok" : 1
}

Zrób to, aby uzyskać zmienną do przekazania jako argument do warunku $nor w drugim znalezisku, tak jak poniżej:

var cond = db.sample.aggregate([ .....

db.sample.find({$nor: cond.result })

I powinieneś otrzymać te same wyniki. W przeciwnym razie skonsultuj się ze swoim kierowcą.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. mongo-go-driver kończy się niepowodzeniem z przekroczeniem limitu czasu wyboru serwera podczas korzystania z MongoDB Atlas

  2. Uruchamianie MongoDB z Ops Manager

  3. importowanie pliku CSV MongoDB z ISODate

  4. Pobieranie losowe z Mongo

  5. Skąd Spring Data wie, w jaki sposób przechowuje repozytorium, za pomocą którego używa się wielu modułów?