10

我正在使用带有 nodejs REST 服务的 MongoDB,该服务公开了我存储在其中的数据。我有一个关于如何查询使用 $ref 的数据的问题。

这是一个 Object 的示例,其中包含对 anther 集合中另一个对象(详细信息)的引用:

{
    "_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 个对象。这是一种“延迟加载”(不完全是但几乎)

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

谢谢

4

5 回答 5

6

不,你不能。

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

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

于 2013-10-30T09:40:54.583 回答
6

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

不,这是不可能的。 Mongo 没有对 refs 的内部支持,因此由您的应用程序来填充它们(参见Brett 的回答)。

但是是否可以使用单个 node.js 命令获取所有 ref 的父对象?

是的,这是可能的。你可以用Mongoose做到这一点。它具有内置裁判的人口支持。您需要稍微更改您的数据模型以使其正常工作,但这正是您要寻找的。当然,这样做 Mongoose 将进行与您所做的相同的两个 MongoDB 查询。

于 2013-10-30T10:16:06.967 回答
2

Vladimir 的回答仍然无效,因为 db.dereference 方法已从 MongoDB Nodejs API 中删除:

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

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

db.dereference 由于服务器中不推荐使用 db 引用

于 2016-06-02T10:53:22.453 回答
1

我通过下一个示例达到了预期的结果:

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

不确定它的解决方案是最好的,但它是我发现的最简单的解决方案。

于 2015-02-06T21:47:59.357 回答
1

不,很少有 MongoDb 的驱动程序包含对DBRef. 有两个原因:

  1. MongoDb 没有任何特殊命令可以检索引用的文档。因此,确实添加支持的驱动程序正在人为地填充结果对象。
  2. API 越“裸机”,它的意义就越小。事实上,如。MongoDb 集合是无模式的,如果 NodeJs 驱动程序在实现所有引用的情况下带回主文档,如果代码随后在不破坏引用的情况下保存了文档,则会导致嵌入的子文档。当然,那将是一团糟。

除非您的字段值不同,否则我不会打扰DBRef类型,而是直接存储ObjectId。正如您所看到的,aDBRef确实没有任何好处,只是每个引用都需要大量重复的磁盘空间,因为更丰富的对象必须与其类型信息一起存储。无论哪种方式,您都应该考虑存储包含引用集合文档的字符串可能不必要的开销。

许多开发人员和 MongoDb, Inc. 在现有的基本驱动程序之上添加了一个对象文档映射层。MongoDb 和 Nodejs 的一个流行选项是 Mongoose。由于 MongoDb 服务器对引用的文档没有真正的认识,因此引用的责任转移到了客户端。由于从给定文档中一致地引用特定集合更为常见,Mongoose 使得将引用定义为模式成为可能。Mongoose 不是无模式的。

如果您接受拥有和使用 Schema 很有用,那么 Mongoose 绝对值得一看。它可以有效地从一组文档中获取一批相关文档(从单个集合中)。它始终使用本机驱动程序,但它通常非常高效地执行操作,并从更复杂的应用程序架构中消除了一些苦差事。

我强烈建议您查看该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
})

如果使用 with代替findById总是返回单个文档的,则将自动填充所有返回文档的属性。它会多次请求相同的参考文档也很聪明。findpopulatedetails

如果您不使用 Mongoose,我建议您考虑使用缓存层以避免在可能的情况下进行客户端引用连接,并尽可能使用$in查询运算符进行批处理。

于 2013-10-30T11:13:31.213 回答