Nodejs + mongodb:如何查询$ ref字段?

我使用MongoDB和一个nodejs REST服务来暴露我的数据存储在里面。 我有一个关于如何查询我使用$ ref的数据的问题。

下面是一个对象的示例,其中包含对另一个对象(细节)的引用在另一个集合中的引用:

{ "_id" : ObjectId("5962c7b53b6a02100a000085"), "Title" : "test", "detail" : { "$ref" : "ObjDetail", "$id" : ObjectId("5270c7b11f6a02100a000001") }, "foo" : bar } 

实际上,使用Node.js和mongodb模块,我做了以下操作:

 db.collection("Obj").findOne({"_id" : new ObjectID("5962c7b53b6a02100a000085"}, function(err, item) { db.collection(item.$ref).findOne({"_id" : item.$id}, function(err,subItem){ ... }); }); 

事实上,我做了2个查询,并获得2个对象。 这是一种“懒加载”(不完全,但几乎)

我的问题很简单:是否有可能在一个查询中检索整个对象图?

谢谢

由于从MongoDB Nodejs API中删除了db.dereference方法,Vladimir的答案仍然无效:

https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver

db实例对象已经被简化了。 我们删除了以下方法:

db.dereference由于在数据库引用被弃用在服务器中

不,你不能。

要解决DBRefs,您的应用程序必须执行额外的查询才能返回引用的文档。 许多驱动程序都有自动为DBRef构成查询的帮助程序方法。 驱动程序不会自动将DBRefsparsing为文档。

来自MongoDB文档http://docs.mongodb.org/manual/reference/database-references/

是否有可能使用单个MongoDB查询获取父对象及其$ ref?

不,这是不可能的。 Mongo对refs没有内在的支持,所以可以由你的应用程序来填充它们(见Brett的答案 )。

但是是否有可能通过一个node.js命令来获取其所有引用的父对象?

是的,这是可能的。 你可以用mongoose做。 它有内置的裁判的人口支持。 你需要稍微改变一下你的数据模型才能使它工作,但是这正是你所需要的。 当然,为了做到这一点,Mongoose将会完成和你一样的两个MongoDB查询。

不,很less有MongoDb驱动程序包含对DBRef特殊支持。 有两个原因:

  1. MongoDb没有任何特殊的命令来检索引用文件。 因此,添加支持的驱动程序是人为地填充结果对象。
  2. API越“裸”,越不合理。 其实就像。 MongoDb集合是无模式的,如果NodeJs驱动程序将带有所有引用的主文档带回来,如果代码保存了文档而不中断引用,则会导致embedded的子文档。 当然,这将是一个烂摊子。

除非你的字段值不同,我不会打扰一个DBReftypes,而是直接存储ObjectId 。 正如你所看到的, DBRef确实没有什么好处,除了每个引用需要大量重复的磁盘空间之外,更丰富的对象必须与其types信息一起存储。 无论哪种方式,您都应该考虑存储包含引用集合文档的string的潜在不必要开销。

许多开发人员和MongoDb,Inc.已经在现有的基础驱动程序之上添加了一个对象文档映射层。 MongoDb和Nodejs的一个stream行选项是Mongoose。 由于MongoDb服务器没有真正意识到引用的文档,引用的责任移动到客户端。 由于从给定文档中持续引用特定集合更为常见,因此Mongoose可以将引用定义为Schema。 mongoose不是无模式的。

如果你接受并使用Schema是有用的,那么Mongoose绝对值得一看。 它可以从一组文档中高效地获取一批相关文档(来自单个集合)。 它总是使用本地驱动程序,但它通常非常有效地运行,并从更复杂的应用程序体系结构中汲取了一些苦头。

我强烈build议你看看populate方法( 在这里 ),看看它能做什么。

 Demo /* Demo would be a Mongoose Model that you've defined */ .findById(theObjectId) .populate('detail') .exec(function (err, doc) { if (err) return handleError(err); // do something with the single doc that was returned }) 

如果不是总是返回单个文档的findById ,而是使用findById ,那么populate ,所有返回的文档的details属性将自动填充。 它也很聪明,它会多次请求相同的参考文件。

如果你不使用Mongoose,我build议你考虑一个caching层,以尽可能避免做客户端引用连接,并尽可能使用$in查询操作符进行批处理。

下一个例子我达到了预期的效果:

 collection.find({}, function (err, cursor) { cursor.toArray(function (err, docs) { var count = docs.length - 1; for (i in docs) { (function (docs, i) { db.dereference(docs[i].ref, function(err, doc) { docs[i].ref = doc; if (i == count) { (function (docs) { console.log(docs); })(docs); } }); })(docs, i) } }); }); 

不知道它的解决scheme是最好的,但它是最简单的解决scheme,我发现。