Comment effectuer l'équivalent SQL Join dans MongoDB?

498

Comment effectuer l'équivalent SQL Join dans MongoDB?

Par exemple, disons que vous avez deux collections (utilisateurs et commentaires) et que je veux extraire tous les commentaires avec pid = 444 ainsi que les informations utilisateur pour chacun.

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

Existe-t-il un moyen d'extraire tous les commentaires avec un certain champ (par exemple ... trouver ({pid: 444})) et les informations utilisateur associées à chaque commentaire en une seule fois?

Pour le moment, je reçois d'abord les commentaires qui correspondent à mes critères, puis je trouve tous les uid de cet ensemble de résultats, je récupère les objets utilisateur et je les fusionne avec les résultats du commentaire. On dirait que je me trompe.

L'inconnu
la source
35
La dernière réponse à cette question est probablement la plus pertinente, car MongoDB 3.2+ a implémenté une solution de jointure appelée $ lookup. Je pensais que je le pousserais ici parce que peut-être que tout le monde ne lira pas au fond. stackoverflow.com/a/33511166/2593330
thefourtheye
6
Correct, la recherche $ a été introduite dans MongoDB 3.2. Les détails peuvent être trouvés sur docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Réponses:

306

Depuis Mongo 3.2, les réponses à cette question ne sont généralement plus correctes. Le nouvel opérateur $ lookup ajouté au pipeline d'agrégation est essentiellement identique à une jointure externe gauche:

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

De la documentation:

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

Bien sûr, Mongo n'est pas une base de données relationnelle, et les développeurs veillent à recommander des cas d'utilisation spécifiques pour la recherche $, mais au moins à partir de la 3.2, la jointure est désormais possible avec MongoDB.

Clayton Gulick
la source
@clayton: Que diriez-vous de plus de deux collections?
Dipen Dedania
1
@DipenDedania vient d'ajouter des étapes de recherche $ supplémentaires au pipeline d'agrégation.
Clayton Gulick
Je ne peux pas rejoindre n'importe quel champ du tableau dans la collection de gauche avec son identifiant correspondant dans la collection de droite. Quelqu'un peut-il m'aider?
Prateek Singh
1
Je suis un peu confus à ce sujet - existe-t-il un moyen de spécifier que vous ne voulez que certains documents dans la collection "from", ou est-ce que cela joint automatiquement tout dans la base de données à la fois?
user3413723
Vous vous demandez simplement si la dernière version de Spring Data MongoDB prend en charge 3.2?
gtiwari333
142

Cette page sur le site officiel de mongodb répond exactement à cette question:

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

Lorsque nous afficherons notre liste d'histoires, nous devrons indiquer le nom de l'utilisateur qui a publié l'histoire. Si nous utilisions une base de données relationnelle, nous pourrions effectuer une jointure sur les utilisateurs et les magasins, et obtenir tous nos objets en une seule requête. Mais MongoDB ne prend pas en charge les jointures et nécessite donc parfois un peu de dénormalisation. Ici, cela signifie mettre en cache l'attribut «nom d'utilisateur».

Les puristes relationnels peuvent déjà se sentir mal à l'aise, comme si nous violions une loi universelle. Mais gardons à l'esprit que les collections MongoDB ne sont pas équivalentes aux tables relationnelles; chacun sert un objectif de conception unique. Une table normalisée fournit un bloc de données atomique et isolé. Un document, cependant, représente de plus près un objet dans son ensemble. Dans le cas d'un site d'actualités sociales, on peut affirmer qu'un nom d'utilisateur est intrinsèque à l'histoire publiée.

