如何在MongoDB中执行SQL连接等价物?

如何在MongoDB中执行SQL连接等价物?,mongodb,join,Mongodb,Join,如何在MongoDB中执行SQL连接等价物 例如,假设您有两个集合用户和注释,我希望提取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,

如何在MongoDB中执行SQL连接等价物

例如,假设您有两个集合用户和注释,我希望提取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"  }
是否有一种方法可以通过某个字段提取所有注释,例如……一次性找到{pid:444}以及与每个注释相关联的用户信息


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

你必须按照你描述的方式来做。MongoDB是一个非关系数据库,不支持连接

很多驱动程序都支持一种称为DBRef的规范

DBRef是用于在文档之间创建引用的更正式的规范。DBREF通常包括集合名称和对象id。大多数开发人员仅在集合可以从一个文档更改到下一个文档时才使用DBREF。如果引用的集合始终相同,则上面概述的手动引用会更有效

摘自MongoDB文档:数据模型>数据模型参考>

mongodb官方网站上的这一页正好解决了这个问题:

当我们显示故事列表时,我们需要显示发布故事的用户的姓名。如果我们使用关系数据库,我们可以对用户和存储执行连接,并在单个查询中获取所有对象。但MongoDB不支持连接,因此有时需要一些非规范化。这里,这意味着缓存“username”属性

关系纯粹主义者可能已经感到不安,好像我们违反了一些普遍规律。但我们要记住,MongoDB集合并不等同于关系表;每一个都有一个独特的设计目标。规范化表提供了一个原子的、孤立的数据块。然而,文档更接近于作为一个整体来表示对象。在社交新闻网站的案例中,可以认为用户名是发布故事的固有属性

以下是join*演员和电影系列的一个示例:

它使用.mapReduce方法


*join—加入面向文档的数据库的另一种选择

playORM可以使用S-SQLScalable SQL为您完成这项工作,它只添加分区,这样您就可以在分区内进行连接。

您可以使用Postgres在MongoDB上运行SQL查询,包括join。

这取决于您尝试执行的操作

您目前已将其设置为一个规范化数据库,这很好,而且您所采用的方式也很合适

然而,还有其他方法可以做到这一点

您可以有一个posts集合,该集合为每个post嵌入了注释,其中包含对用户的引用,您可以通过迭代查询来获取这些用户。您可以将用户名与注释一起存储,也可以将它们全部存储在一个文档中


NoSQL的特点是它是为灵活的模式和非常快速的读写而设计的。在典型的大数据场中,数据库是最大的瓶颈,与应用程序和前端服务器相比,数据库引擎更少……它们更昂贵但功能更强大,而且硬盘空间相对来说也非常便宜。规范化源于试图节省空间的概念,但它带来的代价是让数据库执行复杂的连接、验证关系的完整性、执行级联操作。如果开发人员正确地设计数据库,所有这些都可以为他们节省一些麻烦

在NoSQL中,如果您接受冗余和存储空间不是问题,因为它们在执行更新所需的处理器时间和存储额外数据所需的硬盘驱动器成本方面都有成本,那么对于成为数十万项的嵌入式阵列来说,非规范化不是问题,而是性能问题,但大多数时候这不是问题。此外,每个数据库集群都有几个应用程序和前端服务器。让他们完成连接的繁重工作,让数据库服务器坚持读写


TL;你现在做的很好,还有其他的方法。查看mongodb文档中的数据模型模式,了解一些很好的示例

使用mongodb客户端控制台,我们可以通过几行简单的函数合并/连接一个集合中的所有数据,现在我们可以执行所需的查询。 下面是一个完整的例子

-作者:

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

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

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

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

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);
    }
);
-获取新的收集数据:

db.booksReloaded.find().pretty()
-答复:

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

我希望这行代码可以帮助您。

