elasticsearch中涉及的拍卖

Neil Zhu,简书ID Not_GOD,University AI 创办人 & Chief
Scientist,致力于推进世界人工智能化进程。制定并实施 UAI
中短期增长战略和对象,带领团队连忙成长为人工智能领域最标准的能力。
用作行业老总,他和UAI一起在2014年创造了TASA(中国最早的人工智能协会),
DL Center(深度学习知识基本全球市值网络),AI
growth(行业智库培训)等,为神州的人工智能人才建设输送了汪洋的血流和养分。此外,他还出席仍然举行过各样国际性的人工智能峰会和活动,爆发了了不起的影响力,书写了60万字的人造智能精品技艺内容,生产翻译了海内外率先本深度学习入门书《神经网络与深度学习》,生产的内容被大量的科班垂直公众号和传媒转载与连载。曾经受邀为国内一级大学制定人工智能学习计划和教学人工智能前沿课程,均受学生和教职工好评。

handling relatioships

现实世界里,关系(relationship)是更为重点的:博客作品包含评论,银行账号有对应的贸易,顾客有银行账户,订单也由订单线,而目录则含有文件和子目录。
关系数据库便以此而规划——下面这一个描述对您来说也并不生疏:

  • 每个实体(entity,或者行 row)可以由一个主键唯一识别
  • 实业都是专业了的。对唯一实体的数目只会蕴藏以此,相关的实业则只需要仓储其主键。改变实体的数额只会并发在一个地方
  • NoSQL,实业在询问的时候可以被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与用户不断。indextype
因为他俩在动用中硬编码所以并不曾强制。

在博客作品中询问用户ID为1的就很粗略了:

GET /my_index/blogpost/_search
{
  "query": {
    "filtered": {
      "filter": {
        "term": { "user": 1 }
      }
    }
  }
}

查询用户名是 约翰的博客作品,大家需要周转多少个查询:第一个查找所有叫做约翰的用户得到他们的ID,第二步将这么些ID传入一个查询中收获作者为约翰的稿子

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。

这么些事例中,唯有一个用户匹配了俺们第一询问,但是在具体情形下,大家平常会遭遇百万个以约翰为名的用户。包含所有这个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

网站地图xml地图