sql >> Databáze >  >> NoSQL >> MongoDB

Jak se připojit ke dvěma dalším kolekcím s podmínkami

Co vám zde chybí, je $lookup vytvoří "pole" ve výstupním poli určeném as v jeho argumentech. Toto je obecný koncept „vztahů“ MongoDB v tom, že „vztah“ mezi dokumenty je reprezentován jako „dílčí vlastnost“, která je „uvnitř“ dokumentu samotného a pro mnohé je buď singulární, nebo „pole“.

Protože MongoDB je "bez schématu", obecný předpoklad $lookup je, že máte na mysli "mnoho" a výsledkem je tedy "vždy" pole. Takže když hledáte "stejný výsledek jako v SQL", musíte $unwind toto pole za $lookup . Jestli je to „jeden“ nebo „mnoho“, nezáleží na tom, protože je to stále „vždy“ pole:

db.getCollection.('tb1').aggregate([
  // Filter conditions from the source collection
  { "$match": { "status": { "$ne": "closed" } }},

  // Do the first join
  { "$lookup": {
    "from": "tb2",
    "localField": "id",
    "foreignField": "profileId",
    "as": "tb2"
  }},

  // $unwind the array to denormalize
  { "$unwind": "$tb2" },

  // Then match on the condtion for tb2
  { "$match": { "tb2.profile_type": "agent" } },

  // join the second additional collection
  { "$lookup": {
    "from": "tb3",
    "localField": "tb2.id",
    "foreignField": "id",
    "as": "tb3"
  }},

  // $unwind again to de-normalize
  { "$unwind": "$tb3" },

  // Now filter the condition on tb3
  { "$match": { "tb3.status": 0 } },

  // Project only wanted fields. In this case, exclude "tb2"
  { "$project": { "tb2": 0 } }
])

Zde si musíte povšimnout dalších věcí, které v překladu chybí:

Sekvence je "důležitá"

Agregační kanály jsou „stručnější“ než SQL. Ve skutečnosti je nejlépe považovat za „sekvenci kroků“ použit na zdroj dat, aby bylo možné data poskládat a transformovat. Nejlepší analogií k tomu jsou „potrubí“ instrukce příkazového řádku, jako například:

ps -ef  | grep mongod | grep -v grep | awk '{ print $1 }'

Kde je "potrubí" | lze považovat za „stupeň potrubí“ v „potrubí“ agregace MongoDB.

Jako takový chceme $match abychom jako první operaci vyfiltrovali věci ze „zdrojové“ kolekce. A to je obecně dobrá praxe, protože z dalších podmínek odstraní všechny dokumenty, které nesplňovaly požadované podmínky. Stejně jako to, co se děje v našem příkladu "příkazového řádku", kde vezmeme "vstup" a "potrubí" do grep k „odstranit“ nebo „filtrovat“.

Na cestách záleží

Další věcí, kterou zde uděláte, je "připojit se" přes $lookup . Výsledkem je "pole" položek z "from" argument kolekce odpovídající dodaným polím pro výstup v "as" „polní cesta“ jako „pole“.

Zde zvolené pojmenování je důležité, protože nyní „dokument“ ze zdrojové kolekce považuje všechny položky ze „spojení“ za existující na dané cestě. Abychom to usnadnili, používám stejný název „kolekce“ jako „připojit“ pro novou „cestu“.

Takže počínaje prvním "připojením" je výstup na "tb2" a to bude obsahovat všechny výsledky z této sbírky. U následující sekvence $unwind je také důležité poznamenat a poté $match o tom, jak MongoDB skutečně zpracovává dotaz.

Na určitých sekvencích "skutečně" záleží

Protože to „vypadá“, existují „tři“ fáze potrubí, které jsou $lookup pak $unwind a poté $match . Ale „ve skutečnosti“ MongoDB opravdu dělá něco jiného, ​​což je demonstrováno ve výstupu { "explain": true } přidáno do .aggregate() příkaz:

    {
        "$lookup" : {
            "from" : "tb2",
            "as" : "tb2",
            "localField" : "id",
            "foreignField" : "profileId",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "profile_type" : {
                    "$eq" : "agent"
                }
            }
        }
    }, 
    {
        "$lookup" : {
            "from" : "tb3",
            "as" : "tb3",
            "localField" : "tb2.id",
            "foreignField" : "id",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "status" : {
                    "$eq" : 0.0
                }
            }
        }
    }, 

