文档式存储模式
- 文档式存储模式概念
文档式存储模式和键值存储模式具有一定的相似性,但其值一般为半结构化内容,需要通过某种半结构化标记语言来进行描述。例如通过JSON或XML等方式来组织其值,键值存储则一般不关心值的结构。不同的元组对应的文档结构可能完全不同。文档中还可能会嵌套文档,以及出现不定长的重复属性,因此文档式存储模式也是无法预先定义结构的。
- 文档式存储模式优点
和键值模式相比,文档式存储模式强调可以通过关键词查询文档内部的结构,而非值通过键来进行检索。此外,由于文档允许嵌套,因此可以将传统关系型数据库中需要Join查询的字段整合为一个文档,这种做法理论上会增加存储开销,但是会提高查询效率。在分布式系统中,Join查询的开销较大,以文档式存储的嵌套结构的又是更加明显。
- 嵌套对象和父子文档
资料来源(https://my.oschina.net/fusublog/blog/3062114)
由于在 Elasticsearch 中单个文档的增删改都是原子性操作,那么将相关实体数据都存储在同一文档中也就理所当然。 比如说,我们可以将订单及其明细数据存储在一个文档中。又比如,我们可以将一篇博客文章的评论以一个 comments 数组的形式和博客文章放在一起:
PUT
{
"title": "Nest eggs",
"body": "Making your money work...",
"tags": [ "cash", "shares" ],
"comments": [
{
"name": "John Smith",
"comment": "Great article",
"age": 28,
"stars": 4,
"date": "2014-09-01"
},
{
"name": "Alice White",
"comment": "More like this please",
"age": 31,
"stars": 5,
"date": "2014-10-22"
}
]
}
如果我们依赖字段自动映射,那么 comments 字段会自动映射为 object 类型。
由于所有的信息都在一个文档中,当我们查询时就没有必要去联合文章和评论文档,查询效率就很高。但是当我们使用如下查询时,上面的文档也会被当做是符合条件的结果:
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "Alice" }},
{ "match": { "age": 28 }}
]
}
}
}
Alice实际是31岁,不是28!
正如我们在 对象数组 中讨论的一样,出现上面这种问题的原因是 JSON 格式的文档被处理成如下的扁平式键值对的结构。
{
"title": [ eggs, nest ],
"body": [ making, money, work, your ],
"tags": [ cash, shares ],
"comments.name": [ alice, john, smith, white ],
"comments.comment": [ article, great, like, more, please, this ],
"comments.age": [ 28, 31 ],
"comments.stars": [ 4, 5 ],
"comments.date": [ 2014-09-01, 2014-10-22 ]
}
Alice 和 31 、 John 和 2014-09-01 之间的相关性信息不再存在。虽然 object 类型 (参见 内部对象) 在存储 单一对象 时非常有用,但对于对象数组的搜索而言,毫无用处。
嵌套对象 就是来解决这个问题的。将 comments 字段类型设置为 nested 而不是 object 后,每一个嵌套对象都会被索引为一个 隐藏的独立文档
在独立索引每一个嵌套对象后,对象中每个字段的相关性得以保留。我们查询时,也仅仅返回那些真正符合条件的文档。不仅如此,由于嵌套文档直接存储在文档内部,查询时嵌套文档和根文档联合成本很低,速度和单独存储几乎一样。
嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身。
父子文档
父-子关系文档在实质上类似于nested model :允许将一个对象实体和另外一个对象实体关联起来。 而这两种类型的主要区别是:在nested objects文档中,所有对象都是在同一个文档中,而在父-子关系文档中,父对象和子对象都是完全独立的文档。
父-子关系的主要作用是允许把一个 type 的文档和另外一个 type 的文档关联起来,构成一对多的关系:一个父文档可以对应多个子文档 。与 nested objects 相比,父-子关系的主要优势有:
1、更新父文档时,不会重新索引子文档。
2、创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时。
3、子文档可以作为搜索结果独立返回。
Elasticsearch 维护了一个父文档和子文档的映射关系,得益于这个映射,父-子文档关联查询操作非常快。但是这个映射也对父-子文档关系有个限制条件:父文档和其所有子文档,都必须要存储在同一个分片中。
父-子文档ID映射存储在 Doc Values 中。当映射完全在内存中时, Doc Values 提供对映射的快速处理能力,另一方面当映射非常大时,可以通过溢出到磁盘提供足够的扩展能力。
当文档索引性能远比查询性能重要的时候,父子关系是非常有用的,但是它也是有巨大代价的。其查询速度会比同等的嵌套查询慢5到10倍
4、JSON
JSON是一种轻量级的数据交换语言。JSON最被熟知的应用之一,是作为JavaScript语言中的对象和数组。JSON也被用来在RESTFUL风格的Web接口中进行数据交换。
JSON的语法规则
JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。JSON是一个序列化的对象或数组。
1. 六个构造字符:
[ 左方括号 、 { 左大括号 、 ] 右方括号 、 } 右大括号 : 冒号 、 , 逗号
2.JSON的值:
JSON值可以是对象、数组、数字、字符串或者三个字面值 (false、null、true)中的一个。值中的字面值中的英文必须使用小 写。
2.1对象由花括号括起来的逗号分割的成员构成,成员是字符 串键和上文所述的值由逗号分割的键值对组成
如:
{"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}
2.2数组是由方括号括起来的一组值构成
如:
[3, 1, 4, 1, 5, 9, 2, 6]
2.3 字符串与C或者Java的字符串非常相似。字符串是由双 引号包围的任意数量Unicode字符的集合,使用反斜线转 义。一个字符(character)即一个单独的字符串(character string)。