Tecniche semplici per eseguire operazioni di join in MongoDB

Simple techniques for performing join operations in MongoDB.

Introduzione

Le persone che lavorano con i database sono molto familiari con gli JOIN. Quando vogliamo recuperare dati da più tabelle, spesso uniamo le tabelle in base alle chiavi primarie e alle chiavi esterne. In questo articolo impareremo le tecniche semplici per eseguire operazioni di join in MongoDB.

Fonte dell'immagine: Pixabay

Il diagramma sopra è una rappresentazione grafica dello schema del database relazionale di un’organizzazione. Quei blocchi sono tabelle che memorizzano tipi specifici di dati (studenti/professori/dipendenti) e le linee e le frecce rappresentano la relazione tra le tabelle utilizzando chiavi comuni. Possiamo eseguire join tra le tabelle in base alle relazioni tra di esse.

Ad esempio: in un’ Organizzazione, ci sono tabelle separate per memorizzare i dati dei dipendenti, dei dipartimenti, dei progetti, ecc., dove i dati vengono memorizzati in modo normalizzato. Per recuperare i dettagli dei dipendenti e in quale dipartimento e progetto stanno lavorando, è necessario eseguire un join tra le tabelle e recuperare i dati necessari.

Di nuovo, in un’ Università, potrebbero esserci tabelle separate per memorizzare i dati degli studenti e dei professori. Per scoprire quali professori insegnano a uno studente specifico, è necessario eseguire un join tra le tabelle.

Obiettivo di apprendimento

In questo tutorial specifico, vedremo come eseguire diverse operazioni di join (Join interni, Join esterni, Join destri e Join sinistri) in MongoDB.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Comprensione dei diversi tipi di operazioni di join comuni

A. SQL e diversi tipi di join

La maggior parte di noi ha conoscenze sui database SQL. Lì eseguiamo spesso quattro tipi principali di join che discuteremo di seguito.

1. Join interno: Nella tabella risultante ci saranno solo righe con chiavi comuni da entrambe le tabelle.

Due tabelle del dataset scolastico - Voti e Posizione
Join interno

Come possiamo vedere, dopo aver eseguito i join interni, abbiamo restituito solo le righe in cui la chiave Numero di matricola è comune a entrambe.

2. Join sinistro esterno: Nella tabella risultante ci saranno tutte le righe della tabella sinistra (chiavi corrispondenti + chiavi non corrispondenti). Pertanto, nella tabella risultante ci saranno solo le righe delle chiavi corrispondenti dalla tabella destra; non è possibile eliminarle tutte le righe in cui le chiavi non corrispondono.

Join sinistro esterno

Dopo aver eseguito il join sinistro, abbiamo tutte le colonne della tabella sinistra. Poiché Posizione di classe per Deepak K. non è presente nella tabella destra, lo riempiamo con null. Come discusso, solo quei record della tabella destra che corrispondono alla chiave Numero di matricola con la tabella sinistra sono presenti nei risultati. Per questo motivo, la tupla (3D5RE,16) dalla tabella destra non è presente nel risultato.

3. Join destro esterno: Opposto del join sinistro esterno. Nella tabella risultante ci saranno tutte le righe della tabella destra e solo le righe con chiavi corrispondenti della tabella sinistra.

Join destro esterno

Come previsto, tutti i record/tuple della tabella di destra sono presenti nel risultato, ma il record (2A3AS, Deepak K., 87) della tabella di sinistra è assente.

4. Full Outer Join: Tutte le righe di entrambe le tabelle (con chiavi corrispondenti e non corrispondenti) saranno presenti nella tabella risultante.

Full Outer Join

Come previsto, abbiamo tutte le tuple da entrambe le tabelle nel nostro risultato. I valori mancanti sono riempiti con null.

B. Breve introduzione a MongoDB

MongoDB è un database NoSQL basato su documenti. I database NoSQL sono migliori per memorizzare dati di grandi dimensioni, non relazionali, non strutturati e con frequente modifica. I seguenti due blog contengono confronti e operazioni in MongoDB.

  • Introduzione a MongoDB
  • Operazioni CRUD in MongoDB

