MongoDB一次查询多个集合


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

这是我的mongodb的样本。我想获取所有具有“ via”属性等于“ facebook”并由管理员(“ admin”:1)发布的帖子。我不知道如何获取此查询。由于mongodb不是关系数据库,因此无法执行联接操作。有什么解决方案?

Answers:


52

尝试在MongoDB中加入JOIN会破坏使用MongoDB的目的。但是,您可以使用DBref并编写应用程序级代码(或库),以便它自动为您获取这些引用。

或者,您可以更改架构并使用嵌入式文档

您最后的选择是使事物保持现在的状态,并执行两个查询。


1
如何为此编写两个查询?
Tri Nguyen

Doctrine MongoDB ODM是一个处理此类问题的不错的库。但是,它仅允许您通过一层引用进行连接;您不能以任何语法上的方便程度在引用内的引用内填充引用。
CommaToast,2014年

1
如何在MongoJS中使用简单的旧查询来实现?
皮尔2015年

2
您现在可以使用$lookup加入
Corbfon

抱歉,但是我不得不说,“尝试使用没有JOIN概念的数据库会破坏数据库的目的。完全是因为” Mongo添加了$ lookup,原因是错误地认为不是这种情况。嵌入式文档不是最佳解决方案,因为它们与应与之等效的记录不同步,并且您最终会复制数据。
eggmatters

34

您可以使用$lookup(多个)从多个集合中获取记录:

例:

如果您有更多集合(我在这里有3个要演示的集合,则可以有3个以上)。我想从单个对象的3个集合中获取数据:

集合为:

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

现在您的查询将如下所示:

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

那么您的结果将是:

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

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

如果要获取每个集合的所有记录,则应从查询中删除以下行:

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

删除上面的代码后,您将获得以下总记录:

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

嗨,@ Shubham Verma,如何从“社交”或“地址”集合中获取多个记录。在同一查询中。就像我在单个用户的社交收藏中有多条记录
Venkatesh Somu,

22

这是您的问题的答案。

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

        }}

])

或者,您可以使用mongodb group选项。

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

这是做什么的?这是查询还是将元数据添加到集合中?
ViniciusPires'1

1
$ lookup运算符自3.2版以来是新的,就像左外部联接一样。参见docs.mongodb.com/manual/reference/operator/aggregation/lookup
almoraleslopez

9

如之前在MongoDB中提到的,您不能在集合之间进行联接。

对于您的示例,解决方案可能是:

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

请参阅参考手册的“游标”部分:http : //es.docs.mongodb.org/manual/core/cursors/

其他解决方案是将用户嵌入帖子集合中,但是出于安全原因,我认为对于大多数Web应用程序,用户集合必须独立。用户集合可能具有角色,权限等。

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

接着:

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


2

一种解决方案:将isAdmin:0/1标志添加到您的帖子收集文档中。

其他解决方案:使用DBrefs

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.