Hoe voer ik het SQL Join-equivalent uit in MongoDB?

Hoe voer ik het SQL Join-equivalent uit in MongoDB?

Stel bijvoorbeeld dat je twee verzamelingen hebt (gebruikers en opmerkingen) en dat ik alle opmerkingen wil ophalen met pid=444 samen met de gebruikersinformatie voor elk.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }
users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

Is er een manier om alle opmerkingen met een bepaald veld (bijv. …find({pid:444}) ) en de gebruikersinformatie die bij elke opmerking hoort in één keer op te halen?

Op dit moment krijg ik eerst de opmerkingen die overeenkomen met mijn criteria, dan zoek ik alle uid’s in die resultatenset uit, haal ik de gebruikersobjecten en voeg ik ze samen met de resultaten van de opmerking. Het lijkt erop dat ik het verkeerd doe.


Antwoord 1, autoriteit 100%

Vanaf Mongo 3.2 zijn de antwoorden op deze vraag meestal niet meer correct. De nieuwe $lookup-operator die aan de aggregatiepijplijn is toegevoegd, is in wezen identiek aan een left outer join:

https://docs.mongodb.org/master /reference/operator/aggregation/lookup/#pipe._S_lookup

Uit de documenten:

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

Natuurlijk is Mongo geeneen relationele database, en de ontwikkelaars zijn voorzichtig met het aanbevelen van specifieke use-cases voor $lookup, maar in ieder geval vanaf 3.2 is joinen nu mogelijk met MongoDB.

p>


Antwoord 2, autoriteit 44%

We kunnen alle gegevens in slechts één verzameling samenvoegen/samenvoegen met een eenvoudige functie in een paar regels met behulp van de mongodb-clientconsole, en nu zouden we in staat kunnen zijn om de gewenste query uit te voeren.
Hieronder een compleet voorbeeld,

.- Auteurs:

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

.- Categorieën:

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

.- Boeken

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

.- Boeken uitlenen

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

.- De magie:

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

.- Krijg de nieuwe verzamelgegevens:

db.booksReloaded.find().pretty()

.- Respons 🙂

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

Ik hoop dat deze regels je kunnen helpen.


Antwoord 3, autoriteit 44%

Deze pagina op de officiële mongodb-site behandelt exactdeze vraag:

https: //mongodb-documentation.readthedocs.io/en/latest/ecosystem/tutorial/model-data-for-ruby-on-rails.html

Als we onze lijst met verhalen weergeven, moeten we de naam tonen van de gebruiker die het verhaal heeft gepost. Als we een relationele database zouden gebruiken, zouden we een join kunnen uitvoeren op gebruikers en winkels en al onze objecten in een enkele query kunnen krijgen. Maar MongoDB ondersteunt geen joins en vereist daarom soms een beetje denormalisatie. Hier betekent dit het cachen van het kenmerk ‘gebruikersnaam’.

Relationele puristen voelen zich misschien al ongemakkelijk, alsof we een universele wet overtreden. Maar laten we in gedachten houden dat MongoDB-verzamelingen niet gelijk zijn aan relationele tabellen; elk dient een uniek ontwerpdoel. Een genormaliseerde tabel biedt een atomair, geïsoleerd stuk gegevens. Een document vertegenwoordigt echter meer een object als geheel. In het geval van een sociale nieuwssite kan worden gesteld dat een gebruikersnaam intrinsiek is aan het verhaal dat wordt gepost.


Antwoord 4, autoriteit 11%

Je moet het doen zoals je hebt beschreven. MongoDB is een niet-relationele database en ondersteunt geen joins.


Antwoord 5, autoriteit 7%

Met de juiste combinatie van $lookup, $projecten $match, kun je meerdere tabellen samenvoegen op meerdere parameters. Dit komt omdat ze meerdere keren kunnen worden geketend.

Stel dat we het volgende willen doen (referentie)

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID = R.ID AND S.MID = R.MID  
WHERE R.TIM > 0 AND S.MOB IS NOT NULL

Stap 1: Koppel alle tabellen

u kunt zoveel tabellen opzoeken als u wilt.

$lookup– één voor elke tabel in zoekopdracht