Takže kromě prvního bodu použití "sekvence", kde je potřeba vložit $match prohlášení tam, kde jsou potřeba a dělají „nejvíce dobra“, se to ve skutečnosti stává „opravdu důležitým“ s pojmem „spojení“. Zde je třeba poznamenat, že naše sekvence $lookup pak $unwind a poté $match , ve skutečnosti je MongoDB zpracuje pouze jako $lookup fáze, přičemž ostatní operace jsou „srolovány“ do jedné fáze potrubí pro každou z nich.

Toto je důležitý rozdíl oproti jiným způsobům "filtrování" výsledků získaných $lookup . Protože v tomto případě jsou skutečné podmínky „dotazu“ na „připojení“ z $match jsou prováděny na kolekci, aby se připojily "před" jsou výsledky vráceny nadřazené.

To v kombinaci s $unwind ( což je přeloženo do unwinding ), jak je ukázáno výše, MongoDB se ve skutečnosti vypořádává s možností, že „spojení“ by mohlo vést k vytvoření pole obsahu ve zdrojovém dokumentu, což způsobí, že překročí limit 16 MB BSON. To by se stalo pouze v případech, kdy je výsledek, ke kterému se připojuje, velmi velký, ale stejná výhoda je v tom, že je skutečně použit „filtr“, který je v cílové kolekci „před“ vrácením výsledků.

Právě tento druh manipulace nejlépe „koreluje“ se stejným chováním jako SQL JOIN. Je to také nejefektivnější způsob, jak získat výsledky z $lookup tam, kde existují další podmínky, které se vztahují na JOIN, kromě jednoduše „místních“ nebo „cizích“ klíčových hodnot.

Všimněte si také, že další změna chování je z toho, co je v podstatě LEFT JOIN provedené $lookup kde by „zdrojový“ dokument byl vždy zachován bez ohledu na přítomnost odpovídajícího dokumentu v „cílové“ kolekci. Místo toho $unwind přidává k tomu "zahozením" všech výsledků ze "zdroje", které neměly nic shodného s "cílem" podle dalších podmínek v $match .

Ve skutečnosti jsou dokonce předem vyřazeny kvůli implicitnímu preserveNullAndEmptyArrays: false který je zahrnut a zahodil by vše, kde se „místní“ a „cizí“ klíče mezi oběma kolekcemi ani neshodovaly. To je dobrá věc pro tento konkrétní typ dotazu, protože "join" je u těchto hodnot zamýšlen jako "rovná se".

Ukončit

Jak bylo uvedeno dříve, MongoDB obecně zachází se „vztahy“ hodně odlišně od toho, jak byste použili „relační databázi“ nebo RDBMS. Obecným konceptem „relací“ je ve skutečnosti „vložení“ dat, buď jako jedna vlastnost, nebo jako pole.

Můžete si skutečně přát takový výstup, což je také jedním z důvodů, proč bez $unwind zde seřaďte výstup $lookup je vlastně „pole“. Nicméně pomocí $unwind v tomto kontextu je vlastně nejúčinnější věc, kterou lze udělat, stejně jako poskytnout záruku, že „spojená“ data ve skutečnosti nezpůsobí překročení výše uvedeného limitu BSON v důsledku toho „spojení“.

Pokud skutečně chcete pole výstupu, pak nejlepší věcí, kterou můžete udělat, by bylo použít $group fáze potrubí a případně i více fází za účelem „normalizace“ a „vrácení výsledků“ $unwind

  { "$group": {
    "_id": "$_id",
    "tb1_field": { "$first": "$tb1_field" },
    "tb1_another": { "$first": "$tb1_another" },
    "tb3": { "$push": "$tb3" }    
  }}

Kde byste ve skutečnosti pro tento případ uvedli všechna pole, která jste požadovali z "tb1" podle názvů vlastností pomocí $first zachovat pouze "první" výskyt (v podstatě se opakuje s výsledky "tb2" a "tb3" odvinout ) a poté $push "detail" z "tb3" do "pole" reprezentující vztah k "tb1" .

Ale obecná forma agregačního kanálu, jak je uvedena, je přesná reprezentace toho, jak by byly získány výsledky z původního SQL, což je "denormalizovaný" výstup jako výsledek "spojení". Je na vás, zda chcete výsledky znovu „normalizovat“.




  1. Redis cluster failover:slave se nestane masterem

  2. Spusťte R skript při bootování

  3. Push and Set Operations v Same MongoDB Update

  4. Jak mohu zadat dotaz na odlišné hodnoty v Mongoose?