博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Elasticsearch——利用Parent-Child关系解决大数据场景下的实时查询
阅读量:7208 次
发布时间:2019-06-29

本文共 3160 字,大约阅读时间需要 10 分钟。

表与表之间的关联基本上是所有业务系统都存在的,RDBMS通过外键实现,MongoDB通过嵌入式子文档解决,那么Elasticsearch怎么解决这个问题呢?答案就是Parent-Child关联()

业务场景

有一个广告的分发系统,为了更精准的做广告的推送,除了自身积累的数据以外,还会从其他合作方通过数据交换(当然这些都是脱敏的数据)的方式获取更多用户行为数据,例如从音乐网站获取听的音乐列表、从购物网站获取最近的购物类别、从书评网站获取最近浏览的图书等等。这些来自于外部的数据,有以下几个问题:

  1. 并不是每个用户都有全部的数据,比如有些用户只有书评和音乐信息,而有些用户没有任何外部信息
  2. 某一类外部的数据源可能包含几个网站,比如音乐网站有A、B、C三个网站,它们提供的数据格式也并不一致

在进行广告推送时,需要实时查询一个用户的信息完成精准推荐。比如实时查询满足下面条件的用户:

  • 最近一个月,
  • 经常在早上、傍晚或者晚上连续一个小时的音乐;
  • 购买过跑鞋、运动手表等跑步装备
  • 且购买过或点评过运动类书籍

再继续下面的(十分简化)解决方案之前,可以先思考下

解决方案

这是典型应用大数据进行个性化精准推荐的应用场景,在省却了数据清洗、评分等各种步骤以后,简化为一个查询问题。分析可以发现数据问题的核心就是:无固定表结构,是典型的Schema-Free的NoSQL应用场景,第一个反应出来的就是MongoDB。

MongoDB

MongoDB用作以上的数据存储,毫无疑问具有天然的优势,可以将每个来源的数据都作为user的一个子文档存储,查询时也只是在这一个Collection上进行(可能有人会说这种方案太蠢了,的确是,不过也要看产品所处的阶段)。当然这样做的问题也显而易见:

为了查询速度,索引是必须要创建的。可是因为数据源不断变化,那么索引的维护就会变成一个灾难。一旦忘记创建查询,可能就会拖死整个系统。

下面当然就是主角上场了。

Elasticsearch

定调:

1. 由于字段是变化,因此必须使用动态Mapping()
2. 由于Parent-Child的关系需要创建索引()时就确定,因此必须使用固定的Mapping()

我又检查了上面两条,的确是没有说错。

其实很简单,在创建索引时,只需指定父子关系,无需指定其他未知字段。因为要预先指定type的父子关系,所以就必须先确定type。这是用两个type:user和user_action,那么创建索引时的Mapping大致如下:

{  "mappings": {    "user": {},    "user_action": {      "_parent" : {        "type": "user"      }    }  }}

我好像把文档中的例子抄了一遍,不多实际情况的确是这样。

那么在添加文档到索引中时,对于user就需要指定id,而user_action需要指定parent,例如:

es = Elasticsearch()_id = 27_user = {  'id': 27,  'name': 'Tigger Fei'}# 索引用户文档es.index(index='user_index', doc_type='user', id=str(_id), body=_user)# 索引用户行为文档, type字段表示列表# 音乐_music = {  'type': 'music',  'user': 27,  'period': 'morning',  'duration': 78,  'category': 'running',  'time': '2017-01-29 12:30:00'}es.index(index='user_index', doc_type='user_action', parent=str(_id), body=_music)# 图书,_book = {  'type': 'book,'  'user': 27,  'name': '我的第一个马拉松',  'category': 'running',  'time': '2017-01-30 12:30:00'}es.index(index='user_index', doc_type='user_action', parent=str(_id), body=_book)

如何完成上面的查询呢,如下:

POST user_index/user/_search{  "query": {    "bool": {      "filter": [        {          "has_child": {            "type": "user_action",            "query": {               "bool": {                 "filter": [                  {
"term": {
"type": "music"}}, {
"range": {
"duration": {
"gte": 60}}}, {
"range": { "time": { "gte": "2017-01-07 00:00:00", "format": "yyyy-MM-dd HH:mm:ss" } }}, {
"term": {
"category": "running"}}, {
"terms": {
"period": ["morning", "night"]}} ] } } } }, { "has_child": { "type": "user_action", "query": { "bool": { "filter": [ {
"range": { "time": { "gte": "2017-01-07 00:00:00", "format": "yyyy-MM-dd HH:mm:ss" } }}, {
"term": {
"type": "book"}}, {
"term": {
"category": "running"}} ] } } } } ] } }}

好了,这个简单的解决方案就完了。

转载地址:http://tmgum.baihongyu.com/

你可能感兴趣的文章
【HDU】6148 Valley Numer 数位DP
查看>>
windows下使用hbase/opencv/ffmpeg小记
查看>>
有害的“这样效率最高”思维
查看>>
CodeForces 438D 线段树 剪枝
查看>>
PG,PL,SE,PM都是什么意思,职责划分
查看>>
MyEclipse8.6安装svn(非link方式)
查看>>
SecureCRT恢复默认字体
查看>>
Ubuntu下安装、卸载notepad++
查看>>
Windows Azure上的Odoo(OpenERP)-1.创建Ubuntu虚拟机,安装PostgreSQL 数据库
查看>>
C# 网卡IP(网上资料整理)
查看>>
html网站网址一键打包成App平台-开心App平台
查看>>
使用react的一点提醒17/10/26
查看>>
Java 内部类的阐述
查看>>
redis-大key寻找
查看>>
EF 数据查询(更改默认排序)
查看>>
求连续子数组的最大和
查看>>
SpringMVC最简单配置应用
查看>>
jQuery与Zepto的异同
查看>>
jsp注册页面的省份联动(网上copy别人的,然后自己弄了一下才知道怎么用)
查看>>
CRC检错技术原理
查看>>