Il database MongoDB è composto da una o più Collezioni. Le collezioni possono essere considerate equivalenti alle tabelle nei database SQL. Ogni collezione è composta da uno o più documenti. Quindi i documenti possono essere considerati come righe o tuple di una tabella nei database SQL. I dati sono memorizzati in formato BSON (Binary JSON) all’interno di MongoDB.

Formato di memorizzazione dei dati di MongoDB

Operazione di join in MongoDB

Ora vediamo come le diverse operazioni di join verranno eseguite sulle collezioni di MongoDB.

Convertire le due tabelle Marks e Rank in collezioni, con ogni tupla al loro interno come documenti delle rispettive collezioni. Memorizzare le collezioni in un database chiamato School all’interno di MongoDB.

Marks
Ranks

Left Outer Join

Codice:

db.Marks.aggregate([{$lookup:{from:"Rank",localField:"Roll No", 
foreignField:"Roll No",as:"Ranks"}}])
Left Outer Join

[Ingrandisci Output]

Come si può vedere, i dettagli del rango dello studente rispettivo sono aggiunti al loro documento. Per Deepak, non ci sono dettagli del rango nella tabella Rank, quindi ovviamente il suo campo Ranks è una lista vuota.

Ora comprendiamo i parametri utilizzati:

  • In questo caso, Marks è la nostra tabella di sinistra.
  • $lookup è la funzione/operatore di aggregazione per eseguire il join tra due collezioni.
  • All’interno del lookup, from indica la collezione con cui desideriamo eseguire il join, ovvero la nostra tabella di destra (collezione). Nel nostro caso, Rank è la nostra collezione di destra.
  • localField indica la chiave dalla collezione di sinistra che verrà confrontata con la chiave dalla collezione di destra per eseguire il join. Se viene trovata una chiave corrispondente nel campo della collezione di destra, il campo risultante (qui Ranks) non è vuoto, altrimenti è vuoto (nel caso di Deepak nel nostro esempio).
  • foreignField è la chiave dalla collezione di destra.
  • as indica il nome del nuovo campo che verrà creato nella tabella/collezione risultante a seguito del join, in cui verranno memorizzati i dettagli dalla tabella di destra (collezione).
  • Nel nostro caso, aggiungiamo il nuovo campo Ranks alla tabella/collezione risultante che contiene i dettagli dei ranghi degli studenti corrispondenti.

Ora una cosa da ricordare, in MongoDB $lookup può eseguire solo join a sinistra e non esiste una sintassi specifica disponibile per altri tipi di join. Quindi dobbiamo derivare gli altri join utilizzando trucchi e operazioni diverse

Right Outer Join

Adesso la join a destra è l’opposto della join a sinistra, dove oltre ai record corrispondenti, i record non corrispondenti della tabella/collection di destra devono essere presenti anche nella tabella/collection risultante.

Ora un modo semplice per farlo è semplicemente alterare la posizione delle due collezioni; quindi la nostra collezione di destra diventa la sinistra e viceversa. Ora, la join conterrà tutte le righe (corrispondenti+non corrispondenti) della nostra tabella di destra.

Codice :

db.Rank.aggregate([{$lookup:{from:"Marks", localField:"Roll No", 
foreignField:"Roll No",  as:"Marks_Students"}}])

[Ingrandisci Output]

Inner Join

Possiamo eseguire efficientemente una join interna con un semplice trucco!!! Faremo una join a sinistra e quindi rimuoveremo tutti quei record in cui il campo as è vuoto. Quindi saremo solo con i record in cui le chiavi sono presenti in entrambe le tabelle (collezioni).

Codice :

db.Rank.aggregate([{$lookup:{from:"Marks", localField:"Roll No", foreignField:"Roll No", 
as: "Marks_Students"}}, {$match:{"Marks_Students":{$ne:[]}}}])
Risultato Inner Join

[Ingrandisci Output]

Come possiamo vedere dal risultato sopra abbiamo solo i record per i quali le chiavi di entrambe le collezioni corrispondevano. Qui {$match:{“Marks_Students”:{$ne:[]}}} indica di corrispondere solo quei record in cui il campo Marks_Students non è []( lista vuota )

Full Outer Join

La join full outer è un po’ complicata, l’ho progettata combinando 3 operazioni. Quindi se sembra confusa a prima vista, ti chiedo di leggerla più volte per una migliore comprensione.

