web-dev-qa-db-fra.com

MongoDB interroge plusieurs collections à la fois

users
{
 "_id":"12345",
 "admin":1
},
{
 "_id":"123456789",
 "admin":0
}

posts
{
 "content":"Some content",
 "owner_id":"12345",
 "via":"facebook"
},
{
 "content":"Some other content",
 "owner_id":"123456789",
 "via":"facebook"
}

Voici un échantillon de mon mongodb. Je veux obtenir tous les messages qui ont "via" attribut égal à "facebook" et posté par un administrateur ("admin": 1). Je n'arrivais pas à comprendre comment acquérir cette requête. Mongodb n'étant pas une base de données relationnelle, je ne pouvais pas effectuer d'opération de jointure. Quelle pourrait être la solution?

63
Sarpdoruk Tahmaz

Essayer de vous connecter à MongoDB irait à l'encontre de l'objectif d'utiliser MongoDB. Vous pouvez toutefois utiliser un DBref et écrire votre code au niveau de l'application (ou la bibliothèque) afin qu'il récupère automatiquement ces références pour vous.

Ou vous pouvez modifier votre schéma et utiliser documents incorporés .

Votre choix final est de laisser les choses exactement telles qu’elles sont maintenant et de faire deux requêtes.

46
Charles Hooper

Voici la réponse à votre question.

db.getCollection('users').aggregate([
    {$match : {admin : 1}},
    {$lookup: {from: "posts",localField: "_id",foreignField: "owner_id",as: "posts"}},
    {$project : {
            posts : { $filter : {input : "$posts"  , as : "post", cond : { $eq : ['$$post.via' , 'facebook'] } } },
            admin : 1

        }}

])

Ou vous pouvez choisir l'option de groupe mongodb.

db.getCollection('users').aggregate([
    {$match : {admin : 1}},
    {$lookup: {from: "posts",localField: "_id",foreignField: "owner_id",as: "posts"}},
    {$unwind : "$posts"},
    {$match : {"posts.via":"facebook"}},
    { $group : {
            _id : "$_id",
            posts : {$Push : "$posts"}
    }}
])
17
Anish Agarwal

Vous pouvez utiliser $lookup (multiple) pour obtenir les enregistrements de plusieurs collections:

Exemple:

Si vous avez plus de collections (j'ai 3 collections pour la démo ici, vous pouvez en avoir plus de 3). et je veux obtenir les données de 3 collections en un seul objet:

La collection est comme:

db.doc1.find (). pretty ();

{
    "_id" : ObjectId("5901a4c63541b7d5d3293766"),
    "firstName" : "shubham",
    "lastName" : "verma"
}

db.doc2.find (). pretty ();

{
    "_id" : ObjectId("5901a5f83541b7d5d3293768"),
    "userId" : ObjectId("5901a4c63541b7d5d3293766"),
    "address" : "Gurgaon",
    "mob" : "9876543211"
}

db.doc3.find (). pretty ();

{
    "_id" : ObjectId("5901b0f6d318b072ceea44fb"),
    "userId" : ObjectId("5901a4c63541b7d5d3293766"),
    "fbURLs" : "http://www.facebook.com",
    "twitterURLs" : "http://www.Twitter.com"
}

Maintenant, votre requête sera la suivante:

db.doc1.aggregate([
    { $match: { _id: ObjectId("5901a4c63541b7d5d3293766") } },
    {
        $lookup:
        {
            from: "doc2",
            localField: "_id",
            foreignField: "userId",
            as: "address"
        }
    },
    {
        $unwind: "$address"
    },
    {
        $project: {
            __v: 0,
            "address.__v": 0,
            "address._id": 0,
            "address.userId": 0,
            "address.mob": 0
        }
    },
    {
        $lookup:
        {
            from: "doc3",
            localField: "_id",
            foreignField: "userId",
            as: "social"
        }
    },
    {
        $unwind: "$social"
    },

  {   
    $project: {      
           __v: 0,      
           "social.__v": 0,      
           "social._id": 0,      
           "social.userId": 0
       }
 }

]).pretty();

alors votre résultat sera:

{
    "_id" : ObjectId("5901a4c63541b7d5d3293766"),
    "firstName" : "shubham",
    "lastName" : "verma",

    "address" : {
        "address" : "Gurgaon"
    },
    "social" : {
        "fbURLs" : "http://www.facebook.com",
        "twitterURLs" : "http://www.Twitter.com"
    }
}

Si vous voulez tous les enregistrements de chaque collection, vous devez supprimer la ligne suivante de la requête:

{
            $project: {
                __v: 0,
                "address.__v": 0,
                "address._id": 0,
                "address.userId": 0,
                "address.mob": 0
            }
        }

{   
        $project: {      
               "social.__v": 0,      
               "social._id": 0,      
               "social.userId": 0
           }
     }

Après avoir enlevé le code ci-dessus, vous obtiendrez le total record comme:

{
    "_id" : ObjectId("5901a4c63541b7d5d3293766"),
    "firstName" : "shubham",
    "lastName" : "verma",
    "address" : {
        "_id" : ObjectId("5901a5f83541b7d5d3293768"),
        "userId" : ObjectId("5901a4c63541b7d5d3293766"),
        "address" : "Gurgaon",
        "mob" : "9876543211"
    },
    "social" : {
        "_id" : ObjectId("5901b0f6d318b072ceea44fb"),
        "userId" : ObjectId("5901a4c63541b7d5d3293766"),
        "fbURLs" : "http://www.facebook.com",
        "twitterURLs" : "http://www.Twitter.com"
    }
}
15
Shubham Verma

Comme mentionné précédemment dans MongoDB, vous ne pouvez pas vous joindre entre des collections.

Pour votre exemple, une solution pourrait être:

var myCursor = db.users.find({admin:1});
var user_id = myCursor.hasNext() ? myCursor.next() : null;
db.posts.find({owner_id : user_id._id});

Voir le manuel de référence - section curseurs: http://es.docs.mongodb.org/manual/core/cursors/

Une autre solution consisterait à intégrer les utilisateurs à la collection d'articles, mais je pense que pour la plupart des applications Web, la collection d'utilisateurs doit être indépendante pour des raisons de sécurité. La collection des utilisateurs peut avoir des rôles, des autorisations, etc.

posts
{
 "content":"Some content",
 "user":{"_id":"12345", "admin":1},
 "via":"facebook"
},
{
 "content":"Some other content",
 "user":{"_id":"123456789", "admin":0},
 "via":"facebook"
}

et alors:

db.posts.find({user.admin: 1 });
8
almoraleslopez

Effectuez plusieurs requêtes, utilisez des documents incorporés ou consultez "références de base de données".

3
Andreas Jung

Une solution: ajoutez isAdmin: 0/1 flag à votre document de post-collecte.

Autre solution: utiliser DBrefs

2
kheya

Vous pouvez écrire un exemple de code JavaScript comme ci-dessous et appeler la fonction si nécessaire.

Référez-vous à l'illustration à: http://dbversity.com/mongodb-querying-multiple-collections-with-a-javascript/

function colListQuery() {
var tcol = new Array()
tcol= db.getCollectionNames();
for(var i = 1; i < tcol.length ; i++) {
query = “db.” + tcol[i] + “.find()”;

var docs= eval(query);
docs.forEach( function(doc, index){ print( “Database_Name:”, db, “Collection_Name:”, tcol[i], “x_value:”, doc.x, “_id:”, doc._id) });

}
}

Appelez-le ensuite avec colListQuery () lorsque vous en avez besoin, comme indiqué dans l'illustration.

1
dbversity