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

时间:2013-10-30 09:21:54

标签: javascript node.js mongodb dbref

我正在使用带有nodejs REST服务的MongoDB,它暴露了我存储在里面的数据。我有一个关于如何查询使用$ ref。

的数据的问题

以下是Object的示例,其中包含对其他对象(详细信息)的引用:

{
    "_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个对象。这是一种“懒惰的装载”(不完全是,差不多)

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

谢谢

5 个答案:

答案 0 :(得分:5)

不,你不能。

  

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

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

答案 1 :(得分:5)

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

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

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

是的,这是可能的。您可以使用Mongoose执行此操作。它具有内置ref的人口支持。您需要稍微更改一下数据模型以使其正常工作,但这几乎就是您所需要的。当然,要做到这一点,Mongoose会发出与你相同的两个MongoDB查询。

答案 2 :(得分:1)

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

  1. MongoDb没有任何特殊命令可以检索引用的文档。因此,添加支持的驱动程序会人为地填充生成的对象。
  2. API越多,“裸机”就越少。事实上,作为。 MongoDb集合是无模式的,如果NodeJs驱动程序带回主文档并实现所有引用,如果代码然后保存文档而不破坏引用,则会产生嵌入式子文档。当然,这将是一团糟。
  3. 除非您的字段值有所不同,否则我不会打扰DBRef类型,而只是直接存储ObjectId。正如您所看到的,DBRef确实没有任何好处,除了为每个引用需要大量重复的磁盘空间,因为更丰富的对象必须与其类型信息一起存储。无论哪种方式,您都应该考虑存储包含引用集合文档的字符串的潜在不必要的开销。

    许多开发人员和MongoDb,Inc。都在现有的基本驱动程序之上添加了一个对象文档映射层。 MongoDb和Nodejs的一个流行选项是Mongoose。由于MongoDb服务器没有真正意识到引用的文档,引用的责任移动到客户端。由于始终引用给定文档中的特定集合更为常见,因此Mongoose可以将引用定义为Schema。 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
    })
    

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

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

答案 3 :(得分: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)
        }
    });
});

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

答案 4 :(得分:1)

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

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

  

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

     

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