sql >> Database >  >> NoSQL >> MongoDB

Koppelen en maken van MongoDB-joins met SQL:deel 2

JOIN is een van de belangrijkste onderscheidende kenmerken tussen SQL- en NoSQL-databases. In SQL-databases kunnen we een JOIN uitvoeren tussen twee tabellen binnen dezelfde of verschillende databases. Dit is echter niet het geval voor MongoDB omdat het JOIN-bewerkingen tussen twee verzamelingen in dezelfde database mogelijk maakt.

De manier waarop gegevens in MongoDB worden gepresenteerd, maakt het bijna onmogelijk om ze van de ene verzameling naar de andere te relateren, behalve bij het gebruik van elementaire scriptqueryfuncties. MongoDB de-normaliseert gegevens door gerelateerde items op te slaan in een afzonderlijk document of het relateert gegevens in een ander afzonderlijk document.

Men zou deze gegevens kunnen relateren door handmatige verwijzingen te gebruiken, zoals het veld _id van het ene document dat als referentie in een ander document is opgeslagen. Desalniettemin moet men meerdere query's uitvoeren om enkele vereiste gegevens op te halen, wat het proces een beetje vervelend maakt.

We besluiten daarom om het JOIN-concept te gebruiken dat de relatie van de gegevens vergemakkelijkt. JOIN-bewerking in MongoDB wordt bereikt door het gebruik van $lookup-operator, die werd geïntroduceerd in versie 3.2.

$lookup-operator

Het belangrijkste idee achter het JOIN-concept is om correlatie te krijgen tussen gegevens in de ene verzameling naar de andere. De basissyntaxis van de $lookup-operator is:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

Wat de SQL-kennis betreft, weten we altijd dat het resultaat van een JOIN-bewerking een aparte rij is die alle velden van de lokale en buitenlandse tabel met elkaar verbindt. Voor MongoDB is dit een ander geval omdat de resultaatdocumenten worden toegevoegd als een array van lokale verzameldocumenten. Laten we bijvoorbeeld twee collecties hebben; 'studenten' en 'eenheden'

studenten

