用于嵌入式集合的 MongoDB 首选模式。文档与数组

Posted

技术标签:

【中文标题】用于嵌入式集合的 MongoDB 首选模式。文档与数组【英文标题】:MongoDB preferred schema for embedded collections. documents vs. arrays 【发布时间】:2011-12-26 00:35:29 【问题描述】:

我相信至少有两种方法可以在 mongodb 文档中嵌入数据。在一个简化的例子中,我们可以有这样的东西:


    'name' : 'bill',
    'lines': 
       'idk73716': 'name': 'Line A',
       'idk51232': 'name': 'Line B',
       'idk23321': 'name': 'Line C'
    

作为一个数组:


    'name' : 'bill',
    'lines': [
       'id': 'idk73716', 'name': 'Line A',
       'id': 'idk51232', 'name': 'Line B',
       'id': 'idk23321', 'name': 'Line C'
    ]

正如您在此用例中所见,保留每行的 id 很重要。

我想知道这两种模式之间是否有优缺点。特别是在使用索引时,我觉得第二种可能更容易使用,因为可以在“lines.id”甚至“lines.name”上创建一个索引来搜索所有文档的 id 或名称。在第一个示例中,我没有找到任何有效的解决方案来索引 id('idk73716' 等)。

如果您有这样的用例,通常是否首选使用第二种方法?

【问题讨论】:

【参考方案1】:

今天我们有 $eleMatch 运算符来实现这一点,正如这里所讨论的 - Retrieve only the queried element in an object array in MongoDB collection

但是这个问题提出了一些有趣的设计选择,我今天也在努力做出这些选择。 如果在嵌入式文档中需要频繁的 CRUD,那么从给定的两个选项中应该首选哪一个?

我发现,当 ID 用作属性名称时,使用新的 $set/$unset 运算符在嵌入式文档上执行 CRUD 很容易。而且如果客户可以获取ID进行编辑,它比数组,IMO更好。 这是 Mongodb 关于模式设计和做出这些设计决策的另一篇有用的博文

http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1

【讨论】:

【参考方案2】:

在您的第一种方法中,您无法索引 id 字段,因为 id 用作键。它有点像键值字典。如果您有一组已知的 id(当然数量较少),这种方法很有用。假设在您的第一个示例中,id 在前面是众所周知的,

>>db.your_colleection.find()
  "_id" : ObjectId("4ebbb6f974235464de49c3a5"), "name" : "bill", 
  "lines" :  
             "idk73716" :  "name" : "Line A" ,
             "idk51232" :  "name" : "Line B"  ,
             "idk23321":   "name" : "Line C" 
             
  

所以要查找 id 字段 idk73716 的值,您可以通过

 db.your_colleection.find(,'lines.idk73716':1)
  "_id" : ObjectId("4ebbb6f974235464de49c3a5"), "lines" :  "idk73716" :  "name" : "Line A"   

空的 表示查询,第二部分 'lines.idk73716':1 是查询选择器。

将 id 作为键具有单独选择特定字段的优势。 尽管 'lines.idk73716':1 是一个字段选择器,但在这里它用作查询和选择器。但这不能在您的第二种方法中完成。假设第二个集合是这样的

> db.second_collection.find()
 "_id" : ObjectId("4ebbb9c174235464de49c3a6"), "name" : "bill", "lines" : [
    
        "id" : "idk73716",
        "name" : "Line A"
    ,
    
        "id" : "idk51232",
        "name" : "Line B"
    ,
    
        "id" : "idk23321",
        "name" : "Line C"
    
] 
> 

并且你索引了字段id,所以如果你想通过id查询

> db.second_collection.find('lines.id' : 'idk73716' )

 "_id" : ObjectId("4ebbb9c174235464de49c3a6"), "name" : "bill", "lines" : [
    
        "id" : "idk73716",
        "name" : "Line A"
    ,
    
        "id" : "idk51232",
        "name" : "Line B"
    ,
    
        "id" : "idk23321",
        "name" : "Line C"
    
] 
> 

通过查看上面的输出,可以看出没有办法单独选择匹配的子(嵌入)文档,但在第一种方法中是可能的。这是 mongodb 的默认行为。

db.second_collection.find('lines.id' : 'idk73716' ,'lines':1)

将获取所有行,而不仅仅是 idk73716

 "_id" : ObjectId("4ebbb9c174235464de49c3a6"), "lines" : [
    
        "id" : "idk73716",
        "name" : "Line A"
    ,
    
        "id" : "idk51232",
        "name" : "Line B"
    ,
    
        "id" : "idk23321",
        "name" : "Line C"
    
] 

希望对你有帮助

编辑

感谢@Gates VP指出

db.your_collection.find('lines.idk73716':$exists:true)。如果你 想要使用“ids as keys”版本,exists 查询可以工作,但是 它不能被索引

我们仍然可以使用 $exists 来查询 id,但它不会被索引

【讨论】:

非常感谢您提供详细而快速的解决方案。我已经尝试了您的查询,发现不幸的是“db.your_colleection.find(,'lines.idk73716':1)”将查询并返回所有文档,但如果文档有,则仅返回 lines.idk73716 部分一。确实,只选择这一特定行是一个优势,但是由于 过滤器,结果包括集合的所有文档。 @antons,是的,这就是两种方法之间的区别......第二种方法最适合查询。 first 只有在您知道要使用的文档时才有用... 好的,很酷。这是一个很好的结论。就我而言,快速找到包含“idk73716”行的文档更为重要。所以我想说第二种方法可能最适合我的情况。 我认为在此处添加以下内容很重要:`db.your_collection.find('lines.idk73716':$exists:true)`。如果你想使用“ids as keys”版本,exists 查询可以工作,但它不能被索引。 第二种方法还保证lines 将按特定顺序排列。

以上是关于用于嵌入式集合的 MongoDB 首选模式。文档与数组的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB - 如何从集合中查询嵌入式文档

MongoDB- 入门介绍

MongoDB学习笔记:应用程序设计

MongoDB:用于搜索性能的嵌套值与单独的集合 - 数据库模式设计

mongodb与mysql传统的关系数据库区别

用 MongoDB 嵌入集合(子文档数组)违反 REST?