探索elasticsearch(a personal study record)

Neil Zhu,简书ID Not_GOD,University AI 创始人 & Chief Scientist,致力于推进世界人工智能化进程。制定并实施 UAI 中长期增长战略和目标,带领团队快速成长为人工智能领域最专业的力量。
作为行业领导者,他和UAI一起在2014年创建了TASA(中国最早的人工智能社团), DL Center(深度学习知识中心全球价值网络),AI growth(行业智库培训)等,为中国的人工智能人才建设输送了大量的血液和养分。此外,他还参与或者举办过各类国际性的人工智能峰会和活动,产生了巨大的影响力,书写了60万字的人工智能精品技术内容,生产翻译了全球第一本深度学习入门书《神经网络与深度学习》,生产的内容被大量的专业垂直公众号和媒体转载与连载。曾经受邀为国内顶尖大学制定人工智能学习规划和教授人工智能前沿课程,均受学生和老师好评。

探索elasticsearch

by Andrew Cholakian

相比较官方文档,这本书很好看。

第二章 建模数据

2.1 文档和字段的基础知识

在elasticsearch中的最小独立单元是字段(field),字段有一个已定义的类型(type),并且拥有一个或者多个该类型的值。字段包含一个简单的数据块,如数字42或者字符串"Hello, World!",亦或一个同样类型数据的简单表(single list),如[5,6,7,8]

文档(document)是字段的聚合(collection),并且包含了elasticsearch中的基本存储单元,就像在传统的RDBMS中的行(row)那样。将文档作为一个基本的存储单元的原因是,与Lucene不同的是,所有完全更新的字段重写一个给定的文档进存储设备(同时保留哪些没有变更的字段)。所以,当我们从一个API角度看,字段是最小的单元,而文档则是从存储角度的最小单元。

2.1.1 JSON——ES的语言

在ES中,所有的文档必须为合法的JSON值。例如

{
  "_id": 1,
  "handle": "ron",
  "age": 28,
  "hobbies": ["hacking", "the great outdoors"],
  "computer": {"cpu": "pentium pro", "mhz": 200}
}

Elasticsearch reserves some fields for special use. We’ve specified one of these fields in this example: the _id field. A document’s id is unique, and if unassigned will be created automatically. An elasticsearch id would be a primary key in RDBMS parlance.

While elasticsearch deals with JSON exclusively, internally, the JSON is converted to flat fields for Lucene’s key/value API. Arrays in documents are mapped to Lucene multi-values.

2.2 基本类型

2.2.1 概述

每个ES中的文档肯定要遵循一个用户定义的类型映射,类似于数据库的模式(schema)。一个类型的映射同时包含了字段的类型(integer、string……)和这些属性将被如何索引的方式(这较复杂的部分在后面会介绍)。
多个在一个索引的文档可能拥有相同的ID只要这些文档的类型是不同的。

类型使用Mapping API定义,其将type name和property定义进行关联。图2.1最小版本的映射如下:

{
  "user": {
      "properties": {
          "handle": {"type": "string"},
          "age": {"type": "integer"},
          "hobbies": {"type": "string"},
          "computer": {
              "properties": {
                  "cpu": {"type": "string"},
                  "speed": {"type": "integer"}}}}}}

这里相当于数据库模式的定义

2.2.2 数据类型

在一个mapping的属性部分,每个字段都与一个不同的core类型关联。合法的core数据类型如下表所示。除了关联数据类型到字段外,类型映射定义了诸如analysis设定和默认的boosting值的属性;后续章节会介绍。

Type Definition
string Text
integer 32 bit integers
long 64 bit integers
float IEEE float
double Double precision floats
boolean true or false
date UTC Date/Time (JodaTime)
geo_point Latitude / Longitude

2.2.3 数组,对象和高级类型

复杂JSON类型同样被ES支持,使用数组和对象表示。另外的,ES文档可以处理更加复杂的关系,诸如parent/child关系,和一个嵌套的闻到那个类型。

