sql >> Database >  >> NoSQL >> MongoDB

Twee documenten vinden in MongoDB die een sleutelwaarde delen

Hoewel ik achter de opmerkingen sta dat ik denk dat de manier waarop je je vraag formuleert niet echt gerelateerd is aan een specifiek probleem dat je hebt, zal ik op de een of andere manier de idiomatische SQL-manier uitleggen in een MongoDB-type oplossing. Ik blijf erbij dat uw werkelijke oplossing anders zou zijn, maar u heeft ons dat probleem niet voorgelegd, maar alleen SQL.

Beschouw de volgende documenten dus als een voorbeeldset, en verwijder voor de duidelijkheid de _id-velden in deze lijst:

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

Als we de SQL over dezelfde gegevens zouden laten lopen, zouden we dit resultaat krijgen:

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

We kunnen zien dat 2 documenten niet overeenkomen en werken vervolgens de logica van de SQL-bewerking uit. Dus de andere manier om het te zeggen is:"Welke documenten met de sleutel "naam" doen heb meer dan één mogelijke waarde in de sleutel "type".

Gezien het feit dat we, met een mongo-benadering, kunnen zoeken naar de items die niet . zijn overeenkomen met de gegeven voorwaarde. Dus effectief de omgekeerde van het resultaat:

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

])

Deze bewerking levert de volgende resultaten op:

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

Om nu hetzelfde resultaat te krijgen als de SQL-query, zouden we die resultaten nemen en ze naar een andere query kanaliseren:

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

Die arriveert als het uiteindelijke overeenkomende resultaat:

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

Dit zal dus werken, maar het enige dat dit onpraktisch kan maken, is waar het aantal documenten dat vergelijkt erg groot is, hebben we een werklimiet bereikt om die resultaten tot een array te comprimeren.

Het heeft ook wat last van het gebruik van een negatief in de laatste vondstoperatie die een scan van de collectie zou forceren. Maar in alle eerlijkheid zou hetzelfde gezegd kunnen worden van de SQL-query die hetzelfde negatieve . gebruikt uitgangspunt.

Bewerken

Wat ik natuurlijk niet heb genoemd, is dat als de resultatenset andersom gaat en je overeenkomt met meer resulteert in de uitgesloten items van het aggregaat, draai vervolgens de logica om om de gewenste sleutels te krijgen. Verander eenvoudig $match als volgt:

{$match: {$gt: 1}}

En dat zal het resultaat zijn, misschien niet de daadwerkelijke documenten, maar het is een resultaat. U heeft dus geen nieuwe zoekopdracht nodig om de negatieve gevallen te matchen.

En uiteindelijk was dit mijn fout omdat ik zo gefocust was op de idiomatische vertaling dat ik niet gelezen de laatste regel in uw vraag, waar u doe zeg dat je op zoek was naar een document.

Natuurlijk, momenteel als die resultaatgrootte groter is dan 16 MB, zit je vast. Tenminste tot de 2.6 release, waarbij de resultaten van aggregatiebewerkingen een cursor , dus je kunt dat herhalen als een .find() .

Ook geïntroduceerd in 2.6 is de $size operator die wordt gebruikt om de grootte van een array in het document te vinden. Dit zou dus helpen om de tweede $unwind . te verwijderen en $group die worden gebruikt om de lengte van de set te krijgen. Dit verandert de zoekopdracht in een snellere vorm:

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

En MongoDB 2.6.0-rc0 is momenteel beschikbaar als u dit alleen voor persoonlijk gebruik of voor ontwikkeling/testen doet.

Moraal van het verhaal. Ja, u kan doe het, Maar doe je echt willen of nodig om het zo te doen? Dan waarschijnlijk niet, en als u een andere vraag stelt over de specifieke businesscase, krijgt u mogelijk een ander antwoord. Maar nogmaals, dit kan precies goed zijn voor wat je wilt.

Opmerking

Het is de moeite waard om te vermelden dat wanneer u naar de resultaten van de SQL kijkt, deze ten onrechte dupliceren verschillende items vanwege de andere beschikbare type-opties als je geen DISTINCT . hebt gebruikt voor die waarden of in wezen een andere groepering. Maar dat is het resultaat dat werd geproduceerd door dit proces met MongoDB.

Voor Alexander

Dit is de uitvoer van het aggregaat in de shell van de huidige 2.4.x-versies:

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

Dus doe dit om een ​​var als argument door te geven aan de $nor-voorwaarde in de tweede vondst, zoals deze:

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

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

En je zou dezelfde resultaten moeten krijgen. Raadpleeg anders uw chauffeur.



  1. MongoDB retourneert True als document bestaat

  2. Hoe te projecteren of een veld bestaat

  3. meteorquery voor alle documenten met uniek veld

  4. ClusterControl runtime configuratie-opties