在Elasticsearch中进行创建、删除和更新一个单个的文档的时候它是原子性的操作,所以在同一文档中存储紧密相关的实体是有道理的(百度翻译)。比如将一篇博客和它所有的评论存储在一起,可以通过一个comments数组实现:
PUT /my_index/blogpost/1
{
"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"
}
]
}
如果我们使用动态映射,commets字段将会被自动创建成一个object对象类型的字段
由于所有的内容都在一个文档中,所以在查询的时候不需要对bolg posts和commets进行联合查询,搜索性能也会更好
但是有时候会出现如下的一个问题:
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "comments.name": "Alice" }},
{ "match": { "comments.age": 28 }}
]
}
}
}
我们想搜索评论里name中含有Alice、 age为28的评论者信息,但是搜索结果如下
可以看到JohnSimth和AliceWhite都被搜索了出来,因为John的age与搜索条件中的age为28匹配,Alice与搜索条件中name含有Alice匹配
造成这个的原因是在索引中结构化的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之间的相关性被丢失了,对象类型的字段在存储一个单个的对象时是非常有用的,但是对存储一系列对象数组时就变得没用了。
这个问题可以使用nested对象来解决。将comments映射为一个nested对象类型而不是一个普通的object类型,每个nested对象被索引为隐藏的单独文档,如下所示:
{ //1
"comments.name": [ john, smith ],
"comments.comment": [ article, great ],
"comments.age": [ 28 ],
"comments.stars": [ 4 ],
"comments.date": [ 2014-09-01 ]
}
{ //2
"comments.name": [ alice, white ],
"comments.comment": [ like, more, please, this ],
"comments.age": [ 31 ],
"comments.stars": [ 5 ],
"comments.date": [ 2014-10-22 ]
}
{//3
"title": [ eggs, nest ],
"body": [ making, money, work, your ],
"tags": [ cash, shares ]
}
1是第一个nested对象
2是第二个nested对象
3是根(父)文档
总结:
通过分别对每个嵌套对象进行索引,对象中字段之间的关系可以被维持。我们可以执行一个查询,只有match发生在同一个nested对象时它才会匹配。
不仅如此,由于nested对象被索引的方式,在查询的时候联合nested文档到根文档的速度是非常快的,几乎与查询单个的的文档一样快。
另外,nested文档是隐藏的,我们不能直接访问它。当更新、添加或者删除一个nested对象,必须重新索引整个文档,需要注意的是,发送一个搜索请求时返回的是整个文档而不是只返回nested对象。
翻译自官方文档:https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-objects.html