William Stein
la source
51
@dudelgrincen c'est un changement de paradigme de la normalisation et des bases de données relationnelles. Le but d'un NoSQL est de lire et écrire très rapidement à partir de la base de données. Avec BigData, vous allez avoir des dizaines de serveurs d'applications et frontaux avec des nombres inférieurs sur les bases de données. Vous devez effectuer des millions de transactions par seconde. Déchargez le poids lourd de la base de données et placez-le au niveau de l'application. Si vous avez besoin d'une analyse approfondie, vous exécutez un travail d'intégration qui place vos données dans une base de données OLAP. De toute façon, vous ne devriez pas recevoir beaucoup de requêtes approfondies de votre base de données OLTP.
Snowburnt
18
@dudelgrincen Je dois également dire que ce n'est pas pour chaque projet ou conception. Si vous avez quelque chose qui fonctionne dans une base de données de type SQL, pourquoi le changer? Si vous ne pouvez pas masser votre schéma pour travailler avec noSQL, alors ne le faites pas.
Snowburnt
9
Les migrations et les schémas en constante évolution sont également beaucoup plus faciles à gérer sur un système NoSQL.
juste
14
Que faire si l'utilisateur a 3.540 publications sur le site Web et qu'il change son nom d'utilisateur dans son profil? Faut-il mettre à jour chaque message avec le nouveau nom d'utilisateur?
Ivo Pereira
2
@IvoPereira Oui et c'est exactement pourquoi il faut éviter de modéliser les données de cette façon. Il y a un article qui explique le même scénario et ses conséquences: Pourquoi vous ne devriez jamais utiliser MongoDB
Omid
138

Nous pouvons fusionner / joindre toutes les données dans une seule collection avec une fonction facile en quelques lignes en utilisant la console client mongodb, et maintenant nous pourrions être en mesure d'effectuer la requête souhaitée. Ci-dessous un exemple complet,

.- Auteurs:

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

.- Catégories:

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

.- Livres

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

.- Prêt de livres

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'
    }
]);

.- La 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);
    }
);

.- Obtenez les nouvelles données de collecte:

db.booksReloaded.find().pretty()

.- Réponse :)

{
    "_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" : [ ]
}

J'espère que ces lignes peuvent vous aider.

Orlando Becerra
la source
2
je me demande si ce même code peut être exécuté en utilisant doctrine mongodb?
abbood
4
Que se passe-t-il lorsque l'un des objets de référence reçoit une mise à jour? Cette mise à jour se reflète-t-elle automatiquement dans l'objet livre? Ou cette boucle doit-elle être exécutée à nouveau?
balupton
14
C'est très bien tant que vos données sont petites. Il va apporter le contenu de chaque livre à votre client, puis récupérer chaque catégorie, les prêts et les auteurs un par un. Le moment où vos livres sont en milliers, cela irait vraiment très lentement. Une meilleure technique consisterait probablement à utiliser le pipeline d'agrégation et à exporter les données fusionnées dans une collection distincte. Permettez-moi d'y revenir. J'ajouterai une réponse.
Sandeep Giri
Pouvez-vous adapter votre algorithme à cet autre exemple? stackoverflow.com/q/32718079/287948
Peter Krauss
1
@SandeepGiri comment puis-je faire le pipeline agrégé car j'ai des données vraiment très intensives dans une collection séparée doivent rejoindre ??
Yassine Abdul-Rahman
38

Vous devez le faire comme vous l'avez décrit. MongoDB est une base de données non relationnelle et ne prend pas en charge les jointures.

Otto Allmendinger
la source
4
Cela semble mauvais en termes de performances provenant d'un arrière-plan de serveur SQL, mais ce n'est peut-être pas si mal avec une base de données de document?
terjetyl
3
à partir d'un arrière-plan de serveur sql, j'apprécierais que MongoDB prenne un `` ensemble de résultats '' (avec les champs renvoyés sélectionnés) comme entrée pour une nouvelle requête en une seule fois, un peu comme les requêtes imbriquées dans SQL
Stijn Sanders
1
@terjetyl Vous devez vraiment planifier pour cela. Quels champs allez-vous présenter sur le front-end, s'il s'agit d'un montant limité dans une vue individuelle, vous les prenez comme des documents intégrés. La clé est de ne pas avoir besoin de faire de jointures. Si vous voulez faire une analyse approfondie, vous le faites après coup dans une autre base de données. Exécutez un travail qui transforme les données en cube OLAP pour des performances optimales.
Snowburnt
4
Depuis la version mongo 3.2, les jointures gauches sont prises en charge.
Somnath Muluk
18

