我如何在MongoDB中执行SQL Join等效?

例如,假设你有两个集合(用户和评论),我想拉pid=444的所有评论以及每个评论的用户信息。

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

是否有一种方法可以一次性提取具有特定字段的所有评论(例如. ...find({pid:444}))和与每个评论相关的用户信息?

目前,我首先获取符合条件的注释,然后找出结果集中的所有uid,获取用户对象,并将它们与注释的结果合并。看来我做错了。


当前回答

我认为,如果你需要规范化的数据表-你需要尝试一些其他的数据库解决方案。

但是我在Git上找到了MOngo的解决方案 顺便说一下,在插入代码-它有电影的名称,但没有电影的ID。

问题

你有一个演员集合和他们所做的电影数组。

您希望生成一个Movies集合,每个Movies中都包含一个actor数组。

一些示例数据

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

解决方案

我们需要循环遍历Actor文档中的每个电影,并分别发出每个电影。

这里的问题是在减少阶段。我们不能从reduce阶段发出一个数组,因此必须在返回的“value”文档中构建一个Actors数组。

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

注意,actor_list实际上是一个包含数组的javascript对象。还要注意map发出相同的结构。

执行以下命令执行map / reduce,将其输出到“pivot”集合并打印结果:

printjson (db.actors。mapReduce(map, reduce, "pivot")); db.pivot.find () .forEach (printjson);

以下是输出示例,请注意《风月俏佳人》和《逃跑新娘》中都有“理查德·基尔”和“茱莉亚·罗伯茨”。

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

其他回答

下面是一个“join”* Actors和Movies集合的例子:

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

它使用了.mapReduce()方法

join -在面向文档的数据库中加入的替代方案

我认为,如果你需要规范化的数据表-你需要尝试一些其他的数据库解决方案。

但是我在Git上找到了MOngo的解决方案 顺便说一下,在插入代码-它有电影的名称,但没有电影的ID。

问题

你有一个演员集合和他们所做的电影数组。

您希望生成一个Movies集合,每个Movies中都包含一个actor数组。

一些示例数据

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

解决方案

我们需要循环遍历Actor文档中的每个电影,并分别发出每个电影。

这里的问题是在减少阶段。我们不能从reduce阶段发出一个数组,因此必须在返回的“value”文档中构建一个Actors数组。

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

注意,actor_list实际上是一个包含数组的javascript对象。还要注意map发出相同的结构。

执行以下命令执行map / reduce,将其输出到“pivot”集合并打印结果:

printjson (db.actors。mapReduce(map, reduce, "pivot")); db.pivot.find () .forEach (printjson);

以下是输出示例,请注意《风月俏佳人》和《逃跑新娘》中都有“理查德·基尔”和“茱莉亚·罗伯茨”。

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

通过正确组合$lookup, $project和$match,您可以在多个参数上连接多个表。这是因为它们可以被链接多次。

假设我们想做以下(引用)

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

步骤1:链接所有表

您可以根据需要查找任意数量的表。

$lookup -查询中的每个表一个

$unwind -正确地反规格化数据,否则它将被包装在数组中

Python代码. .

db.LeftTable.aggregate([
                        # connect all tables

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

步骤2:定义所有条件

$project:在这里定义所有的条件语句,加上所有你想选择的变量。

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

第三步:连接所有的条件句

$match -使用OR或AND等连接所有条件可以有很多个。

$project:取消所有的条件

完整的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
                        }}

                        ])

几乎任何表、条件和连接的组合都可以用这种方式完成。

你可以在Mongo中使用3.2版本提供的查找来连接两个集合。在您的情况下,查询将是

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

或者你也可以加入关于用户,然后会有一个小的变化如下所示。

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

它的工作原理与SQL中的左连接和右连接一样。

不,看起来你并没有做错。MongoDB连接是“客户端”。就像你说的

目前,我首先获取符合条件的注释,然后找出结果集中的所有uid,获取用户对象,并将它们与注释的结果合并。看来我做错了。

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.

它不是一个“真正的”连接,但它实际上比SQL连接有用得多,因为您不必处理“多”面连接的重复行,而是修饰最初选择的集合。

这一页上有很多废话和FUD。结果5年后,MongoDB仍然存在。