设为首页 收藏本站
查看: 1493|回复: 0

[经验分享] elasticsearch中关系的处理

[复制链接]

尚未签到

发表于 2017-5-21 08:00:33 | 显示全部楼层 |阅读模式
handling relatioships
  现实世界里,关系(relationship)是尤其重要的:博客文章包含评论,银行账号有相应的交易,顾客有银行账户,订单也由订单线,而目录则包含文件和子目录。
关系数据库便以此而设计——下面这些描述对你来说也并不陌生:


  • 每个实体(entity,或者行 row)可以由一个主键唯一识别
  • 实体都是正规化了的。对唯一实体的数据只会存储以此,相关的实体则只需要存储其主键。改变实体的数据只会出现在一个地方
  • 实体在查询的时候可以被join从而支持多个实体的交叉查询
  • 大多数的关系型数据库支持在多个实体上的ACID事务
  但是关系型数据库除了不支持全文检索外还拥有自身的缺陷。在查询时进行join常常耗资源。依赖不同的硬件执行实体的join不实用。这也就给存放在一个服务器上的数据量造成了一个限制。
  ES,如同大多数的NoSQL数据库,就将显示世界看做是扁平的。index 就是独立的文档的扁平集合。单一的文档应当包含确定其被搜索请求命中所需的信息。
  改变ES中的单一文档的数据是ACID的,包含多个文档的事务却不是。没有办法使得可以保证事务的正常回滚。
  扁平世界的优点;


  • 索引快速且无锁
  • 搜索快速且无锁
  • 海量数据可以分布在若干节点上,因为每个文档都是独立于其他文档。
  然而关系也是重要的。我们需要消除扁平世界和真实世界的隔阂。在 ES 中,四种通用的技术用来管理关系型数据:


  • Application-side joins
  • Data denormalization
  • Nested objects
  • Parent/Child relationships
  而最终的解决方案需要这些技术的混合。

Application-side joins
  我们可以部分地通过在应用中实现 join 模拟关系型数据库。例如,我们在索引用户和用户的博客文章。在关系型世界中,我们可以做下面的动作:

PUT /my_index/user/1
{ "name": "John Smith",
"email": "john@smith.com",
"dob": "1970/10/24"
}
PUT /my_index/blogpost/2
{ "title": "Relationships",
"body": "It's complicated...",
"user": 1
}
  index, type, id整体作为主键
blogpost通过存放用户的 id与用户相连。index和type 因为他们在应用中硬编码所以并没有强制。

  在博客文章中查询用户ID为1的就很简单了:

GET /my_index/blogpost/_search
{
"query": {
"filtered": {
"filter": {
"term": { "user": 1 }
}
}
}
}
  查询用户名是 John 的博客文章,我们需要运行两个查询:第一个查找所有叫做John的用户得到他们的ID,第二步将这些ID传入一个查询中获得作者为John的文章

GET /my_index/user/_search
{ "query": { "match": { "name": "John" } }}
GET /my_index/blogpost/_search
{ "query": { "filtered": { "filter": { "terms": { "user": [1] }  } } } }
  在terms过滤器中的值就是从第一个查询中得到的结果。
  application-side join的主要好处就是数据的正规化。改变用户的名字只会在一个地方:user文档。而其弱点就是你需要执行额外的查询在搜索时进行join。
  这个例子中,只有一个用户匹配了我们第一查询,但是在现实情形下,我们常常会碰到百万个以John为名的用户。包含所有这些 ID 在第二个查询中就产生了一个巨大的查询,包含数百万的term检索。
  这个场景适用于第一个搜索结果比较小的情形,并且最好他们基本不变化。这就使得ES可以缓存结果避免频繁执行第一个查询。

Data denormalization
  从ES获得最佳搜索性能的方法就是通过在索引时的去正规化数据。对每个需要获取的文档保持冗余的拷贝将会去除join的需要。
如果我们希望通过作者名找到博客文章,包含这个用户的名字在该博客文章文档本身即可:

PUT /my_index/user/1
{
"name":     "John Smith",
"email":    "john@smith.com",
"dob":      "1970/10/24"
}
PUT /my_index/blogpost/2
{
"title":    "Relationships",
"body":     "It's complicated...",
"user":     {
"id":       1,
"name": "John Smith"
}
}
  现在,我们既可以通过单个查询找到作者为John的文章了。