Comme d'autres l'ont souligné, vous essayez de créer une base de données relationnelle à partir d'aucune base de données relationnelle, ce que vous ne voulez vraiment pas faire, mais de toute façon, si vous avez un cas à faire, voici une solution que vous pouvez utiliser. Nous effectuons d'abord une recherche foreach sur la collection A (ou dans votre cas, les utilisateurs), puis nous obtenons chaque élément en tant qu'objet, puis nous utilisons la propriété d'objet (dans votre cas uid) pour rechercher dans notre deuxième collection (dans vos commentaires de cas) si nous peut le trouver alors nous avons une correspondance et nous pouvons imprimer ou faire quelque chose avec elle. J'espère que cela vous aide et bonne chance :)

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
    }
});
grepit
la source
Cela ne trouvera-t-il pas l'élément sur lequel nous bouclons actuellement?
Skarlinski
18

Avec la bonne combinaison de $ lookup , $ project et $ match , vous pouvez joindre plusieurs tables sur plusieurs paramètres. En effet, ils peuvent être enchaînés plusieurs fois.

Supposons que nous voulons faire ce qui suit ( référence )

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

Étape 1: lier toutes les tables

vous pouvez rechercher autant de tables que vous le souhaitez.

$ lookup - un pour chaque table de la requête

$ unwind - parce que les données sont correctement dénormalisées, sinon enveloppées dans des tableaux

Code Python ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

Étape 2: définir toutes les conditions

$ project : définissez ici toutes les instructions conditionnelles, ainsi que toutes les variables que vous souhaitez sélectionner.

Code Python ..

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

Étape 3: joindre toutes les conditions

$ match - joignez toutes les conditions en utilisant OR ou AND etc. Il peut y en avoir plusieurs.

$ project : annuler la définition de toutes les conditions

Code Python ..

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

                        ])

À peu près n'importe quelle combinaison de tables, de conditions et de jointures peut être effectuée de cette manière.

sbharti
la source
17

Voici un exemple de collections "Join" * Acteurs et films :

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

Il utilise .mapReduce() méthode

* join - une alternative pour rejoindre des bases de données orientées document

antitoxique
la source
19
-1, cela ne joint PAS les données de deux collections. Il utilise les données d'une seule collection (acteurs) pivotant autour des données. Alors que les choses qui étaient des clés sont maintenant des valeurs et les valeurs sont maintenant des clés ... très différentes d'un JOIN.
Evan Teran
12
C'est exactement ce que vous devez faire, MongoDB n'est pas relationnel mais orienté document. MapReduce permet de jouer avec des données avec de grandes performances (vous pouvez utiliser le cluster etc ....) mais même pour des cas simples, c'est très utile!
Thomas Decaux
14

Vous pouvez rejoindre deux collections dans Mongo en utilisant la recherche qui est offerte dans la version 3.2. Dans votre cas, la requête serait

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

ou vous pouvez également rejoindre en ce qui concerne les utilisateurs, il y aura un petit changement comme indiqué ci-dessous.

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

Cela fonctionnera de la même manière que la jointure gauche et droite dans SQL.

jarry jafery
la source
11

Cela dépend de ce que vous essayez de faire.

Vous l'avez actuellement configuré en tant que base de données normalisée, ce qui est bien, et la façon dont vous le faites est appropriée.

Cependant, il existe d'autres façons de procéder.

Vous pouvez avoir une collection de publications qui comporte des commentaires intégrés pour chaque publication avec des références aux utilisateurs que vous pouvez interroger de manière itérative pour obtenir. Vous pouvez stocker le nom de l'utilisateur avec les commentaires, vous pouvez les stocker tous dans un seul document.

Le problème avec NoSQL est qu'il est conçu pour des schémas flexibles et une lecture et une écriture très rapides. Dans une ferme Big Data typique, la base de données est le plus gros goulot d'étranglement, vous avez moins de moteurs de base de données que vous ne le faites pour les serveurs d'applications et frontaux ... ils sont plus chers mais plus puissants, aussi l'espace disque dur est très bon marché comparativement. La normalisation vient du concept d'essayer d'économiser de l'espace, mais elle a un coût pour rendre vos bases de données effectuer des jointures compliquées et vérifier l'intégrité des relations, effectuer des opérations en cascade. Tout cela évite aux développeurs des maux de tête s'ils ont correctement conçu la base de données.