{"_id" : 1,"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}
{"_id" : 2,"name" : "Clinton Ariango","age" : 14.0,"grade" : "B","score" : 7.5}
{"_id" : 3,"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5}

Eenheden

{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}
{"_id" : 2,"Maths" : "B","English" : "B","Science" : "A","History" : "B"}
{"_id" : 3,"Maths" : "A","English" : "A","Science" : "A","History" : "A"}

We kunnen de studenteneenheden met respectieve cijfers ophalen met behulp van de $lookup-operator met de JOIN-aanpak .i.e.

db.getCollection('students').aggregate([{
$lookup:
    {
        from: "units",
        localField: "_id",
        foreignField : "_id",
        as: "studentUnits"
    }
}])

Dat geeft ons de onderstaande resultaten:

{"_id" : 1,"name" : "James Washington","age" : 15,"grade" : "A","score" : 10.5,
    "studentUnits" : [{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}]}
{"_id" : 2,"name" : "Clinton Ariango","age" : 14,"grade" : "B","score" : 7.5,
    "studentUnits" : [{"_id" : 2,"Maths" : "B","English" : "B","Science" : "A","History" : "B"}]}
{"_id" : 3,"name" : "Mary Muthoni","age" : 16,"grade" : "A","score" : 11.5,
    "studentUnits" : [{"_id" : 3,"Maths" : "A","English" : "A","Science" : "A","History" : "A"}]}

Zoals eerder vermeld, als we een JOIN doen met behulp van het SQL-concept, worden we teruggestuurd met afzonderlijke documenten in het Studio3T-platform .i.e

SELECT *
  FROM students
    INNER JOIN units
      ON students._id = units._id

Is een equivalent van

db.getCollection("students").aggregate(
    [
        { 
            "$project" : {
                "_id" : NumberInt(0), 
                "students" : "$$ROOT"
            }
        }, 
        { 
            "$lookup" : {
                "localField" : "students._id", 
                "from" : "units", 
                "foreignField" : "_id", 
                "as" : "units"
            }
        }, 
        { 
            "$unwind" : {
                "path" : "$units", 
                "preserveNullAndEmptyArrays" : false
            }
        }
    ]
);

De bovenstaande SQL-query geeft de onderstaande resultaten:

{ "students" : {"_id" : NumberInt(1),"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}, 
    "units" : {"_id" : NumberInt(1),"Maths" : "A","English" : "A","Science" : "A","History" : "B"}}
{ "students" : {"_id" : NumberInt(2), "name" : "Clinton Ariango","age" : 14.0,"grade" : "B","score" : 7.5 }, 
    "units" : {"_id" : NumberInt(2),"Maths" : "B","English" : "B","Science" : "A","History" : "B"}}
{ "students" : {"_id" : NumberInt(3),"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5},
"units" : {"_id" : NumberInt(3),"Maths" : "A","English" : "A","Science" : "A","History" : "A"}}

De duur van de prestatie is uiteraard afhankelijk van de structuur van uw zoekopdracht. Als u bijvoorbeeld veel documenten in de ene verzameling boven de andere heeft, moet u de verzameling samenvoegen met minder documenten en vervolgens opzoeken in de verzameling met meer documenten. Op deze manier is het opzoeken van het gekozen veld uit de verzameling met kleinere documenten vrij optimaal en kost het minder tijd dan het meerdere keren opzoeken voor een gekozen veld in de verzameling met meer documenten. Het is daarom raadzaam om eerst de kleinere collectie te plaatsen.

Voor een relationele database maakt de volgorde van de databases niet uit, aangezien de meeste SQL-interpreters optimizers hebben, die toegang hebben tot extra informatie om te beslissen welke de eerste moet zijn.

In het geval van MongoDB zullen we een index moeten gebruiken om de JOIN-bewerking te vergemakkelijken. We weten allemaal dat alle MongoDB-documenten een _id-sleutel hebben die voor een relationele DBM als de primaire sleutel kan worden beschouwd. Een index biedt een betere kans om de hoeveelheid gegevens die moet worden geopend te verminderen, naast het ondersteunen van de bewerking bij gebruik in de externe sleutel $lookup.

Om een ​​index te gebruiken in de aggregatiepijplijn, moeten we ervoor zorgen dat de $match in de eerste fase wordt uitgevoerd om documenten uit te filteren die niet aan de criteria voldoen. Als we bijvoorbeeld het resultaat voor de student willen ophalen met een veldwaarde _id gelijk aan 1:

select * 
from students 
  INNER JOIN units 
    ON students._id = units._id 
      WHERE students._id = 1;

De equivalente MongoDB-code die u in dit geval krijgt, is:

db.getCollection("students").aggregate(
[{"$project" : { "_id" : NumberInt(0), "students" : "$$ROOT" }}, 
     {  "$lookup" : {"localField" : "students._id", "from" : "units",  "foreignField" : "_id",  "as" : "units"} }, 
     { "$unwind" : { "path" : "$units","preserveNullAndEmptyArrays" : false } }, 
      { "$match" : {"students._id" : NumberLong(1) }}
    ]);

Het geretourneerde resultaat voor de bovenstaande zoekopdracht is:

{"_id" : 1,"name" : "James Washington","age" : 15,"grade" : "A","score" : 10.5,
    "studentUnits" : [{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}]}

Als we de $match-fase niet of liever niet in de eerste fase gebruiken, als we het controleren met de explain-functie, krijgen we de COLLSCAN-fase ook inbegrepen. Het uitvoeren van een COLLSCAN voor een grote set documenten kost over het algemeen veel tijd. We besluiten daarbij om een ​​indexveld te gebruiken dat in de functie explain alleen de IXSCAN-fase omvat. Dit laatste heeft een voordeel aangezien we een index in de documenten controleren en niet alle documenten doorbladeren; het zal niet lang duren om de resultaten terug te sturen. Mogelijk hebt u een andere gegevensstructuur, zoals:

{    "_id" : NumberInt(1), 
    "grades" : {"Maths" : "A", "English" : "A",  "Science" : "A", "History" : "B"
    }
}

Misschien willen we de cijfers teruggeven als verschillende entiteiten in een array in plaats van een heel ingebed cijferveld.

Nadat we de bovenstaande SQL-query hebben geschreven, moeten we de resulterende MongoDB-code wijzigen. Klik hiervoor op het kopieerpictogram rechts zoals hieronder om de aggregatiecode te kopiëren:

Ga vervolgens naar het aggregatietabblad en in het gepresenteerde paneel is er een plakpictogram, klik erop om de code te plakken.

Klik op de $match-rij en vervolgens op de groene pijl-omhoog om de fase naar boven te verplaatsen als de eerste fase. U moet echter eerst een index in uw verzameling maken, zoals:

db.students.createIndex(
   { _id: 1 },
   { name: studentId }
)

U krijgt het onderstaande codevoorbeeld:

db.getCollection("students").aggregate(
    [{ "$match" : {"_id" : 1.0}},
  { "$project" : {"_id" : NumberInt(0),"students" : "$$ROOT"}}, 
      { "$lookup" : {"localField" : "students._id","from" : "units","foreignField" : "_id","as" : "units"}}, 
      { "$unwind" : {"path" : "$units", "preserveNullAndEmptyArrays" : false}}
    ]
Multiplenines Word een MongoDB DBA - MongoDB naar productie brengenLeer over wat u moet weten om MongoDB gratis te implementeren, bewaken, beheren en schalen

Met deze code krijgen we het onderstaande resultaat:

{ "students" : {"_id" : NumberInt(1), "name" : "James Washington","age" : 15.0,"grade" : "A", "score" : 10.5}, 
    "units" : {"_id" : NumberInt(1), "grades" : {"Maths" : "A", "English" : "A", "Science" : "A",  "History" : "B"}}}

Maar het enige dat we nodig hebben, is dat de cijfers een afzonderlijke documententiteit zijn in het geretourneerde document en niet zoals in het bovenstaande voorbeeld. We zullen daarom de $addfields-fase toevoegen, vandaar de onderstaande code.

db.getCollection("students").aggregate(
    [{ "$match" : {"_id" : 1.0}},
  { "$project" : {"_id" : NumberInt(0),"students" : "$$ROOT"}}, 
      { "$lookup" : {"localField" : "students._id","from" : "units","foreignField" : "_id","as" : "units"}}, 
      { "$unwind" : {"path" : "$units", "preserveNullAndEmptyArrays" : false}},
      { "$addFields" : {"units" : "$units.grades"} }]

De resulterende documenten zijn dan:

{
"students" : {"_id" : NumberInt(1), "name" : "James Washington", "grade" : "A","score" : 10.5}, 
     "units" : {"Maths" : "A", "English" : "A",  "Science" : "A", "History" : "B"}
}

De geretourneerde gegevens zijn heel netjes, omdat we ingesloten documenten uit de verzameling van de eenheden hebben verwijderd als een apart veld.

In onze volgende zelfstudie gaan we query's met verschillende joins onderzoeken.


  1. Ingesloten document in array vervangen in MongoDB

  2. Hoe wissel ik tussen redis-database?

  3. Mongo converteert alle numerieke velden die zijn opgeslagen als string

  4. MongooseError [MongooseServerSelectionError]:verbinding <monitor> naar 52.6.250.237:27017 gesloten