我们可以使用mongoDB子查询合并两个集合。举个例子, 评论-

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

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])
用于联接的MongoDB子查询-

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`
得到结果fr om新生成的集合-

db.newCommentUsers.find().pretty()
结果-

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

希望这会有所帮助。

MongoDB不允许加入,但您可以使用插件来处理。检查mongo连接插件。这是最好的,我已经用过了。您可以直接使用npm安装它,就像这个npm安装mongo join一样。你可以查一下

++当我们需要加入N个集合时,这是一个非常有用的工具

-我们可以在查询的顶层应用条件

范例


正如其他人指出的,您正试图从非关系型数据库创建关系型数据库,这是您确实不想做的,但无论如何,如果您有必须这样做的情况,这里有一个解决方案,您可以使用。我们首先对集合a或您的案例用户执行foreach查找,然后将每个项目作为对象获取,然后使用案例uid中的对象属性在您的案例注释中的第二个集合中进行查找。如果我们可以找到它,那么我们有一个匹配项,我们可以打印或使用它执行某些操作。 希望这能帮助你,祝你好运:

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

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

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

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

这一页上有很多胡说八道和胡说八道。5年后,MongoDB仍然是一种东西。

我认为,如果您需要规范化的数据表,您需要尝试其他一些数据库解决方案。 但我已经为MOngo提供了解决方案 顺便说一下,在插入代码-它有电影的名称,但noi电影的ID

问题 你有一群演员,他们拍了一系列电影

您希望生成一组电影,每个电影中都有一组演员

一些样本数据

 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阶段发出数组,因此必须在返回的值文档中构建Actors数组

代码 请注意,actor_list实际上是一个包含数组的javascript对象。还请注意,贴图发射相同的结构

运行以下操作以执行map/reduce,将其输出到pivot集合并打印结果:

printjsondb.actors.mapReducemap、reduce、pivot; db.pivot.find.forEachprintjson

这是示例输出,请注意,《漂亮女人》和《逃跑新娘》都有理查德·基尔和朱莉娅·罗伯茨

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

截至Mongo 3.2,这个问题的答案大部分不再正确。添加到聚合管道的新$lookup运算符与左外部联接基本相同:

从文档中:

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

当然,Mongo不是一个关系数据库,开发人员正在谨慎地为$lookup推荐特定的用例,但至少在3.2版本中,MongoDB现在可以使用join。通过使用3.2版本中提供的lookup,您可以在Mongo中加入两个集合。在您的情况下,查询将是

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"
    }
})
在3.2.6之前,Mongodb不支持像mysql那样的连接查询。下面是适合您的解决方案

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])
通过$lookup、$project和$match的正确组合,可以在多个参数上联接多个表。这是因为它们可以被链接多次

假设我们想做下面的事情

步骤1:链接所有表

您可以根据需要$lookup任意多个表

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

                        ])
步骤3:加入所有条件

$match-使用或等连接所有条件。这些条件可以是多个

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

                        ])

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

您可以使用聚合管道来完成,但是自己编写是一件痛苦的事情

您可以使用从查询自动创建聚合管道

这是您的查询的外观:

const mongoose=所需的mongoose; const joinQuery=requiremogo join query; joinQuery mongoose.models.Comment, { 查找:{pid:444}, 填充:[uid] }, 呃,res=>呃?console.logError:,err:console。 logSuccess:,res.results ; 您的结果将在uid字段中包含用户对象,您可以链接任意深度的级别。您可以填充对用户的引用,它引用一个团队,它引用其他东西,等等

免责声明:我编写mongo join query就是为了解决这个问题。

$lookup aggregation 对同一数据库中的未分片集合执行左外部联接,以从“联接”集合中筛选文档进行处理。$lookup阶段向每个输入文档添加一个新的数组字段,其元素是来自“joined”集合的匹配文档。$lookup阶段将这些重新成形的文档传递到下一阶段。 $lookup阶段具有以下语法:

平等比赛 要在输入文档中的字段与“联接”集合中的文档中的字段之间执行相等匹配,$lookup stage具有以下语法:

该操作将对应于以下伪SQL语句:

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


从sql server背景来看,从性能角度来看,这似乎是错误的,但对于文档数据库来说,这可能并没有那么糟糕?从sql server背景来看,我希望MongoDB能够一次性将带有选定返回字段的“结果集”作为新查询的输入,就像中的嵌套查询一样SQL@terjetyl你得好好计划一下。您将在前端显示哪些字段,如果单个视图中的字段数量有限,则将其作为嵌入文档。关键是不需要进行连接。如果您想进行深入分析,可以在另一个数据库中事后进行。运行将数据转换为OLAP多维数据集以获得最佳性能的作业。从mongo 3.2版本开始,支持左连接。-1,这不是连接两个集合中的数据。它使用的是单个集合中的数据,并围绕这些数据旋转。所以曾经是关键的东西现在变成了值,值现在变成了关键。。。与连接非常不同。这正是您必须做的,MongoDB不是关系型的,而是面向文档的。MapReduce允许以高性能处理数据,您可以使用群集等。。。。但即使对于简单的情况,它也非常有用@dudelgrincen这是从规范化和关系数据库的范式转变。NoSQL的目标是非常快速地读取和写入数据库。有了BigData,您将拥有SCAD的应用程序和前端服务器,在DBs上的数量更少。你需要每秒完成数百万笔交易。从数据库中卸下繁重的工作,并将其放到应用程序级别。如果需要深入分析,可以运行集成作业,将数据放入OLAP数据库。无论如何,您不应该从OLTP数据库中得到太多深入的查询。@dudelgrincen我还应该说,这并不适用于每个项目或设计。如果您有在SQL类型数据库中工作的东西,为什么要更改它?如果你不能使用noSQL来处理你的模式,那么就不要这样做。迁移和不断发展的模式在noSQL系统上也更容易管理。如果用户在网站上有3.540篇文章,并且他确实在个人资料中更改了用户名,该怎么办?每个帖子都应该用新用户名更新吗?@IvoPereira是的,这就是为什么人们应该避免用这种方式建模数据。有一篇文章解释了相同的场景及其后果:规范化来自于试图节省空间的概念,我对此表示怀疑。IMHO规范化源于避免冗余的概念。假设您将一个用户的姓名与一篇博客文章一起存储。如果她结婚了怎么办?在一个未规范化的模型中,你将不得不浏览所有帖子并更改名称。在标准化模型中,您通常会更改一条记录。@DanielKhan防止冗余和节省空间是类似的概念,但经过重新分析,我同意,冗余是这种设计的根本原因。我会改写的。感谢您的提醒。我想知道是否可以使用doctrine mongodb运行相同的代码?当其中一个引用对象得到更新时会发生什么?该更新是否自动反映在book对象中?或者该循环是否需要再次运行?只要数据很小,就可以了。它将把每本书的内容带给你的客户,然后一个接一个地取回每一个类别、借阅者和作者。当你的书有几千本的时候,这会很慢。更好的技术可能是使用聚合管道并将合并的数据输出到单独的集合中。让我再说一遍。我会补充一个答案。你能把你的算法调整到另一个例子吗@Sandepgiri既然我在分离的集合中有非常密集的数据需要加入,我该如何做聚合管道?为什么你基本上复制了这个几乎相同的、一年前的答案对于多面连接,您不必处理重复的行-不知道这是什么意思。你能澄清一下吗?@MarkAmery,当然。在SQL中,n-n关系将返回重复的行。
例如,朋友。如果鲍勃是玛丽和简的朋友,你会得到鲍勃的两排:鲍勃,玛丽和鲍勃,简。两个鲍勃是谎言,只有一个鲍勃。通过客户端连接,您可以从Bob开始,按照自己喜欢的方式进行装饰:Bob、Mary和Jane。SQL让我们用子查询来实现这一点,但这是在db服务器上完成的工作,可以在客户端上完成。请注意,此答案的大部分内容,即易懂的英语部分,都是从MongoDB cookbook中复制的,位于回答者提供的GitHub链接处。关于此问题的最后一个答案可能是最相关的,因为MongoDB 3.2+实现了一个名为$lookup的连接解决方案。我想我会把它推到这里,因为也许不是每个人都会读到底。MongoDB 3.2中引入了正确的$lookup。有关详细信息,请访问@clayton:两个以上的集合如何?@DipenDedania只需向聚合管道添加额外的$lookup阶段。我无法将左侧集合中的数组中的任何字段与其在右侧集合中的对应id连接起来。有人能帮我吗?我对此有点困惑-有没有办法指定您只需要from集合中的某些文档,或者它会自动一次将所有文档连接到db中吗?只是想知道最新的Spring数据MongoDB是否支持3.2?这是否会找到我们当前正在循环的项?子查询与连接完全不同,如果左侧表很大,子查询意味着每一行都必须自己进行查询。它将变得非常缓慢。sql中的join非常快。谢谢!,喜欢你的答案的格式。完美的答案,对我来说,它给了一个错误{$unwind:R},如果它改为{$unwind:$R},它工作完美!
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
                        }}

                        ])
{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}
SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT <documents as determined from the pipeline>
                               FROM <collection to join>
                               WHERE <pipeline> );