Avec NoSQL, si vous acceptez que la redondance et l'espace de stockage ne sont pas des problèmes en raison de leur coût (à la fois en temps de processeur requis pour effectuer les mises à jour et en coûts de disque dur pour stocker des données supplémentaires), la dénormalisation n'est pas un problème (pour les baies intégrées qui deviennent des centaines de milliers d'éléments, cela peut être un problème de performances, mais la plupart du temps ce n'est pas un problème). De plus, vous disposerez de plusieurs serveurs d'applications et frontaux pour chaque cluster de base de données. Demandez-leur de soulever les jointures et de laisser les serveurs de bases de données s'en tenir à la lecture et à l'écriture.

TL; DR: Ce que vous faites est très bien, et il existe d'autres façons de le faire. Consultez les modèles de modèle de données de la documentation de mongodb pour de bons exemples. http://docs.mongodb.org/manual/data-modeling/

Brûlé par la neige
la source
8
"La normalisation vient du concept d'essayer d'économiser de l'espace" Je remets cela en question. La normalisation à mon humble avis vient du concept d'éviter la redondance. Supposons que vous stockiez le nom d'un utilisateur avec un blog. Et si elle se marie? Dans un modèle non normalisé, vous devrez parcourir toutes les publications et changer le nom. Dans un modèle normalisé, vous modifiez généralement UN enregistrement.
DanielKhan
@DanielKhan empêchant la redondance et économisant de l'espace sont des concepts similaires, mais en ré-analyse, je suis d'accord, la redondance est la cause première de cette conception. Je vais reformuler. Merci pour la note.
Snowburnt
11

Il existe une spécification prise en charge par de nombreux pilotes appelée DBRef.

DBRef est une spécification plus formelle pour créer des références entre des documents. Les DBRefs (généralement) incluent un nom de collection ainsi qu'un identifiant d'objet. La plupart des développeurs n'utilisent des DBRefs que si la collection peut changer d'un document au suivant. Si votre collection référencée sera toujours la même, les références manuelles décrites ci-dessus sont plus efficaces.

Tiré de la documentation MongoDB: Modèles de données> Référence de modèle de données> Références de base de données

Pickels
la source
11

$ lookup (agrégation)

Effectue une jointure externe gauche vers une collection non conservée dans la même base de données pour filtrer les documents de la collection «jointe» pour traitement. À chaque document d'entrée, l'étape de recherche $ ajoute un nouveau champ de tableau dont les éléments sont les documents correspondants de la collection «jointe». L'étape de recherche $ passe ces documents remodelés à l'étape suivante. L'étape de recherche $ a les syntaxes suivantes:

Match d'égalité

Pour effectuer une correspondance d'égalité entre un champ des documents d'entrée et un champ des documents de la collection «jointe», l'étape de recherche $ a la syntaxe suivante:

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

L'opération correspondrait à l'instruction pseudo-SQL suivante:

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

URL Mongo

GoutamS
la source
la sous-requête est totalement différente de la jointure, si votre table de gauche est énorme, la sous-requête signifie que chaque ligne doit effectuer une requête elle-même. cela deviendra très lent. join est très rapide en sql.
yww325
8

Avant 3.2.6 , Mongodb ne prend pas en charge la requête de jointure comme mysql. ci-dessous solution qui fonctionne pour vous.

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

Vous pouvez exécuter des requêtes SQL, y compris join sur MongoDB avec mongo_fdw de Postgres.

metdos
la source
3

MongoDB n'autorise pas les jointures, mais vous pouvez utiliser des plugins pour gérer cela. Vérifiez le plugin mongo-join. C'est le meilleur et je l'ai déjà utilisé. Vous pouvez l'installer en utilisant npm directement comme ceci npm install mongo-join. Vous pouvez consulter la documentation complète avec des exemples .

(++) outil vraiment utile lorsque nous devons rejoindre des collections (N)

(-) nous pouvons appliquer des conditions juste au niveau supérieur de la requête

Exemple

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;


            });

    });
Amine_Dev
la source
2

Vous pouvez le faire en utilisant le pipeline d'agrégation, mais c'est difficile de l'écrire vous-même.

Vous pouvez utiliser mongo-join-querypour créer automatiquement le pipeline d'agrégation à partir de votre requête.

Voici à quoi ressemblerait votre requête:

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))
);

Votre résultat aurait l'objet utilisateur dans le uidchamp et vous pouvez lier autant de niveaux que vous le souhaitez. Vous pouvez remplir la référence à l'utilisateur, qui fait référence à une équipe, qui fait référence à autre chose, etc.

