47 votes

Comment joindre une requête dans mongodb ?

J'ai une collection de documents d'utilisateur comme ceci :

User {
   id:"001"
   name:"John",
   age:30,
   friends:["userId1","userId2","userId3"....]
}

Un utilisateur a de nombreux amis, j'ai la requête suivante en SQL :

select * from user where in (select friends from user where id=?) order by age

J'aimerais avoir quelque chose de similaire dans MongoDB.

0 votes

Peut-être avez-vous besoin de références de base de données - docs.mongodb.org/manual/reference/database-references

47voto

Derek Points 1301

Pour tout avoir en une seule requête en utilisant la fonction $lookup du cadre d'agrégation, essayez ceci :

db.User.aggregate(
    [
        // First step is to extract the "friends" field to work with the values
        {
            $unwind: "$friends"
        },
        // Lookup all the linked friends from the User collection
        {
            $lookup:
            {
                from: "User",
                localField: "friends",
                foreignField: "_id",
                as: "friendsData"
            }
        },
        // Sort the results by age
        {
            $sort: { 'friendsData.age': 1 }
        },
        // Get the results into a single array
        {
            $unwind: "$friendsData"
        },
        // Group the friends by user id
        {
            $group:
            {
                _id: "$_id",
                friends: { $push: "$friends" },
                friendsData: { $push: "$friendsData" }
            }
        }
    ]
)

Disons que le contenu de votre collection d'utilisateurs est le suivant :

{
    "_id" : ObjectId("573b09e6322304d5e7c6256e"),
    "name" : "John",
    "age" : 30,
    "friends" : [
        "userId1",
        "userId2",
        "userId3"
    ]
}
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }

Le résultat de la requête sera :

{
    "_id" : ObjectId("573b09e6322304d5e7c6256e"),
    "friends" : [
        "userId3",
        "userId1",
        "userId2"
    ],
    "friendsData" : [
        {
            "_id" : "userId3",
            "name" : "Bobby",
            "age" : 12
        },
        {
            "_id" : "userId1",
            "name" : "Derek",
            "age" : 34
        },
        {
            "_id" : "userId2",
            "name" : "Homer",
            "age" : 44
        }
    ]
}

0 votes

Joli, je ne comprends pas comment on peut effectuer une telle opération sur un élément d'une collection (où est l'équivalent de findOne) ? Edit : il semble que $match puisse le faire.

0 votes

Je me demande si c'est plus efficace que 2 requêtes car $match n'arrêtera pas de chercher, contrairement à findOne . Je me demande si un $limit au-dessus, ça aiderait.

36voto

dannie.f Points 564

Edit : cette réponse ne s'applique qu'aux versions de MongoDb antérieures à la v3.2.

Vous ne pouvez pas faire ce que vous voulez en une seule requête. Vous devez d'abord récupérer la liste des identifiants des utilisateurs amis, puis transmettre ces identifiants à la deuxième requête pour récupérer les documents et les trier par âge.

var user = db.user.findOne({"id" : "001"}, {"friends": 1})
db.user.find( {"id" : {$in : user.friends }}).sort("age" : 1);

4 votes

Notez que le SQL affiché dans la question est également deux requêtes (bien que ce serait possible avec une seule avec une meilleure organisation, par exemple une table d'amis).

1 votes

Je pense que si vous deviez convertir cela dans une base de données "correcte", les amis se retrouveraient automatiquement dans une table séparée par quiconque s'y connaît en conception de bases de données et en normalisation.

0 votes

En fait, il serait préférable d'avoir une entité relationnelle du côté sql afin de pouvoir faire select users.* from users join friends on friends.friend_id = users.id et friends.user_id = ? order by users.age ... plutôt que deux tables users effectives ...

11voto

user3280472 Points 127

https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/

Voici la documentation sur la requête jointe dans mongodb, c'est une nouvelle fonctionnalité de la version 3.2.

Ce sera donc utile.

5voto

Andrey Araya Points 91

Vous pouvez utiliser dans Moongoose JS .populate() y { populate : { path : 'field' } } . Exemple :

Modèles :

 mongoose.model('users', new Schema({
        name:String,
        status: true,
        friends: [{type: Schema.Types.ObjectId, ref:'users'}], 
        posts: [{type: Schema.Types.ObjectId, ref:'posts'}], 

    }));
 mongoose.model('posts', new Schema({
            description: String,
            comments: [{type: Schema.Types.ObjectId, ref:'comments'}], 

        }));
 mongoose.model('comments', new Schema({
            comment:String,
            status: true

        }));

Si vous voulez voir les messages de vos amis, vous pouvez utiliser ceci.

Users.find().                    //Collection 1
        populate({path:'friends',   //Collection 2
        populate:{path:'posts'   //Collection 3
        }})
    .exec();

Si vous voulez voir les messages de vos amis et aussi apporter tous les commentaires, vous pouvez utiliser ceci et aussi, vous pouvez identifier la collection si ceci ne trouve pas et la requête est fausse.

 Users.find().                                    //Collection 1
        populate({path:'friends',                 //Collection 2
        populate:{path:'posts',                   //Collection 3
        populate:{path:'commets, model:Collection'//Collection 4 and more
        }}})
    .exec();

Et pour finir, si vous voulez obtenir seulement certains champs d'une collection, vous pouvez utiliser la propiertie select Example :

Users.find().                                    
        populate({path:'friends', select:'name status friends'                  
        populate:{path:'comments'               
        }})
    .exec();

4voto

pingw33n Points 6428

MongoDB n'a pas de jointures, mais dans votre cas, vous pouvez le faire :

db.coll.find({friends: userId}).sort({age: -1})

0 votes

Bonjour, je ne vous comprends pas, je veux obtenir les amis de l'utilisateur spécifié, et trier ces amis par âge.

0 votes

Bonjour, j'ai lu le document mongodb.org/display/DOCS/Querying, mais je ne trouve pas la solution Je pose donc ma question ici.

4 votes

C'est une solution correcte si la liste d'amis est symétrique : si A est un ami de B, alors B sera un ami de A. Ce que pingw33n fait ici est de dire : donnez-moi tous les utilisateurs qui ont userId comme un ami. Il ne s'agit donc pas de trouver un utilisateur, puis de trouver ses amis. Si votre relation d'amitié n'est pas symétrique (s'il s'agit d'une relation de type "follow" comme sur Twitter), vous devrez suivre l'approche de dannie.t, qui consiste à procéder en deux étapes. Ou alors, ajoutez un champ pour la relation inverse ('friendedBy', par exemple) et procédez de cette façon. Il s'agit d'une "dé-normalisation" : stocker les mêmes données de plusieurs façons pour plus de rapidité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X