GET /my_index/blogpost/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title":     "relationships" }},
{ "match": { "user.name": "John"          }}
]
}
}
}
  data denormalization 的优势是速度。因为每个文档包含需要确定是否满足查询的所有的信息,也就避免了额外的 join。

Field collapsing
  一般要求是用一个特定的字段的 group 来展现搜索结果。我们可能希望返回最相关的博客文章,而使用用户名进行group。根据用户名进行group就代表着对 terms 聚合的要求。为了对用户的全名进行 group,name 字段就应被设置成 not_analyzed 形式,正如在 聚合和分析 中解释的那样:

PUT /my_index/_mapping/blogpost
{
"properties": {
"user": {
"properties": {
"name": {
"type": "string",
"fields": {
"raw": {
"type":  "string",
"index": "not_analyzed"
}
}
}
}
}
}
}
  user.name 字段用作全文检索
user.name.raw 字段用作terms聚合

  然后添加一些数据:

PUT /my_index/user/1
{
"name": "John Smith",
"email": "john@smith.com",
"dob": "1970/10/24"
}
PUT /my_index/blogpost/2
{
"title": "Relationships",
"body": "It's complicated...",
"user": {
"id": 1,
"name": "John Smith"
}
}
PUT /my_index/user/3
{
"name": "Alice John",
"email": "alice@john.com",
"dob": "1979/01/04"
}
PUT /my_index/blogpost/4
{
"title": "Relationships are cool",
"body": "It's not complicated at all...",
"user": {
"id": 3,
"name": "Alice John"
}
}
  现在我们就可以执行一个查询relationships的博客文章了,作者名为John,并使用作者名进行group,使用top_hits聚合

GET /my_index/blogpost/_search?search_type=count
{
"query": {
"bool": {
"must": [
{ "match": { "title":     "relationships" }},
{ "match": { "user.name": "John"          }}
]
}
},
"aggs": {
"users": {
"terms": {
"field":   "user.name.raw",
"order": { "top_score": "desc" }
},
"aggs": {
"top_score": { "max":      { "script":  "_score"           }},
"blogposts": { "top_hits": { "_source": "title", "size": 5 }}
}
}
}
}
  我们感兴趣的博客文章返回在 blogposts 聚合,所以我们可以通过设置 search_type=count 来关闭常用的搜索 hits.
query 返回用户为 John 的关于 relationships 博客文章
terms 聚合对每个 user.name.raw 值创建了一个桶
top_score 聚合将在 users 聚合中的项进行按桶的排序
top_hits 聚合返回对每个用户前5个相关文章

  上面查询的简化结果如下:

...
"hits": {
"total":     2,
"max_score": 0,
"hits":      []
},
"aggregations": {
"users": {
"buckets": [
{
"key":       "John Smith",
"doc_count": 1,
"blogposts": {
"hits": {
"total":     1,
"max_score": 0.35258877,
"hits": [
{
"_index": "my_index",
"_type":  "blogpost",
"_id":    "2",
"_score": 0.35258877,
"_source": {
"title": "Relationships"
}
}
]
}
},
"top_score": {
"value": 0.3525887727737427
}
},
...
  hits 数组为空,因为我们设置了 search_type = count
对每个用户都有一个桶
在每个用户的桶下面都有一个blogposts.hits的数组包含了对那个用户的前列的结果
在每个用户的桶内是按照相关度进行排序的

  使用top_hits聚合等价于执行查询返回用户的名字以及相应最为相关的博客文章,接着对每个用户执行同样地查询,来获得相应最相关的博客文章。但是更加高效。
  在每个桶中返回的前面几条记录是执行一个基于原初主查询的微型查询的结果。这个微查询支持常用的特征,可以通过高亮和分页的特征。

Nested objects

Parent/Child relationships
  http://blog.csdn.net/dm_vincent/article/details/47710367
  http://blog.csdn.net/dm_vincent/article/details/47710591

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-379451-1-1.html 上篇帖子: elasticsearch jest client 注意事项 下篇帖子: Elasticsearch搜索类型(query type)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表