$unwind– denormaliseert gegevens correct, anders zou het in arrays worden verpakt

Python-code..

db.LeftTable.aggregate([
                        # connect all tables
                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}
                        ])

Stap 2: Definieer alle voorwaarden

$project: definieer hier alle voorwaardelijke uitspraken, plus alle variabelen die u wilt selecteren.

Python-code..

db.LeftTable.aggregate([
                        # connect all tables
                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},
                        # define conditionals + variables
                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

Stap 3: Doe mee met alle voorwaarden

$match– sluit je aan bij alle voorwaarden met OR of AND etc. Er kunnen veelvouden hiervan zijn.

$project: definieer alle voorwaarden

Complete Python-code..

db.LeftTable.aggregate([
                        # connect all tables
                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},
                        # define conditionals + variables
                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},
                        # join all conditionals
                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},
                        # undefine conditionals
                        {"$project": {
                          "midEq": 0
                        }}
                        ])

Vrijwel elke combinatie van tabellen, conditionals en joins kan op deze manier worden gedaan.


Antwoord 6, autoriteit 6%

Zoals anderen hebben opgemerkt, probeer je een relationele database te maken van geen relationele database die je echt niet wilt doen, maar hoe dan ook, als je een zaak hebt die je hier moet doen, is een oplossing die je kunt gebruiken . We doen eerst een foreach-vondst op verzameling A (of in uw casusgebruikers) en dan krijgen we elk item als een object, dan gebruiken we Object Property (in uw case-uid) om in onze tweede collectie te zoeken (in uw casu’s) als wij kan het vinden, dan hebben we een match en we kunnen er iets mee afdrukken of doen.
Ik hoop dat dit je helpt en veel geluk 🙂

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});

Antwoord 7, Autoriteit 5%

Hier is een voorbeeld van een * * acteurs en films collecties:

https://github.com/mongodb/cookbook /blob/master/content/patterns/pivot.txt

Het maakt gebruik van .mapReduce()methode

* Word lid – een alternatief voor deelname aan documentgerichte databases


Antwoord 8, Autoriteit 4%

U kunt deelnemen aan twee collectie in Mongo door opzoeking te gebruiken die wordt aangeboden in 3.2-versie. In uw geval zou de query

zijn

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

of je kunt ook lid worden met betrekking tot gebruikers, dan zal er een kleine verandering zijn zoals hieronder aangegeven.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

Het werkt precies hetzelfde als left en right join in SQL.


Antwoord 9, autoriteit 4%

$lookup (aggregatie)

Voert een left outer join uit op een niet-gecodeerde verzameling in dezelfde database om documenten uit de “samengevoegde” verzameling te filteren voor verwerking. Aan elk invoerdocument voegt de $lookup-fase een nieuw arrayveld toe waarvan de elementen de overeenkomende documenten zijn uit de “samengevoegde” verzameling. De $lookup-fase geeft deze hervormde documenten door aan de volgende fase.
De $lookup-fase heeft de volgende syntaxis:

Gelijkheidswedstrijd

Om een gelijkheidsovereenkomst uit te voeren tussen een veld uit de invoerdocumenten met een veld uit de documenten van de “joined” collectie, heeft de $lookup-fase de volgende syntaxis:

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

De bewerking komt overeen met de volgende pseudo-SQL-instructie:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT <documents as determined from the pipeline>
                               FROM <collection to join>
                               WHERE <pipeline> );

mongo-url


Antwoord 10, Autoriteit 3%

Het hangt af van wat je probeert te doen.

U hebt momenteel het ingesteld als een genormaliseerde database, wat prima is, en de manier waarop u het doet, is passend.

Er zijn echter andere manieren om het te doen.

U kunt een berichtenverzameling hebben die opmerkingen heeft ingediend voor elke post met verwijzingen naar de gebruikers die u kunt vragen om te krijgen. U kunt de naam van de gebruiker opslaan bij de opmerkingen, u kunt ze allemaal in één document opslaan.