注意ES中数组不支持混合类型的元素。如果一个field被声明为一个整数,它可以存储一个或者多个整数,不过不能参杂其他类型的元素。
An important thing to remember, however, is that elasticsearch arrays cannot store mixed types. If a field is declared as an integer, it can store one or many integers, but never a mix of types.

As seen in figure 2.2, mappings may describe objects which contain other objects, as in the computer field in that figure. The key thing to remember with these objects is that their properties are declared in the properties field in the mapping, and that the type field is omitted.
当使用对象的时候,type字段被省略。

子对象仍然存储在同样的物理文档中。所以,还有一个特殊的nested类型,尽管相似,但是拥有不同的性能和查询特征,因为被分别存储在不同的文档中。(也就是不同的记录row中)。后面将介绍parent/child文档。

2.3 索引的基础

在ES中最大的独立的数据单元是index。索引是文档的逻辑和物理划分(partition)。文档和文档类型对于index来说是唯一的。索引不知道包含在其他索引中的数据。从可操作角度看,需要性能和持久性(durability)相关的选项(option)被设置为per-index层面。
从查询的角度,当ES支持交叉索引搜索时,通常in practice it usually makes more organizational sense to design for searches against individual indexes.

ES索引是在一个独立的运行时服务器实例一个完全划分的universe。文档和类型映射是以index范围内进行的。所以跨索引重用名称和id是安全的。索引同样拥有供集群复制,sharding,定制文本分析和其他考虑的本身的设置。

ES的索引不是与Lucene索引一一对应的。实际上,他们是一个Lucene索引的集合(通常设为5)对shard有一个备份。一个单一的机器可能相比较其他的机器对同一个索引有或多或少的shards。ES尽可能地保持所有索引的全部数据在所有的机器上都一致,即使那意味着某些索引可能会在某台机器上不成比例地出现。每个shard拥有一个可配置数目地全replica,这总是存储在唯一地实例上。如果cluster不足够大来支撑指定数量地replica,cluster health会被报告为恶化的(‘yellow’)状态。基本的ES开发安装因此在使用默认索引运行。一个单一运行的实例没有peer来复制其数据。注意这对于开发过程没有实际的影响。然后,建议大家ES在产品环境中运行在多个服务器上。正如一个clustered数据库,数据的保证依赖于多个节点的可用上。
我们在下个section:Basic CRUD介绍基本使用index的操作命令

2.4 CRUD基础

Let’s perform some basic operations on data. Elasticsearch is RESTish in design and tends to match HTTP verbs up to the Create, Read, Update, and Delete operations that are fundamental to most databases. We’ll create an index, then a type, and finally a document within that index using that type. Open up elastic-hammer, and we’ll issue the following operations from figure 2.3.

F 2.3 Simple CRUD

// Create a type called 'hacker'
PUT /planet/hacker/_mapping
{
  "hacker": {
    "properties": {
      "handle": {"type": "string"},
      "age": {"type": "long"}}}}
// Create a document
PUT /planet/hacker/1
{"handle": "jean-michel", "age": 18}
// Retrieve the document
GET /planet/hacker/1
// Update the document's age field
POST /planet/hacker/1/_update
{"doc": {"age": 19}}
// Delete the document
DELETE /planet/hacker/1
fig-simplecrud
// Create an index named 'planet'
PUT /planet

在上面的例子中,我们可以看到ES全部的CRUD生命周期。现在我们可以对数据执行一些基本的操作。现在就到了最为关键的地方了——搜索。注意URL模式与这些操作是一致的,大多数的URL形如/index/type/docid,使用一个下划线前缀作为特定操作的名字空间。

database ACID

  • atomicity;
  • consistency;
  • isolation;
  • durability:
