ElasticSearch是个天然分布式的搜索引擎,有一些核心概念与传统的数据库知识区别很大,这里做一下记录和整理。
分布式相关
集群
一个集群cluster由一个或者多个节点组成,具有相同的cluster.name,协同工作,分项数据和负载。当有新的节点加入或者删除了一个节点时,集群回感知到并能够平衡数据。
ElasticSearch中可以监控很多信息,有一个最重要的就是集群健康。集群健康有三个状态:green(所有主要分片和复制分片都可用),yellow(所有主要分片可用,但不是所有复制分片都可用),red(不是所有的主要分片都可用)。
节点
一个节点node就是一个ElasticSearch的实例。
集群中的一个节点会被选举为主节点master,它将临时管理集群级别的一些变更,譬如新建或者删除索引、增加或者移除节点等等。主节点不参与文档级别的变更或者搜索,所以不会成为集群的瓶颈。任何节点都可以成为主节点。
用户能够与集群中的任何节点通信,包括主节点。每一个节点都知道文档存在于哪个节点上,可以转发请求到相应的节点上。
分片
一个分片shard是一个最小级别的工作单元,仅保存了索引中所有数据的一部分。
分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。
文档存储在分片中,并且在分片中被索引,但是程序不会直接与分片通信,而是与索引通信。
主分片
索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。
索引创建完成时,主分片的数量就固定了,但是复制分片的数量可以随时调整。
复制分片
复制分片仅仅是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者从别的shard取回文档。
元数据相关
一个文档不仅仅有数据,还包含了元数据(关于文档的信息)。三个必须的元数据节点是:_index,_type,_id。
索引
_inde,文档存储的地方,类似于关系型数据中的数据库。
事实上,数据被存储和索引在分片中,索引仅仅是一个把一个或者多个分片分组在一起的逻辑空间。
索引名字必须是全部小写,不允许以下划线开头,不能包含逗号。
类型
_type,对应于关系型数据库中的表。
每个类型type都有自己的映射或者结构定义,就像传统数据库表中的列一样。类型的映射会告诉ElasticSearch文档如何被索引。
_type的名字可以是大写或者小写,不能包含下划线或者逗号。
文档
文档id是一个字符串,与_index和_type组合时,就可以在ElasticSearch中唯一标识一个文档。创建文档时,可以自定义_id,也可以让ES帮助自动生成。
数据操作相关
数据相关的操作有:
索引一个文档;PUT。
检索文档,检索文档的一部分;GET,以及_source参数。
检索文档是否存在;HEAD(返回200或者404)。
更新整个文档;PUT。
创建新的文档;POST(冲突时返回409)。
删除文档;DELETE(返回200或者404)。
文档局部更新;POST,以及_update参数。
检索多个文档;_mget参数指令,docs数组参数。
批量更新;_bulk参数指令,可以包含create,index,update,delete动作。
搜索
ElasticSearch不仅会存储文档,还会索引文档内容使之可以被搜索。
搜索使用_search指令。
返回内容包括hits,took,shards,timeout。
分页参数包括from以及size,要特别关注深度分页。
映射
映射是指数据在每个字段中的解释说明。
映射机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型(string,number,boolean,date等)。
_mapping参数指令用于参数某index下某type的映射情况。
不同类型的字段的索引方式是不同的,因此会导致查询结果不同。
ElasticSearch中的数据大致分为两类:确切值,全文文本。这也是ES区别于别的数据库的根本差异。
确切值
确切值是确定的。
全文文本
全文文本,从某个角度来讲是文本化的数据,通常以人类的语言书写。
倒排索引
倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。词terms,表征tokens。
分词,指的是标记化和标准化的过程。
核心简单字段类型
ElasticSearch支持以下简单字段类型:String(string),Whole Number(byte,short,integer,long),Floating point(float,double),Boolean(boolean),Date(date)。
当索引一个包含新字段的文档,ES将使用动态映射猜测字段类型,这个类型来自于JSON的基本数据类型(注意:如果是一个带引号的数字,ES可能将其猜测为字符串,而不是数字)。
自定义字段映射
多数情况下基本数据类型已经能够满足需求,不过也会需要自定义一些特殊类型,特别是字符串字段类型。自定义类型能够实现如下需求:
- 区分全文字符串字段和确切值字符串字段(即,是否进行分词);
- 使用特定语言的分析器;
- 优化部分匹配字段;
- 指定自定义日期格式;
- 。。。
映射的重要参数
- type:即指定字段的类型;
- index:控制字符串以何种方式被索引;analyzed表示全文文本形式,not_analyzed表示确切值形式 ,no表示不索引(即不能被搜索到)。string类型字段的默认值是analyzed。
- analyzer:指定分析器(譬如whitespace,simple,english)。
复合核心字段类型
除了之前提到的简单标量类型,JSON还有null值,数组和对象;这些类型ES都支持。
多值字段
{"tag": ["elastic", "search"]}
对于数组不需要特殊的映射,任何一个字段可以包含0个,1个或者多个值,同样对于全文本字段将被分析并产生多个值。
同时这意味着数组中的所有值的类型必须统一,ES会使用数组中第一个值的类型来确定新字段的类型。
数组作为一个值的集合被对待,没有顺序。
空字段
数组也可以是空的,这等价于0个值。
Lucene没法存放null值,一个null值得字段会被认为是空字段。
如下的四个字段将被识别为空字段而不被索引。
"empty_string": "",
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]
多层对象
内部对象经常用于在另一个对象中嵌入一个实体或者对象。
ES会动态监测新对象的字段,并映射它们为object类型,将每个字段加到properties字段下。
Lucene并不了解内部对象,一个Lucene文件包含一个键-值对应的扁平表单,所以内嵌对象的属性会通过“.”连接后的属性表示。
对象-数组
内部对象数组在Lucene中也会被扁平化处理,因而每个值的栏位会变成一个值集合,而不是有序的阵列。关联内部对象用来解决嵌套对象的问题。
分析
分析机制用于进行全文本的分词,以建立供搜索使用的反向索引。
分析的过程如下:
1.首先,标记化一个文本块为适用于倒排索引的单独的词term;
2.然后,标准化这些词为标准形式,提供它们的可搜索性或者查全率。
字符过滤器
character filter:字符过滤器的工作是在标记化之前处理字符串,字符过滤器能够去除HTML标记,或者转换“&”为“and”等工作。
分词器
tokenizer:文档被标记化为独立的词,一个简单的分词器可以根据空格或者逗号将单词分开(这个在中文中不适用)。
标记过滤
token filter:分词后每个独立的词都通过标记过滤,标记过期能够修改词(譬如将大写转换为小写,将时态进行转换),去掉词(譬如去掉“a”, “and”, “the”等连词和冠词),或者增加词(譬如同义词)。
分析器
ElasticSearch附带了一些预装的分析器,可以直接使用。
标准分析器
这是ElasticSearch默认使用的分析器。对于文本分析,它对于任何语言都是最佳选择(如果没有特殊需求的话)。标准分析器根据Unicode Consortium定义的单词边界来切分文本,然后去掉大部分的标点符号,最后把所有词转换为小写。
简单分析器
简单分析器将非单个字母的文本切分,然后把每个词转换为小写。
空格分析器
空格分析器依据空格切分文本,不转换大小写。
语言分析器
特定语言分析器适用于很多语言,能够考虑到特定语言的特性。
例如,english分析器自带一套英语停用词库,这些词被移除之后,英语单词的主体含义依旧能被理解。
注:analyze API能够查看文本是如何被分析的。
查询
简单查询语句是一种有效的命令行adhoc查询。
如果要体会搜索的强大,要是用请求体查询API,这种查询的参数以JSON格式容纳而不是查询字符串。
请求体查询并不仅仅用来处理查询,还可以高亮返回结果中的片段,并给出寻找最好结果的相关数据建议。
结构化查询是一种灵活,多表现形式的查询语言。
使用结构化查询,需要传递query参数。
查询子句:
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
查询子句像是搭积木,可以合并简单的子句为一个复杂的查询语句。
- 叶子子句(比如match子句)用于将查询字符串与一个字段或者多个字段进行比较;
- 复合子句(比如bool子句),用于合并其他的子句(使用must,must_not,should等进行连接);
match_all
查询所有文档,是没有查询条件下的默认语句。
match
标准查询,不论需要全文本查询还是精确查询基本上都要用到它。
如果使用match查询一个全文本字段,它会在真正查询之前用分析器先分析一下match的查询字符。
如果使用match查询确切值(譬如数字、日期、布尔值、not_analyzed字符串),ES将会搜索给定值。
multi_match
multi_match查询允许做match查询的基础上同时搜索多个字段。
bool
bool查询与bool过滤相似,用于合并多个查询子句。
不同之处在于:bool过滤可以直接给出是否匹配成功,而bool查询要计算每一个查询子句的_score。
- must:查询指定文档一定要包含;
- must_not:查询指定文档一定不要被包含;
- should:查询指定文档,有则可以为文档相关性加分;
过滤
事实上有两种结构化语句:结构化查询,结构化过滤。二者非常相似,但是它们由于使用目的不同而差异。
一条过滤语句会询问每个文档的字段值是否包含特定值;
一条查询语句则询问每个文档的字段值与特定值得匹配程度如何;查询语句会计算每个文档与查询语句的相关性,给出一个相关性评分_score,并且按照相关性对匹配到的文档进行排序。
原则上来说,使用查询语句来做全文本搜索,或者需要进行相关性评分的时候;其他的情况下应该使用过滤语句。
term
term主要用于精确匹配哪些值,比如数字、日期、布尔值或者not_analyzed的字符串。
terms
terms和term有点类似,但是terms允许指定多个匹配条件。譬如某个字段指定了多个值,那么文档需要一起去做匹配。
range
range过滤允许我们按照指定范围查找一批数据。
操作符包含gt,gte,lt,lte。
exists&missing
exists和missing过滤用于查找文档中是否包含指定字段或者是否没有某个字段。
bool
bool过滤用来合并多个过滤条件的布尔逻辑。
包含如下操作符:
- must:多个查询条件完全匹配,相当于and;
- must_not:多个查询条件的相反匹配,相当于not;
- should:至少有一个查询条件匹配,相当于or;
查询与过滤条件合并
查询语句和过滤语句可以放在各自的上下文中。
在ES API中可以看到许多带有query或者filter的语句,这些语句既可以包含单条query语句,也可以包含一条filter子句;也就是说,这些语句需要首先创建一个query或者filter的上下文关系。
通常情况下,一条查询语句需要过滤语句的辅助,全文本搜索除外。
search API中仅能包含query语句,所以需要用filtered来同时包含query和filter子句。
带过滤的查询语句:
GET /_search
{
"query": {
"filtered": {
"query": { "match": { "email": "business opportunity" }},
"filter": { "term": { "folder": "inbox" }}
}
}
}
查询语句中的过滤:
GET /_search
{
"query": {
"filtered": {
"filter": {
"bool": {
"must": { "term": { "folder": "inbox" }},
"must_not": {
"query": {
"match": { "email": "urgent business proposal" }
}
}
}
}
}
}
}
_validate API可以验证一条查询语句是否合法。