Het ding met NOSQL is het ontworpen voor flexibele schema’s en zeer snelle lezing en schrijven. In een typische Big Data Farm is de database het grootste knelpunt, heb je minder databasemotoren dan je toepassing en front-eindservers … ze zijn duurder maar krachtiger, ook de ruimte van de harde schijf is erg goedkoop relatief. Normalisatie komt uit het concept van proberen om ruimte te besparen, maar het komt met een kosten bij het maken van uw databases die ingewikkeld worden uitgevoerd en het verifiëren van de integriteit van relaties, het uitvoeren van cascading-operaties. Delen van welke de ontwikkelaars sommige hoofdpijn bespaart als ze de database correct hebben ontworpen.

Met NOSQL is als u dat ontslag en opslagruimte accepteert vanwege hun kosten (zowel in processortijd nodig om updates en hardeskosten te doen om extra gegevens op te slaan), is Denormalizing geen probleem (voor ingebed Arrays die honderdduizenden items worden, kan het een uitvoeringsprobleem zijn, maar meestal is dat geen probleem). Bovendien hebt u verschillende applicatie- en front-eindservers voor elk databasecluster. Laat ze de zware tillen van de joins doen en laat de databaseservers zich vasthouden aan lezen en schrijven.

TL;DR: Wat je doet is prima, en er zijn andere manieren om het te doen. Bekijk de datamodelpatronen van de mongodb-documentatie voor enkele geweldige voorbeelden. http://docs.mongodb.org/manual/data-modeling/


Antwoord 11, autoriteit 3%

Er is een specificatie die door veel stuurprogramma’s wordt ondersteund en die DBref wordt genoemd.

DBref is een meer formele specificatie voor het maken van referenties tussen documenten. DBRef’s bevatten (over het algemeen) een collectienaam en een object-ID. De meeste ontwikkelaars gebruiken alleen DBref’s als de verzameling van het ene document naar het andere kan veranderen. Als uw verzameling waarnaar wordt verwezen altijd hetzelfde is, zijn de hierboven beschreven handmatige verwijzingen efficiënter.

Genomen uit MongoDB-documentatie: Gegevensmodellen > Gegevensmodel referentie >
Databasereferenties


Antwoord 12, autoriteit 3%

Vóór 3.2.6ondersteunde Mongodb geen join-query’s zoals mysql. onderstaande oplossing die voor u werkt.

db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])

Antwoord 13

U kunt SQL-query’s uitvoeren, inclusief deelnemen aan MongoDB met mongo_fdwvan Postgres.


Antwoord 14

MongoDB toestaat niet bij joins, maar u kunt plug-ins gebruiken om dat aan te pakken. Controleer de plug-in van de Mongo-Join. Het is de beste en ik heb het al gebruikt. U kunt het installeren met NPM rechtstreeks zoals deze npm install mongo-join. U kunt de volledige documentatie met voorbeelden .

(++) Echt handig hulpmiddel wanneer we lid worden (n) collecties

(-) We kunnen voorwaarden toepassen op het hoogste niveau van de query

voorbeeld

var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server;
db.open(function (err, Database) {
    Database.collection('Appoint', function (err, Appoints) {
        /* we can put conditions just on the top level */
        Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date },
            full_date :{ $lte: end_date }}, function (err, cursor) {
            var join = new Join(Database).on({
                field: '_id_Doctor', // <- field in Appoints document
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            }).on({
                field: '_id_Patient', // <- field in Appoints doc
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            })
            join.toArray(cursor, function (err, joinedDocs) {
                /* do what ever you want here */
                /* you can fetch the table and apply your own conditions */
                .....
                .....
                .....
                resp.status(200);
                resp.json({
                    "status": 200,
                    "message": "success",
                    "Appoints_Range": joinedDocs,
                });
                return resp;
            });
    });

Antwoord 15

Je kunt het doen met behulp van de aggregatiepijplijn, maar het is lastig om het zelf te schrijven.

U kunt mongo-join-queryom de aggregatiepijplijn automatisch te maken op basis van uw zoekopdracht.

Zo ziet uw zoekopdracht eruit:

const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");
joinQuery(
    mongoose.models.Comment,
    {
        find: { pid:444 },
        populate: ["uid"]
    },
    (err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);

Uw resultaat zou het gebruikersobject in het veld uidhebben en u kunt zoveel niveaus diep koppelen als u wilt. U kunt de verwijzing naar de gebruiker invullen, die verwijst naar een Team, die verwijst naar iets anders, enz.

Disclaimer: ik heb mongo-join-querygeschreven om dit exacte probleem aan te pakken.


Antwoord 16

playORM kan het voor je doen met behulp van S-SQL (Scalable SQL) die alleen partitionering toevoegt, zodat je joins binnen partities kunt doen.


Antwoord 17

Nee, het lijkt er niet op dat je het verkeerd doet. MongoDB-joins zijn “client-side”. Ongeveer zoals je zei:

Op dit moment krijg ik eerst de opmerkingen die overeenkomen met mijn criteria, dan zoek ik alle uid’s in die resultatenset uit, haal ik de gebruikersobjecten en voeg ik ze samen met de resultaten van de opmerking. Het lijkt erop dat ik het verkeerd doe.

1) Select from the collection you're interested in.
2) From that collection pull out ID's you need
3) Select from other collections
4) Decorate your original results.