* if a flight booking reports that a seat has successfully been booked, then the seat will remain booked even if the system crashes. In distributed transactions, all participating servers must coordinate before commit can be acknowledged. This is usually done by a two-phase commit protocol.
* Many DBMSs implement durability by writing transactions into a transaction log that can be reprocessed to recreate the system state right before any later failure. A transaction is deemed committed only after it is entered in the log.

shard
shard是Lucene的独立的实例,这个工作单元是由elasticsearch自动管理的。一个index是一个逻辑的名字空间,其指向主从shards。但是我们不需要关注一个索引如何定义主从shard的数量,不会直接接触到shard,只需要直接处理index。ES将shards分发给cluster中所有的节点。,并且可以在node失败时或者新增nodes时自动地将shards从一个node移向另外一个。

第三章 搜索数据

3.1 搜索API

搜索API通常使用/indexindex/type后接_search作为路径。索引搜索可能是/myidx/_search,而对独立文档类型的范围搜索可以是/myidx/mytype/_search。搜索API的作用是调用一个查询,其参数可以是结果集合的最大数,结果偏移位置,和一些性能指标选项。搜索API同样提供Faceting和过滤(Filtering),这些在后面会介绍到。

下面展示了搜索API的骨架。例子中只有size和query参数被设置,没有facet或者filter被应用。例子很简单。_search后缀可以使用GETPOSTHTTP方法。
Simple Query

{
  "size": 3,
    "query": {
        "match": {"hobbies": "skateboard"}
     }}

Query DSL用来确定哪些文档匹配了指定的限定。同样对文档进行排序,通过他们的相似度来做排序的基准,这在Lucene中是标准的技术术语。相似度度值通常被称文档的打分(score)。The Query DSL is employed as the contents of either the query key in JSON posted to the _search endpoint as in the example above.

3.1.1 Mixing查询、过滤器和Facets

transportclient, node client

3.2 分析

3.2.1 文本分析

分析自然是最为重要的和神奇的特性了,可以处理自然语言及复杂数据。

下面出现的问题,就是当数据存储在文档中时,知道哪里和什么时候使用分析,然后在每次查询过来时,根据那个字段的分析规则进行匹配。文档进行分析过程如下:

  • 文档的更新和创建使用PUT或者POST
  • 文档中字段的值通过一个分析器后被转化成零个、1个或者多个可索引的token
  • 被token化的值存放在索引中个,指向了这个文档的全文

3.2.2 分析API

The easiest way to see analysis in action is with the Analyze API, which lets you test pieces of text against any analyzer. To test the words “candles” and “candle” for instance, against a snowball analyzer, you would issue the query in Figure 3.6.
使用分析API是看分析过程的最佳方法,你可以对分析器测试各种文档输入。例如测试词“candles”和“candle”,使用一个snowball分析器:
F 3.6 使用分析API

GET '/_analyze?analyzer=snowball&text=candles%20candle&pretty=true'
在这种情形下,我们可以分析“candles candle”来展示两个相似的词是如何被分析的。你会得到下面的结果:

F 3.7 Analysis API Output

{
  "tokens" : [ {
    "token" : "candl",
    "start_offset" : 0,
    "end_offset" : 7,
    "type" : "<ALPHANUM>",
    "position" : 1
  }, {
    "token" : "candl",
    "start_offset" : 8,
    "end_offset" : 14,
    "type" : "<ALPHANUM>",
    "position" : 2
  } ]
}

分析API在试着区分为何一些词完全一样的token化还有其他没有的时候是最有价值的。如果你被不知道如何解决没有匹配的查询难住,首先你可以使用分析API来试试一些文本

定制分析器

分析器是典型的三步走:

  • Character Filtering: 将输入字符串转化为一个不同的字符串
  • Tokenization: 将char过滤后的字符串转化为一个token的数组
  • Token Filtering: 将过滤过的token转化为一个mutated token数组

3.3 基于相似度的排名

现在我们走入了ES的核心部分。搜索是两大步

  1. 匹配所有满足要求的文档。这是IR领域的布尔搜索,因为这步只是存在与否的的衡量。
  2. 第二步则是基于与查询的相似度进行打分,然后以打分的倒序展示结果。