Passo 1: Faremo una join a sinistra di Marks (collezione di sinistra) e Rank (collezione di destra) e aggiungeremo un campo vuoto chiamato Marks a tutti i record risultanti e invieremo/ritorneremo il risultato in una nuova collezione chiamata J2.

Codice:

db.Marks.aggregate([{$lookup:{from:"Rank",localField:"Roll No", foreignField:"Roll No", as:"Rank"}},
{$addFields:{Marks:[]}},{$out:"J2"}])
Risultato del Passo 1

[Ingrandisci Output]

Quindi la nostra nuova collezione assomiglia allo screenshot sopra.

  • {$addFields:{Marks:[]}} -> aggiunge un campo extra Marks a tutti i record.
  • {$out:“J2″}] -> invia/ritorna il risultato in una nuova collezione J2.

Quindi ora come ovvio, il nostro database School contiene 3 collezioni-

Marks, Rank, J2

Collezioni

Passo 2: Eseguiremo il join destro (come discusso in precedenza considerando Rank come la collezione di sinistra) di Marks e Rank e aggiungeremo il risultato alla collezione J2.

Codice:

db.Rank.aggregate([{$lookup:{from:"Marks",localField:"Roll No",foreignField:"Roll No",
 as:"Marks"}},{$merge:"J2"}])
Risultato del Passo 2

[Ingrandisci Output]

Risultato del Passo 2 Contd…

[Ingrandisci Output]

Notare come il sistema aggiunge il nuovo output in fondo al vecchio output del passo 1.

Passo 3:

Terremo/conserviamo solo i record nel nostro risultato aggregato in cui il campo Match è [](vuoto) e scarteremo il resto. In questo modo, rimuoviamo i duplicati e avremo solo nei nostri risultati tutti i campi distinti da entrambe le collezioni/tabelle. (Potresti aver notato nell’output del Passo 2 ci sono duplicati ad esempio: Ci sono due record di Nikita)

Alla fine rimuoveremo il campo Marks vuoto dal risultato dell’aggregazione poiché è un campo vuoto e non serve visualizzarlo. Il suo scopo era quello di rimuovere i duplicati.

Codice:

db.J2.aggregate([{$redact:{$cond:[{$eq:["$Marks",[]]},"$KEEP","$PRUNE"]}},
{$unset:"Marks"}])
Risultato del Passo 3

[Ingrandisci Output]

Quindi alla fine abbiamo il nostro output desiderato. Abbiamo tutti i record che corrispondono in entrambe le tabelle (collezioni) oltre ad altri record presenti in una delle due tabelle (collezioni). (Deepak’s in Marks e il numero di matricola 3D5RE in Rank).

Conclusioni

Abbiamo con successo derivato diversi tipi di join dal join sinistro in MongoDB. Quindi solo per riassumere, c’è solo una sintassi diretta disponibile in MongoDB per eseguire join sinistri. Altri tipi di join devono essere derivati applicando diversi tipi di operazioni e tecniche sul join sinistro. Ad esempio: rimuovere le collezioni con campo as vuoto in caso di join interno, ecc.

Nel processo di derivazione di questi join abbiamo realizzato:

  • È necessaria una buona conoscenza delle query di aggregazione.
  • È necessaria un’osservazione attenta dei risultati intermedi per decidere il passo successivo.
  • Potrebbero esserci altri (e anche migliori) modi per derivare i join.

Se conosci modi migliori sei invitato a condividerli nei commenti.

Punti chiave

  • Solo l’operazione di join sinistro ha una sintassi diretta in MongoDB.
  • Derivare il join destro alterando la posizione delle collezioni nella sintassi.
  • Derivare il join interno eseguendo prima il join sinistro e quindi rimuovendo il campo as vuoto.
  • I join esterni possono essere eseguiti da una serie di operazioni semplici e intelligenti.

Domande frequenti

Riferimenti

  • Documentazione MongoDB su Lookup
  • Documentazione ufficiale MongoDB su $redact
  • MongoDB Inner Join 101: Sintassi ed Esempio Semplificato di Samuel Salimon

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e sono utilizzati a discrezione dell’autore.