I'am using MongoDB with a nodejs REST service which exposes my data stored inside. I have a question about how to interrogate my data which uses $ref.
我使用带有nodejs REST服务的MongoDB,它暴露了我存储在里面的数据。我有一个关于如何查询使用$ ref的数据的问题。
Here is a sample of an Object which contains a reference to another object (detail) in anther collection :
以下是Object的示例,其中包含对其他对象(详细信息)的引用:
{
"_id" : ObjectId("5962c7b53b6a02100a000085"),
"Title" : "test",
"detail" : {
"$ref" : "ObjDetail",
"$id" : ObjectId("5270c7b11f6a02100a000001")
},
"foo" : bar
}
Actually, using Node.js and mongodb module, I do the following :
实际上,使用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){
...
});
});
In fact I make 2 queries, and get 2 objects. It's a kind of "lazy loading" (not exactly but almost)
实际上我做了2个查询,得到了2个对象。这是一种“懒惰的装载”(不完全是,几乎)
My question is simple : is it possible to retrieve the whole object graph in one query ?
我的问题很简单:是否可以在一个查询中检索整个对象图?
Thank you
谢谢
5 个解决方案
#1
1
Answer of Vladimir is not still valid as the db.dereference method was deleted from MongoDB Nodejs API:
由于从MongoDB Nodejs API中删除了db.dereference方法,因此Vladimir的答案仍然无效:
https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver
https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver
The db instance object has been simplified. We've removed the following methods:
db实例对象已经简化。我们删除了以下方法:
db.dereference due to db references being deprecated in the server
db.dereference由于在服务器中不推荐使用db引用
#2
4
No, you can't.
不,你不能。
To resolve DBRefs, your application must perform additional queries to return the referenced documents. Many drivers have helper methods that form the query for the DBRef automatically. The drivers do not automatically resolve DBRefs into documents.
要解析DBRefs,您的应用程序必须执行其他查询才能返回引用的文档。许多驱动程序都有辅助方法,可以自动形成DBRef的查询。驱动程序不会自动将DBRef解析为文档。
From the MongoDB docs http://docs.mongodb.org/manual/reference/database-references/.
来自MongoDB文档http://docs.mongodb.org/manual/reference/database-references/。
#3
4
Is it possible to fetch parent object along with it's $ref using single MongoDB query?
是否可以使用单个MongoDB查询获取父对象及其$ ref?
No, it's not possible. Mongo have no inner support for refs, so it up to your application to populate them (see Brett's answer).
不,这是不可能的。 Mongo没有内部支持refs,所以由你的应用程序来填充它们(参见Brett的回答)。
But is it possible to fetch parent object with all its ref's with a single node.js command?
但是有可能使用单个node.js命令获取其所有ref的父对象吗?
Yes, it's possible. You can do it with Mongoose. It has build-in ref's population support. You'll need to change your data model a little bit to make it work, but it's pretty much what you're looking for. Of course, to do so Mongoose will make the same two MongoDB queries that you did.
是的,这是可能的。你可以用Mongoose做到这一点。它具有内置ref的人口支持。您需要稍微更改数据模型才能使其正常工作,但这几乎就是您所需要的。当然,要做到这一点,Mongoose会对您执行相同的两个MongoDB查询。
#4
1
No, very few drivers for MongoDb include special support for a DBRef
. There are two reasons:
不,MongoDb的驱动程序很少包含对DBRef的特殊支持。有两个原因:
- MongoDb doesn't have any special commands to make retrieval of referenced documents possible. So, drivers that do add support are artificially populating the resulting objects.
- MongoDb没有任何特殊命令可以检索引用的文档。因此,添加支持的驱动程序会人为地填充生成的对象。
- The more, "bare metal" the API, the less it makes sense. In fact, as. MongoDb collections are schema-less, if the NodeJs driver brought back the primary document with all references realized, if the code then saved the document without breaking the references, it would result in an embedded subdocument. Of course, that would be a mess.
- API中的“裸机”越多,它就越少。事实上,作为。 MongoDb集合是无模式的,如果NodeJs驱动程序带回主文档并实现所有引用,如果代码然后保存文档而不破坏引用,则会产生嵌入式子文档。当然,那将是一团糟。
Unless your field values vary, I wouldn't bother with a DBRef
type and would instead just store the ObjectId
directly. As you can see, a DBRef
really offers no benefit except to require lots of duplicate disk space for each reference, as a richer object must stored along with its type information. Either way, you should consider the potentially unnecessary overhead of storing a string containing the referenced collection's documents.
除非您的字段值有所不同,否则我不会打扰DBRef类型,而只是直接存储ObjectId。正如您所看到的,DBRef确实没有任何好处,除了为每个引用需要大量重复的磁盘空间,因为更丰富的对象必须与其类型信息一起存储。无论哪种方式,您都应该考虑存储包含引用集合文档的字符串的潜在不必要的开销。
Many developers and MongoDb, Inc. have added an object document mapping layer on top of the existing base drivers. One popular option for MongoDb and Nodejs is Mongoose. As the MongoDb server has no real awareness of referenced documents, the responsibility of the references moves to the client. As it's more common to consistently reference a particular collection from a given document, Mongoose makes it possible to define the reference as a Schema. Mongoose is not schema-less.
许多开发人员和MongoDb,Inc。都在现有的基本驱动程序之上添加了一个对象文档映射层。 MongoDb和Nodejs的一个流行选项是Mongoose。由于MongoDb服务器没有真正意识到引用文档,因此引用的责任移动到客户端。因为从给定文档中一致地引用特定集合更常见,所以Mongoose可以将引用定义为Schema。 Mongoose不是架构。
If you accept having and using a Schema is useful, then Mongoose is definitely worth looking at. It can efficiently fetch a batch of related documents (from a single collection) from a set of documents. It always is using the native driver, but it generally does operations extremely efficiently and takes some of the drudgery out of more complex application architectures.
如果您接受并使用Schema是有用的,那么Mongoose绝对值得一看。它可以从一组文档中有效地获取一批相关文档(来自单个集合)。它总是使用本机驱动程序,但它通常可以非常高效地执行操作,并且可以从更复杂的应用程序体系结构中解决一些苦差事。
I would strongly suggest you have a look at the populate
method (here) to see what it's capable of doing.
我强烈建议你看看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
})
If instead of findById
, which always returns a single document, find
were used, with populate
, all returned documents' details
property will be populated automatically. It's smart too that it would request the same referenced documents multiple times.
如果不是findById,它总是返回单个文档,则使用find,使用populate,将自动填充所有返回文档的详细信息属性。它也很聪明,它会多次请求相同的参考文档。
If you don't use Mongoose, I'd suggest you consider a caching layer to avoid doing client side reference joins when possible and use the $in
query operator to batch as much as possible.
如果你不使用Mongoose,我建议你考虑一个缓存层,以避免在可能的情况下进行客户端引用连接,并使用$ in query运算符尽可能地批量处理。
#5
1
I reach the desired result with next example:
我用下一个例子达到了预期的结果:
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)
}
});
});
Not sure that it solution is best of the best, but It is simplest solution that i found.
不确定它的解决方案是最好的,但它是我找到的最简单的解决方案。
#1
1
Answer of Vladimir is not still valid as the db.dereference method was deleted from MongoDB Nodejs API:
由于从MongoDB Nodejs API中删除了db.dereference方法,因此Vladimir的答案仍然无效:
https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver
https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver
The db instance object has been simplified. We've removed the following methods:
db实例对象已经简化。我们删除了以下方法:
db.dereference due to db references being deprecated in the server
db.dereference由于在服务器中不推荐使用db引用
#2
4
No, you can't.
不,你不能。
To resolve DBRefs, your application must perform additional queries to return the referenced documents. Many drivers have helper methods that form the query for the DBRef automatically. The drivers do not automatically resolve DBRefs into documents.
要解析DBRefs,您的应用程序必须执行其他查询才能返回引用的文档。许多驱动程序都有辅助方法,可以自动形成DBRef的查询。驱动程序不会自动将DBRef解析为文档。
From the MongoDB docs http://docs.mongodb.org/manual/reference/database-references/.
来自MongoDB文档http://docs.mongodb.org/manual/reference/database-references/。
#3
4
Is it possible to fetch parent object along with it's $ref using single MongoDB query?
是否可以使用单个MongoDB查询获取父对象及其$ ref?
No, it's not possible. Mongo have no inner support for refs, so it up to your application to populate them (see Brett's answer).
不,这是不可能的。 Mongo没有内部支持refs,所以由你的应用程序来填充它们(参见Brett的回答)。
But is it possible to fetch parent object with all its ref's with a single node.js command?
但是有可能使用单个node.js命令获取其所有ref的父对象吗?
Yes, it's possible. You can do it with Mongoose. It has build-in ref's population support. You'll need to change your data model a little bit to make it work, but it's pretty much what you're looking for. Of course, to do so Mongoose will make the same two MongoDB queries that you did.
是的,这是可能的。你可以用Mongoose做到这一点。它具有内置ref的人口支持。您需要稍微更改数据模型才能使其正常工作,但这几乎就是您所需要的。当然,要做到这一点,Mongoose会对您执行相同的两个MongoDB查询。
#4
1
No, very few drivers for MongoDb include special support for a DBRef
. There are two reasons:
不,MongoDb的驱动程序很少包含对DBRef的特殊支持。有两个原因:
- MongoDb doesn't have any special commands to make retrieval of referenced documents possible. So, drivers that do add support are artificially populating the resulting objects.
- MongoDb没有任何特殊命令可以检索引用的文档。因此,添加支持的驱动程序会人为地填充生成的对象。
- The more, "bare metal" the API, the less it makes sense. In fact, as. MongoDb collections are schema-less, if the NodeJs driver brought back the primary document with all references realized, if the code then saved the document without breaking the references, it would result in an embedded subdocument. Of course, that would be a mess.
- API中的“裸机”越多,它就越少。事实上,作为。 MongoDb集合是无模式的,如果NodeJs驱动程序带回主文档并实现所有引用,如果代码然后保存文档而不破坏引用,则会产生嵌入式子文档。当然,那将是一团糟。
Unless your field values vary, I wouldn't bother with a DBRef
type and would instead just store the ObjectId
directly. As you can see, a DBRef
really offers no benefit except to require lots of duplicate disk space for each reference, as a richer object must stored along with its type information. Either way, you should consider the potentially unnecessary overhead of storing a string containing the referenced collection's documents.
除非您的字段值有所不同,否则我不会打扰DBRef类型,而只是直接存储ObjectId。正如您所看到的,DBRef确实没有任何好处,除了为每个引用需要大量重复的磁盘空间,因为更丰富的对象必须与其类型信息一起存储。无论哪种方式,您都应该考虑存储包含引用集合文档的字符串的潜在不必要的开销。
Many developers and MongoDb, Inc. have added an object document mapping layer on top of the existing base drivers. One popular option for MongoDb and Nodejs is Mongoose. As the MongoDb server has no real awareness of referenced documents, the responsibility of the references moves to the client. As it's more common to consistently reference a particular collection from a given document, Mongoose makes it possible to define the reference as a Schema. Mongoose is not schema-less.
许多开发人员和MongoDb,Inc。都在现有的基本驱动程序之上添加了一个对象文档映射层。 MongoDb和Nodejs的一个流行选项是Mongoose。由于MongoDb服务器没有真正意识到引用文档,因此引用的责任移动到客户端。因为从给定文档中一致地引用特定集合更常见,所以Mongoose可以将引用定义为Schema。 Mongoose不是架构。
If you accept having and using a Schema is useful, then Mongoose is definitely worth looking at. It can efficiently fetch a batch of related documents (from a single collection) from a set of documents. It always is using the native driver, but it generally does operations extremely efficiently and takes some of the drudgery out of more complex application architectures.
如果您接受并使用Schema是有用的,那么Mongoose绝对值得一看。它可以从一组文档中有效地获取一批相关文档(来自单个集合)。它总是使用本机驱动程序,但它通常可以非常高效地执行操作,并且可以从更复杂的应用程序体系结构中解决一些苦差事。
I would strongly suggest you have a look at the populate
method (here) to see what it's capable of doing.
我强烈建议你看看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
})
If instead of findById
, which always returns a single document, find
were used, with populate
, all returned documents' details
property will be populated automatically. It's smart too that it would request the same referenced documents multiple times.
如果不是findById,它总是返回单个文档,则使用find,使用populate,将自动填充所有返回文档的详细信息属性。它也很聪明,它会多次请求相同的参考文档。
If you don't use Mongoose, I'd suggest you consider a caching layer to avoid doing client side reference joins when possible and use the $in
query operator to batch as much as possible.
如果你不使用Mongoose,我建议你考虑一个缓存层,以避免在可能的情况下进行客户端引用连接,并使用$ in query运算符尽可能地批量处理。
#5
1
I reach the desired result with next example:
我用下一个例子达到了预期的结果:
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)
}
});
});
Not sure that it solution is best of the best, but It is simplest solution that i found.
不确定它的解决方案是最好的,但它是我找到的最简单的解决方案。