2012-12-10 77 views
0

我正在学习node.js和mongodb的过程。在许多教程的推荐下,我正在利用猫鼬帮助与mongo进行交互。使事情复杂化,我有一个重要的RDMS背景,并且正在尽我所能来打破我的想法,希望通过SQL透镜来查看mongodb。mongodb是否提供查询子文档集合以查找父文档?

现在我与查询子文档的概念挣扎。我已经想出了如何根据子文档的属性来查询父文档,但无法通过直接查询子文档来查找如何查询所有父文档(不管类型)。为了说明我有以下的人为的例子的模式:

// subdocument 
var CategorySchema = new Schema({ 
    name: { type: String, required: true } 
}); 

var IpSchema = new Schema({ 
    ip_address: { type: String, required: true, index: true } 
    ,categories: [CategorySchema] 
}); 

var DomainSchema = new Schema({ 
    domain_name: { type: String, required: true, index: true } 
    ,categories: [CategorySchema] 
}); 

var ip = mongoose.model('Ip', IpSchema); 
var domain = mongoose.model('Domain', DomainSchema); 
var category = mongoose.model('Category', CategorySchema); 

上述模式嵌入每个存储的域和IP文档中类别的子文档阵列。通过一个类别名称轻松检索域名和ips 单独,但很难在单个镜头中检索与特定类别关联的所有域和ips。下面的代码概述为什么我相信这一点:

category.find(function (err, tcs) { 
    console.log(tcs); // contains an empty set because no categories stored here 
}); 

ip.find({ 'categories.name' : req.params.category }, function(err, ips) { 
    console.log(ips); // contains all parent documents w/ subdocument name 
}); 

domain.find({ 'categories.name' : req.params.category }, function(err, ips) { 
    console.log(ips); // contains all parent documents w/ subdocument name 
}); 

现在我可以结合上面的查询的结果,但似乎可能brittle--假设我在重用现在越来越多的文件类型。这是否让我存储类别,然后通过类别ID嵌入参考?这似乎是为了优化读取而写入时会增加流失。不幸的是,我的Googlefu让我无法找到任何关于标记方案的教程/最佳实践。也可能是我过于复杂的事情。

什么是检索基于共享子文档不同的父文档的最佳方法是什么?

回答

2

AFAIK一个蒙戈查询必须运行针对只有一个集合。这不是一个简陋的事实,而是mongodb本身的一个事实。鉴于这个事实,你可以尝试一些可能的设计。每次都会有不同的取舍,所以你要明白,这将是您的应用程序的重要查询并据此选择

1)同时存储IP地址和域在一个单一的集合,但与具有type属性中的每个文件和相应的属性。

猫鼬没有真正建立起来,以促进这一使用模式。如果大部分藏品都保存同质文档,猫鼬的效果最好。对于mongodb本身也是如此,但不是那样。不推荐使用,但如果您的使用模式确实需要这样,则不会出现问题。

2)运行针对并行的多个集合中的相同的查询。我有一些代码来做到这一点。这是对Mongoose.Query内部的一个相当讨厌的攻击,但它的工作原理。

var _     = require('underscore'); 
var async    = require('async'); 
function multiModelFind(query, models, outerCallback) { 
    var queries = _.map(models, function (Model) { 
    var otheModelQuery = new Query(); 
    var state = _.pick(query, 
     '_conditions', 
     '_fields', 
     '_updateArg', 
     'op', 
     'options', 
     'safe' 
    ); 
    state.model = Model; 
    _.extend(otheModelQuery, state); 
    return otheModelQuery; 
    }); 
    async.map(queries, function (query, callback) { 
    query.exec(callback); 
    }, function (error, models) { 
    outerCallback(error, _.flatten(models)); 
    }); 
} 

使用范例:

var query = IP.find({"categories.name": "foo"); 
multiModelfind(query, [IP, Domain], function (error, ipsAndDomains) {/*...*/}); 

我认为这是可行的少数藏品,但比屈指可数,你可能会需要移动到选项3。

3)创建一个Categorized集合与具有该是一个的ObjectId与猫鼬ref并使用.populate()加载每个集合一个命名属性模式中的“结合”的记录。这与关系数据库中的连接表非常类似。

{ 
    category: {type: ObjectId, ref: 'Category'}, 
    ip: {type: ObjectId, ref 'IP'}, 
    domain: {type: ObjectId, ref 'Domain'}, 
} 

对于每个记录在Categorized只有这些属性的2实际上将非空,你会在每个查询做了.populate('ip').populate('domain')。对于每个匹配的文档,将有1个针对Categorized集合的查询和针对_id的1个索引查询。如果只是一个关键字标签,您也可以直接存储该类别的名称,然后您不需要先按名称查找该类别的ObjectId。

+0

选项3似乎是最合乎逻辑的。显然,mongo允许这种使用模式,但是我是否违反了使用它的原则? – ahsteele

+0

不是。关系数据库和文档数据库都不适合每种用例。我认为整个社区正在认识到,大型复杂的现代应用程序需要由多个不同的数据库引擎支持,每个数据库引擎都处理它擅长的用例。这对于面向文档或关系数据库来说都不是理想的用例,但它在每一种情况下都可行。在那里可能有一个不同的NoSQL数据库比MongoDB更有效地处理这个数据库。不确定。 –

+0

对不起,将其标记为答案。 – ahsteele