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

At finde to dokumenter i MongoDB, der deler en nøgleværdi

Mens jeg står ved kommentarer om, at jeg ikke tror, ​​at den måde, du formulerer dit spørgsmål på, faktisk er relateret til et specifikt problem, du har, vil jeg gå et stykke vej for at forklare den idiomatiske SQL-måde i en MongoDB-type løsning. Jeg tror på, at din faktiske løsning ville være anderledes, men du har ikke præsenteret os for det problem, men kun SQL.

Så overvej følgende dokumenter som et eksempelsæt, fjern _id-felter i denne liste for klarhedens skyld:

{ "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" } 

Hvis vi kørte SQL præsenteret over de samme data, ville vi få dette resultat:

a|ba|ca|cb|cb|ab|aa|bb|c 

Vi kan se, at 2 dokumenter ikke stemmer overens, og så finder vi logikken i SQL-operationen. Så den anden måde at sige det på er "Hvilke dokumenter med nøglen "navn" gør har mere end én mulig værdi i nøglen "type".

Da vi ved at tage en mongo-tilgang kan forespørge efter de varer, der ikke matche den givne betingelse. Så effektivt omvendt af resultatet:

db.sample.aggregate([ // Gem unikke dokumenter grupperet efter "navn" {$group:{ _id:"$name", comp:{ $addToSet:{ name:"$name", type:"$type" } } }}, // Slap af "set" resultaterne {$unwind:"$comp"}, // Skub resultaterne tilbage for at få det unikke antal // *bemærk* du ikke kunne have gjort dette sammen med $addtoSet {$group:{ _id:"$_id", comp:{ $push:{ name:"$comp.name", type:"$comp.type" } }, count:{$sum:1} }}, // Match kun det, der blev talt én gang {$match:{count:1}}, // Slap af arrayet {$unwind:"$comp"}, // Ryd op til "name" og "type " kun {$project:{ _id:0, navn:"$comp.name", type:"$comp.type"}}]) 

Denne operation vil give resultaterne:

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

For nu at få det samme resultat som SQL-forespørgslen ville vi tage disse resultater og kanalisere dem til en anden forespørgsel:

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

Som kommer som det endelige matchende resultat:

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

Så dette vil fungere, men den ene ting, der kan gøre dette upraktisk, er hvor antallet af dokumenter, der sammenlignes er meget stor, rammer vi en arbejdsgrænse for at komprimere disse resultater ned til en matrix.

Det lider også lidt under brugen af ​​en negativ i den endelige fundoperation, som ville fremtvinge en scanning af samlingen. Men i al retfærdighed kunne det samme siges om SQL-forespørgslen, der bruger det samme negativ forudsætning.

Rediger

Det, jeg selvfølgelig ikke nævnte, er, at hvis resultatsættet går den anden vej rundt, og du matcher mere resulterer i de udelukkede elementer fra aggregatet, så skal du bare vende logikken for at få de nøgler, du ønsker. Du skal blot ændre $match som følger:

{$match:{$gt:1}} 

Og det bliver resultatet, måske ikke de faktiske dokumenter, men det er et resultat. Så du behøver ikke en anden forespørgsel for at matche de negative tilfælde.

Og i sidste ende var dette min skyld, fordi jeg var så fokuseret på den idiomatiske oversættelse, at jeg ikke læste den sidste linje i dit spørgsmål, hvor skal du gøre sige, at du ledte efter en dokument.

Selvfølgelig i øjeblikket hvis den resultatstørrelse er større end 16 MB, sidder du fast. I hvert fald indtil 2.6 udgivelse, hvor resultaterne af aggregeringsoperationer er en markør , så du kan gentage det som en .find() .

Også introduceret i 2.6 er $size operator, som bruges til at finde størrelsen af ​​et array i dokumentet. Så dette ville hjælpe med at fjerne den anden $unwind og $group der bruges for at få sættets længde. Dette ændrer forespørgslen til en hurtigere 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, navn:"$comp.name", type:"$comp.type"}}]) 

Og MongoDB 2.6.0-rc0 er i øjeblikket tilgængelig, hvis du kun gør dette til personlig brug eller udvikling/testning.

Historiens morale. Ja, du kan gør det, Men gør du virkelig ønsker eller behov at gøre det på den måde? Så nok ikke, og hvis du stillede et andet spørgsmål om den konkrete business case, kan du få et andet svar. Men igen kan dette være det helt rigtige til det, du ønsker.

Bemærk

Værd at nævne, at når du ser på resultaterne fra SQL'en, vil det fejlagtigt duplikeres flere elementer på grund af de andre tilgængelige typeindstillinger, hvis du ikke brugte en DISTINCT for disse værdier eller i det væsentlige en anden gruppering. Men det er resultatet, der blev produceret af denne proces ved hjælp af MongoDB.

Til Alexander

Dette er output fra aggregatet i shellen fra nuværende 2.4.x-versioner:

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

Så gør dette for at få en var til at passere som argumentet til $nor-betingelsen i det andet fund, sådan her:

var cond =db.sample.aggregate([ .....db.sample.find({$nor:cond.result }) 

Og du bør få de samme resultater. Ellers kontakt din chauffør.



  1. WiredTiger og opdateringer på stedet

  2. moveChunk kunne ikke engagere TO-shard i dataoverførslen:kan ikke acceptere nye bidder, fordi

  3. Skal 'StackExchange.Redis.ConnectionMultiplexer' være 'AddSingleton' eller 'AddScope' i .NET Core afhængighedsinjektion?

  4. Vælg sidste værdi af array i underdokument