有多个可配置的相似度算法。本章介绍默认的TFIDF相似度类。这是相当著名的打分算法,在Lucene的TFIDFSimilarity类中实现。

基本思想:
文档的分数高当:

  • 被匹配的term是“罕见的”,指这个term比其他term在更少的文档中出现。
  • term以一个很高的频率出现在该文档中
  • 如果多个term在查询中,该文档比其他文档包含更多的查询term
  • 字段field或文档在索引时指定了boost因子或者查询时间(query time)。

上面的列表是极其简化的相似度计算的思想。请牢记的是,所有的打分因子之间关系不都是线性的,还有一些比较微妙的地方。然而,大多数时间,Lucene已经做了你想要做的,而不要你自己计算TF/IDF。而且,ES支持可配置的相似度算法,如BM25算法。如果给你的算法包对你作用不大,你自己可以写一个script查询,这样可以根据自己的想法对文档进行打分,或者写一个实现你自己的打分算法的java插件。

一般来说,TF/IDF、显式的排序(如按日期降序)、和脚本查询,将给你很好的效果。

分面

faceted search

3.4.1 什么是分面(Faceting)

聚合统计是ES的一个核心部分,可以通过Search API进行调用。分面(facet)总是跟一个查询一起,让我们返回正常查询结果和聚合统计。想象一个用户使用电影名来查询电影。使用分面搜索你可以给出结果中不同类属的聚合数量。如果你曾经在一个电商网站上进行过搜索,你可能会在sidebar处看到一个下拉选项。
分面是高度可定制的,同样也是可以复合的。除了统计不同字段的值外,分面可以使用更加复杂的群组特性,例如时间跨度、嵌套过滤器、甚至全面、嵌套的elasticsearch查询。

Filtering

filter对搜索的执行path有着明显的影响。当查询给出哪些文档出现在结果中,以及他们如何被打分,过滤器只会给出哪些文档出现在结果中。这个可以获得一个极其快速的查询。另外,一些限制可以通过过滤器指定。过滤器可以将结果集进行切分,且不需要执行代价昂贵的打分计算。过滤器也可以用来,当一个term必须要匹配但是其对文档整体分数的影响应该是一个确定的量而不管TF/IDF分值。最后,不同与查询的是,filters可以被缓存,当filter被重复使用时,导致了一个明显的性能提升。

elasticsearch有三种filter出现方式。控制filter应用在query和factes上,查询或者自查询,或者facet。下面列表说明

  • Queries of the filtered/constant_score类型:这些都内嵌在query字段中,filters将影响查询结果和facet计数。
  • top-level filter element: 确定一个filter在搜索的根,只会过滤查询,但不会影响facet
  • facets with facet_filter选项:给每个facet添加可选的facet_filter元素,这个可以用来在数据被聚合之前预先过滤数据。这个过滤器将只会影响定义在其里面的facet,不会影响查询结果。

3.5.1 过滤的三种不同过程

使用filtered和constant_score_quries

这两种类型均允许嵌套一个regular查询。过滤器先运行,然后查询和任何的的facets,如果filter足够强地限制了结果集合,这将千载地提供了一个有效的安全的速度提升。

// Load Dataset: products.eloader
POST /products/_search
{
  "facets": {"department": {"terms": {"field": "department_name"}}},
  "query": {
    "filtered": {
      "query": {"match": {"name": "fake"}},
      "filter": {"term": {"department_name": "Books"}}}}}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,201评论 1 300
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,387评论 1 257
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 94,683评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,080评论 0 179
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,865评论 1 255
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,810评论 1 177
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,440评论 2 271
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,200评论 0 167
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,019评论 6 231
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,540评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,337评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,658评论 1 231
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,255评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,145评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,535评论 3 206
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,626评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,028评论 0 166
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,555评论 2 230
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,652评论 2 231

推荐阅读更多精彩内容