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

我如何在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,获取用户对象,并将它们与评论结果合并。 好像我做错了。

从Mongo 3.2开始,这个问题的答案大多不再正确。 添加到聚合pipe道中的新的$ lookup操作符与左外部连接基本相同:

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_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推荐特定的用例,但至less从3.2开始,现在可以使用MongoDB进行连接。

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

http://docs.mongodb.org/ecosystem/tutorial/model-data-for-ruby-on-rails/

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

关系纯粹主义者可能已经感到不安,好像我们违反了一些普遍的法律。 但是让我们记住,MongoDB集合不等于关系表; 每个都有独特的devise目标。 规范化的表提供了一个primefaces的,孤立的数据块。 然而,文件更接近地代表了一个整体的客体。 在社交新闻网站的情况下,可以认为用户名是发布故事的内在原因。

我们可以使用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是一个非关系数据库,不支持连接。

下面是一个“join” * 演员电影collections的例子:

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

它使用.mapReduce()方法

*join – join面向文档的数据库的替代方法

正如其他人指出的,你正在试图从非关系数据库创build一个关系数据库,你真的不想这样做,但是如果你有这种情况,你必须这样做,这是一个你可以使用的解决scheme。 我们首先对集合A(或者你的用户)进行foreach查找,然后我们将每个项目作为一个对象,然后我们使用对象属性(在你的情况下)来查找我们的第二个集合(在你的案例中的评论)可以find它,然后我们有一个匹配,我们可以打印或做一些事情。 希望这可以帮助你,祝你好运:)

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

这取决于你想要做什么。

你现在已经把它设置为一个规范化的数据库,这很好,你这样做的方式是适当的。

但是,还有其他方法可以做到这一点。

你可以有一个post集合,为每个post提供了embedded的注释,引用了你可以迭代查询得到的用户。 您可以将用户名称与注释一起存储,您可以将它们全部存储在一个文档中。

NoSQL是为灵活的架构devise的,而且非常快速的读写。 在一个典型的大数据农场中,数据库是最大的瓶颈,数据库引擎比应用程序和前端服务器要less…它们更昂贵,但function更强大,硬盘空间也相对便宜。 规范化来自于尝试节省空间的概念,但是它使得数据库执行复杂的连接和validation关系的完整性,执行级联操作带来了成本。 所有这些都为开发人员如果正确devise数据库节省了一些麻烦。

使用NoSQL,如果您接受冗余和存储空间不是由于其成本(无论是在更新所需的处理器时间还是存储额外数据的硬盘驱动器成本方面都不是问题),反规范化不是问题(对于embedded式arrays成千上万的项目可能是一个性能问题,但大部分时间,这不是一个问题)。 另外,您将为每个数据库群集提供多个应用程序和前端服务器。 让他们做重要的连接,让数据库服务器坚持读写。

TL; DR:你在做什么很好,还有其他的方法。 查看mongodb文档的数据模型模式以获得一些很好的例子。 http://docs.mongodb.org/manual/data-modeling/

有一个规范,很多驱动程序支持称为DBRef。

DBRef是在文档之间创build引用的更正式的规范。 DBRefs(通常)包括集合名称以及对象ID。 如果集合可以从一个文档更改为下一个,大多数开发人员只使用DBRefs。 如果您引用的集合总是相同的,那么上面概述的手册参考更有效率。

来自MongoDB文档: 数据模型>数据模型参考> 数据库引用

您可以使用3.2版本中提供的查找function在Mongo中join两个collections。 在你的情况下,查询将是

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

或者你也可以join用户方面,那么会有一些变化,如下所示。

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

它将和SQL中的左右连接一样工作。

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

3.2.6之前,Mongodb不像mysql那样支持连接查询。 下面的解决scheme适合你。

  db.getCollection('comments').aggregate([ {$match : {pid : 444}}, {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}}, ]) 

通过$ 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 :在这里定义所有的条件语句,加上你想要select的所有variables。

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

第3步:join所有的条件

$ match – 使用OR或AND等参数join所有条件。可以有多个这样的条件。

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

几乎所有的表,条件和联接的组合都可以用这种方式完成。

MongoDB不允许连接,但你可以使用插件来处理。 检查mongo-join插件。 这是最好的,我已经使用它。 你可以像这样使用npm直接npm install mongo-join 。 您可以通过示例查看完整的文档 。

(++)当我们需要join(N)集合时真的有用的工具

( – )我们可以在查询的顶层应用条件

 var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server; db.open(function (err, Database) { Database.collection('Appoint', function (err, Appoints) { /* we can put conditions just on the top level */ Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date }, full_date :{ $lte: end_date }}, function (err, cursor) { var join = new Join(Database).on({ field: '_id_Doctor', // <- field in Appoints document to: '_id', // <- field in User doc. treated as ObjectID automatically. from: 'User' // <- collection name for User doc }).on({ field: '_id_Patient', // <- field in Appoints doc to: '_id', // <- field in User doc. treated as ObjectID automatically. from: 'User' // <- collection name for User doc }) join.toArray(cursor, function (err, joinedDocs) { /* do what ever you want here */ /* you can fetch the table and apply your own conditions */ ..... ..... ..... resp.status(200); resp.json({ "status": 200, "message": "success", "Appoints_Range": joinedDocs, }); return resp; }); }); 

playORM可以为您使用S-SQL(可伸缩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连接更有用,因为您不必处理重复的行以进行“多”侧连接,而是处理最初select的集合。

在这个页面上有很多废话和FUD。 5年后,MongoDB仍然是一个问题。

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

但是我已经find了Git上 MOngo的解决方法顺便说一句,在插入代码 – 它有电影的名字, 但是noi电影的ID

问题

你有一系列他们已经完成的电影的演员。

你想要生成一个电影的集合,每个电影都有一个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”文档中构build一个Actors数组。

代码

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

我们可以通过使用mongoDB子查询来合并两个集合。 这里是例子,Commentss–

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

Userss–

 db.userss.insert([ { uid:12345, name:"john" }, { uid:99999, name:"mia" }]) 

用于JOIN的MongoDB子查询 –

 `db.commentss.find().forEach( function (newComments) { newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray(); db.newCommentUsers.insert(newComments); } );` 

从新生成的集合中获取结果 –

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

希望这会有所帮助。