【问题标题】:Nodejs + mongodb : How to query $ref fields?Nodejs + mongodb:如何查询 $ref 字段?
【发布时间】:2016-12-10 04:59:35
【问题描述】:

我正在使用带有 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 个对象。这是一种“延迟加载”(不完全是但几乎)

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

谢谢

【问题讨论】:

    标签: javascript node.js mongodb dbref


    【解决方案1】:

    不,你不能。

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

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

    【讨论】:

    • 据我所知,node-mongodb-native 驱动程序没有办法为您解析这些引用。
    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

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

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

        我强烈建议您查看 populate 方法 (here) 以了解它的功能。

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

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

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

        【讨论】:

        • 谢谢,我去看看猫鼬。
        【解决方案5】:

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

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

        不确定它的解决方案是否是最好的,但它是我找到的最简单的解决方案。

        【讨论】:

          猜你喜欢
          • 2018-01-18
          • 2020-07-20
          • 1970-01-01
          • 2018-11-17
          • 1970-01-01
          • 2020-03-25
          相关资源
          最近更新 更多