赞
踩
原文请参考: https://www.felayman.com/articles/2017/11/10/1510292946325.html
nested结构是Elasticsearch提供关系存储的一种特殊的结构,是NOSQL的一种高级特性,在传统的关系型sql中,很难做到一行记录中存储某个实体以及附属的内容,比如某个用户下评论数据,或某个订单下的所有商品等这种关系比较强的内容。当然传统sql也能做到,比如我们当想存储一个订单和该订单下的商品内容。我们可以定义一个text类型的字段,以json的方式存储不同的商品信息,但是这样有一个致命的缺点,就是性能非常差,其次无法高效的支持搜索。
我们现在模拟这样一个场景。
我们设计了一个博客系统.需要支持搜索该文章的评论信息。
我们当然可以利用传统的sql来做存储,我们可以这样设计文章与评论的表结构。
post表结构:
id,title,body,tags,comment_id
comments表结构:
id,name,comment,age,starts,date
然后利用post表中的comment_id与comments表中的id做外键关联,这样就可以存储文章与评论直接的关系,大部分设计也是如此。
然而问题来了,当我需要对评论内容进行模糊查询的时候,我们可能需要对两个表进行关联查询,
比如我想这样查询:查询title中包含”elasticsearch教程”的文章的评论中包含”视频教程地址”
这个时候我们要面临两个困难,当表稍微大一些的时候,需要对2个表的数据进行like查询,还需要对两个表进行联合查询,这个性能是无法忍受的。
利用Elasticsearch的Nested结构可以很好的解决这个问题,下面是Nested的一些常识,可以参考:
nested结构介绍
PUT /my_index/blogpost/1
{
"title": "Nest eggs",
"body": "Making your money work...",
"tags": [ "cash", "shares" ],
"comments": [ <1>
{
"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"
}
]
}
我们如果直接在marvel插件中这样利用动态映射的话,comments栏位会被自动建立为一个object栏位,因为所有内容都在同一个文档中,使搜寻时并不需要连接(join)blog文章与回应,因此搜寻表现更加优异。
问题在於以上的文档可能会如下所示的匹配一个搜寻:
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "Alice" }},
{ "match": { "age": 28 }} <1>
]
}
}
}
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类型的栏位用于储存单一对象是非常有用的。 从搜寻的角度来看,对於排序一个对象阵列来说关联是不需要的东西。
这是嵌套对象被设计来解决的问题。 藉由映射commments栏位为nested类型而不是object类型, 每个嵌套对象会被索引为一个隐藏分割文档,例如:
{ <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> 第一个嵌套对象
<2> 第二个嵌套对象
<3> 根或是父文档
藉由分别索引每个嵌套对象,对象的栏位中保持了其关联。 我们的查询可以只在同一个嵌套对象都匹配时才回应。
不仅如此,因嵌套对象都被索引了,连接嵌套对象至根文档的查询速度非常快–几乎与查询单一文档一样快。
这些额外的嵌套对象被隐藏起来,我们无法直接访问他们。 为了要新增丶修改或移除一个嵌套对象,我们必须重新索引整个文档。 要牢记搜寻要求的结果并不是只有嵌套对象,而是整个文档。
设定一个nested栏位很简单–在你会设定为object类型的地方,改为nested类型:
PUT /my_index
{
"mappings": {
"blogpost": {
"properties": {
"comments": {
"type": "nested", <1>
"properties": {
"name": { "type": "string" },
"comment": { "type": "string" },
"age": { "type": "short" },
"stars": { "type": "short" },
"date": { "type": "date" }
}
}
}
}
}
}
<1> 一个nested栏位接受与object类型相同的参数。
所需仅此而已。 任何comments对象会被索引为分离嵌套对象。 参考更多 nested type reference docs
因嵌套对象(nested objects)会被索引为分离的隐藏文档,我们不能直接查询它们。而是使用 nested查询或 nested 过滤器来存取它们:
GET /my_index/blogpost/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "eggs" }}, <1>
{
"nested": {
"path": "comments", <2>
"query": {
"bool": {
"must": [ <3>
{ "match": { "comments.name": "john" }},
{ "match": { "comments.age": 28 }}
]
}}}}
]
}}}
<1> title条件运作在根文档上
<2> nested条件深入嵌套的comments栏位。它不会在存取根文档的栏位,或是其他嵌套文档的栏位。
<3> comments.name以及comments.age运作在相同的嵌套文档。
一个nested栏位可以包含其他nested栏位。 相同的,一个nested查询可以包含其他nested查询。 嵌套阶层会如同你预期的运作。
当然,一个nested查询可以匹配多个嵌套文档。 每个文档的匹配会有各自的关联分数,但多个分数必须减少至单一分数才能应用至根文档。
在预设中,它会平均所有嵌套文档匹配的分数。这可以藉由设定score_mode参数为avg, max, sum或甚至none(为了防止根文档永远获得1.0的匹配分数时)来控制。
GET /my_index/blogpost/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "eggs" }},
{
"nested": {
"path": "comments",
"score_mode": "max", <1>
"query": {
"bool": {
"must": [
{ "match": { "comments.name": "john" }},
{ "match": { "comments.age": 28 }}
]
}}}}
]
}}}
<1> 从最匹配的嵌套文档中给予根文档的_score值。
nested过滤器类似於nested查询,除了无法使用score_mode参数。 只能使用在filter context—例如在filtered查询中–其作用类似其他的过滤器: 包含或不包含,但不评分。
nested过滤器的结果本身不会缓存,通常缓存规则会被应用於nested过滤器之中的过滤器。
我们可以依照嵌套栏位中的值来排序,甚至藉由分离嵌套文档中的值。为了使其结果更加有趣,我们加入另一个记录:
PUT /my_index/blogpost/2
{
"title": "Investment secrets",
"body": "What they don't tell you ...",
"tags": [ "shares", "equities" ],
"comments": [
{
"name": "Mary Brown",
"comment": "Lies, lies, l
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。