Het is geen “echte” join, maar het is eigenlijk veel nuttiger dan een SQL-join omdat je niet te maken hebt met dubbele rijen voor “veel” zijdige joins, maar je verfraait de oorspronkelijk geselecteerde set.

>

Er staat veel onzin en FUD op deze pagina. Het blijkt 5 jaar later dat MongoDB nog steeds een ding is.


Antwoord 18

Ik denk dat als je genormaliseerde gegevenstabellen nodig hebt, je een aantal andere database-oplossingen moet proberen.

Maar ik heb die oplossing voor MOngo gevonden op Git
Trouwens, in voegt code in – het heeft de naam van de film, maar geen film-ID.

Probleem

Je hebt een verzameling acteurs met een reeks films die ze hebben gemaakt.

U wilt een verzameling films genereren met elk een reeks acteurs.

Enkele voorbeeldgegevens

db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
 db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

Oplossing

We moeten elke film in het Actor-document doorlopen en elke film afzonderlijk uitzenden.

De vangst hier bevindt zich in de verkleiningsfase. We kunnen geen array uit de verkleinende fase uitzenden, dus we moeten een acteursarray bouwen in het document “Waarde” dat wordt geretourneerd.

De code

map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}
reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

Kennisgeving hoe actor_list eigenlijk een JavaScript-object is dat een array bevat. Merk ook op dat de kaart dezelfde structuur uitzendt.

Voer het volgende uit om de kaart uit te voeren / te verminderen, het uit te voeren naar de “PIVOT” -verzameling en druk het resultaat af:

Printjson (DB. Actoren. Applyreduce (kaart, verminderen, “Pivot”));
db.pivot.find (). Foreach (Printjson);

Hier is de voorbeelduitvoer, merk op dat “mooie vrouw” en “Runaway Bride” zowel “Richard Gere” en “Julia Roberts” hebben.

{ "_id" : { "movie" : "Chicago" }, "value" : { "actors" : [ "Richard Gere" ] } }
{ "_id" : { "movie" : "Erin Brockovich" }, "value" : { "actors" : [ "Julia Roberts" ] } }
{ "_id" : { "movie" : "Pretty Woman" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }
{ "_id" : { "movie" : "Runaway Bride" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }


Antwoord 19

We kunnen twee collectie samenvoegen met behulp van MongoDB-subquery. Hier is een voorbeeld,
Opmerkingen –

`db.commentss.insert([
  { uid:12345, pid:444, comment:"blah" },
  { uid:12345, pid:888, comment:"asdf" },
  { uid:99999, pid:444, comment:"qwer" }])`

Gebruikers –

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])

MongoDB-subquery voor join –

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`

Krijg het resultaat van nieuw gegenereerde collectie –

db.newCommentUsers.find().pretty()

Resultaat –

`{
    "_id" : ObjectId("5511236e29709afa03f226ef"),
    "uid" : 12345,
    "pid" : 444,
    "comment" : "blah",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f0"),
    "uid" : 12345,
    "pid" : 888,
    "comment" : "asdf",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f1"),
    "uid" : 99999,
    "pid" : 444,
    "comment" : "qwer",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f3"),
            "uid" : 99999,
            "name" : "mia"
        }
    ]
}`

Hoop dus dit zal helpen.

Other episodes