Avertissement : j'ai écritmongo-join-query pour résoudre ce problème exact.

Marcelo Lazaroni
la source
0

playORM peut le faire pour vous en utilisant S-SQL (Scalable SQL) qui ajoute simplement un partitionnement de sorte que vous pouvez faire des jointures au sein des partitions.

Dean Hiller
la source
-2

Non, il ne semble pas que vous vous trompiez. Les jointures MongoDB sont "côté client". Comme vous l'avez dit:

Pour le moment, je reçois d'abord les commentaires qui correspondent à mes critères, puis je trouve tous les uid de cet ensemble de résultats, je récupère les objets utilisateur et je les fusionne avec les résultats du commentaire. On dirait que je me trompe.

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.

Ce n'est pas une "vraie" jointure, mais elle est en fait beaucoup plus utile qu'une jointure SQL car vous n'avez pas à traiter les lignes en double pour les jointures "à plusieurs", à la place, vous décorez l'ensemble sélectionné à l'origine.

Il y a beaucoup de bêtises et de FUD sur cette page. Il s'avère que 5 ans plus tard, MongoDB est toujours une chose.

Michael Cole
la source
«vous n'avez pas à traiter les lignes en double pour les jointures à« plusieurs »côtés» - aucune idée de ce que vous entendez par là. Pouvez-vous clarifier?
Mark Amery
1
@MarkAmery, bien sûr. En SQL, une relation nn renvoie des lignes en double. Par exemple, mes amis. Si Bob est ami avec Mary et Jane, vous obtiendrez 2 rangées pour Bob: Bob, Mary et Bob, Jane. 2 Bobs est un mensonge, il n'y a qu'un seul Bob. Avec les jointures côté client, vous pouvez commencer avec Bob et décorer comme vous le souhaitez: Bob, "Mary and Jane". SQL vous permet de faire cela avec des sous-requêtes, mais cela fait un travail sur le serveur db qui pourrait être fait sur le client.
Michael Cole
-3

Je pense que si vous avez besoin de tables de données normalisées - vous devez essayer d'autres solutions de base de données.

Mais j'ai trouvé cette sollicution pour MOngo sur Git Soit dit en passant, dans le code inserts - il a le nom du film, mais l'ID du film noi .

Problème

Vous avez une collection d'acteurs avec une gamme de films qu'ils ont réalisés.

Vous souhaitez générer une collection de films avec un tableau d'acteurs dans chacun.

Quelques exemples de données

 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'] });

Solution

Nous devons parcourir chaque film dans le document Acteur et émettre chaque film individuellement.

Le hic est ici en phase de réduction. Nous ne pouvons pas émettre un tableau à partir de la phase de réduction, nous devons donc créer un tableau Acteurs à l'intérieur du document "value" qui est retourné.

Le 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;
}

Remarquez comment acteur_list est en fait un objet javascript qui contient un tableau. Notez également que la carte émet la même structure.

Exécutez ce qui suit pour exécuter la carte / réduire, exportez-la dans la collection "pivot" et imprimez le résultat:

printjson (db.actors.mapReduce (mapper, réduire, "pivoter")); db.pivot.find (). forEach (printjson);

Voici l'exemple de sortie, notez que "Pretty Woman" et "Runaway Bride" ont à la fois "Richard Gere" et "Julia Roberts".

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

Max Sherbakov
la source
Notez que la plupart du contenu de cette réponse (c'est-à-dire le bit qui est en anglais compréhensible) est copié du livre de recettes MongoDB sur le lien GitHub fourni par le répondeur.
Mark Amery
-4

Nous pouvons fusionner deux collections en utilisant la sous-requête mongoDB. Voici un exemple, Commentss--

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

Utilisateurs -

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

Sous-requête MongoDB pour JOIN--

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

Obtenez le résultat de la collection nouvellement générée -

db.newCommentUsers.find().pretty()

Résultat--

`{
    "_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"
        }
    ]
}`

J'espère que cela vous aidera.

Krishna
la source
7
Pourquoi avez-vous essentiellement copié cette réponse presque identique, vieille d'un an? stackoverflow.com/a/22739813/4186945
hackel