• ES概述
    • 1.1 基本概念
  • ES集群
    • 2.1 集群相关概念
    • 2.2 单机部署
    • 2.3 集群部署
      • 2.3.1 如何分配主分片?
      • 2.3.2 备份片设置多少合适?
      • 2.3.3 集群选举
  • ES集群核心原理
    • 3.1 分片存储
      • 3.1.1 主分片(primary shard)
      • 3.1.2 备份片(replica shard)
    • 3.2 负载均衡
    • 3.3 故障探测
    • 3.4 索引存储
      • 3.4.1 不可变性
      • 3.4.2 存储结构
      • 3.4.3 存储流程
    • 3.5 DOC操作
      • 3.5.1 doc新增
      • 3.5.2 refresh
      • 3.5.3 doc删除(修改)
      • 3.5.3 段合并
    • 3.6 集群路由
      • 3.6.1 document如何路由
    • 3.7 倒序索引
      • 3.7.1 Posting List
      • 3.7.2 Term Dictionary
      • 3.7.3 Term Index
      • 3.7.4 FST(Finite State Transducers)
      • 3.7.5 Frame Of Reference
      • 3.7.5 联合索引
  • ES基础语法
    • 4.1 添加数据
    • 4.2 查询数据
    • 4.3 更新数据
    • 4.4 删除数据
    • 4.5 排序分页
    • 4.6基于Java代码查询
      • 4.6.1 查询所有
      • 4.6.2 解析查询字符串
      • 4.6.3 通配符查询(wildcardQuery)
      • 4.6.4 词条查询(termQuery)
      • 4.6.5 字段匹配查询
      • 4.6.6 只查询ID(标识符查询)
      • 4.6.7 相似度查询
      • 4.6.8 范围查询
      • 4.6.9 组合查询(复杂查询)
      • 4.6.10 排序查询
      • 4.6.11 其他
  • 分词器
    • 5.1 简介
    • 5.2 分类
    • 5.3 指定分词器
    • 5.4 IK分词器
      • 5.4.1 分词划分
      • 5.4.2 扩展词和关键词
  • ES 深度翻页问题解决方案
    • 6.1 scroll
    • 6.2 sliced scroll
    • 6.3 search after

ES概述

Elasticsearch是一个开源的分布式、RESTful 风格的搜索和数据分析引擎,它的底层是开源库Apache Lucene。
EleasticSearch的特点如下:

  • 一个分布式的实时文档存储,每个字段可以被索引与搜索。
  • 一个分布式实时分析搜索引擎。
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据

ES是一个搜索引擎, 同时也是一个分布式文档存储数据库。

1.1 基本概念

数据库
MySQL DB Table Row Colume
ES Index Mapping Document Field
  • 索引Index
    ES中的索引概念不是关系型数据库中的索引, 而是指存数据的地方, 类似于关系型数据库中数据库的概念。

  • 类型Type
    有的文章说ES中的类型Type对应的是关系型数据库中的表, 在使用ES中我们会遇到另外一个概念映射(Mapping), 大部分文档都认为映射(Mapping)才是真正的对应关系型数据库中的表。

    实际上ES中Type的概念已经名存实亡了,后期的版本中越来越被弱化, 在未被ES正式移除之前, ES后期版本已经不允许一个索引Index创建多个Type了, 在ES7中已经移除了Type, 而ES6只允许一个Index创建一个Mapping。

    如果现阶段一定要理解ES中的Type, 那么一定要和Mapping结合起来。可以理解为类型Type就是定义一个表而已, 而映射Mapping定义了表结构, 包括哪些列, 哪些行.

  • 文档Document
    在非关系型数据库中, 有部分被称之为"文档数据库", 对应于关系型数据库中的一行记录。

  • 字段Field
    对应关系型数据库中的列。

ES集群

2.1 集群相关概念

  • 节点
    一个ES实例称之为一个节点, 单机部署的ES有且只有一个节点, 集群部署的ES有多个节点且只有一个主节点。

  • 分片
    ES作为分布式集群部署, 同样也可以作为单机节点部署.。

    ES中数据被分散存储在分片中, ES屏蔽了底层的分片实现, 我们直接与索引交互而不与分片交互。

    分片数量的多少与是否是集群部署和单机部署无关, 即使是单机部署在创建索引的时候也可以制定多个分片(默认是5个主分片, 1个备份(包含5个备分片))。

    分片有主分片和备份片之分, 顾名思义, 备份片是主分片的备份, 当主分片出现故障, 备份片充当主分片。

2.2 单机部署

  • 单机部署的ES, 即ES只有一个节点;
  • 在创建索引时, 如果不指定主分片和备份片的数量, 默认创建5个主分片, 5个备份片;

实际上对于单机而言多个主分片并无多大意义, 因为主备都在一个节点机器上, 如果主分片故障, 备分片也同样会故障.。

2.3 集群部署

  • 集群部署的ES有多个节点,且只有一个主节点。
  • 主节点是可以通过选举产生的,主从节点是对于集群内部来说的。
  • ES是去中心化的,与任何一个节点的通信和与整个ES集群通信是等价的。
  • 提供了联合索引以及可跨所有节点的搜索能力。
  • 拥有冗余能力,它可以在一个或几个节点出现故障时保证服务的整体可用性。
  • 需要确定主分片的分配,备份片机制。

2.3.1 如何分配主分片?

  • 主分片的划分更多是取决于用户的数量和节点的数量。通常来讲, 分片数量越多越好, 因为这样能将数据分散到不同分片, 以便于以后在扩容新增节点时, ES能自动将分片重新均匀分布。

  • 但这也不是绝对的, 例如有3个节点, 100个分片, 每个节点就33个分片, 当搜索请求调度到同一节点的不同分片时, 此时就会引发硬件资源争夺, 造成性能问题。

  • 反过来, 如果3个节点只分配3个分片, 随着业务增长, 数据量增大, 单个分片已不能承载它最大的数量, 此时就算新增节点, 但是分片数量就3个, 主分片的数量在创建索引时便确定且不可修改, 此时只能重新创建索引。

  • 既要对合理的数据增长有一个判断, 又要对期望有一度的把握。官方给出的建议, 每个分片的数量最好在20G~40G。

  • 这就意味着如果你有4个节点, 数量预估在200G左右甚至更大, 此时分片数量设置为5~10个比较合适, 7或8个差不多, 每个节点2个分片。

2.3.2 备份片设置多少合适?

  • 上面谈到主分片, 副分片划分同等重要。
  • 如果不对主分片备份, 主分片故障则会导致数据丢失, 部分数据不可查询。
  • 副本分片设置过多会造成额外的存储空间。
  • 默认情况下, 创建索引时会创建一个分片副本。

2.3.3 集群选举

  • 如果同时启动, 按照nodeId进行排序, 取出最小的做为master节点。
  • 如果不是同时启动, 则先启动的候选master节点, 会竞选为master节点。
  • 节点完成选举后, 新节点加入, 会发送join request 到master节点。默认会重试20次。
  • 如果宏机, 集群node会再次进行ping过程, 并选择一个新的master。
  • 一旦一个节点被明确设为一个客户端节点(node.client设置为true), 则不能在成为主节点(node.master会自动设为false)。

相关配置:

# 如果`node.master`设置为了false,则该节点没资格参与`master`选举。
node.master = true
# 默认3秒,最好增加这个参数值,避免网络慢或者拥塞,确保集群启动稳定性
discovery.zen.ping_timeout: 3s
# 用于控制选举行为发生的集群最小master节点数量,防止脑裂现象(= 节点数/2+1)
discovery.zen.minimum_master_nodes : 2
# 新节点加入集群的等待时间
discovery.zen.join_timeout : 10s
# 当集群中没有活动的Master节点后,该设置指定了哪些操作(read、write)需要被拒绝(即阻塞执行)。有两个设置值:all和write,默认为wirte。
discovery.zen.no_master_block : write

节点类型:

#配置文件中给出了三种配置高性能集群拓扑结构的模式,如下:
#1. 如果你想让节点从不选举为主节点,只用来存储数据,可作为负载器
node.master: false
node.data: true
#2. 如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器
node.master: true
node.data: false
#3. 如果想让节点既不称为主节点,又不成为数据节点,那么可将他作为搜索器,从节点中获取数据,生成搜索结果等
node.master: false
node.data: false

ES集群核心原理

3.1 分片存储

  • ES的“分片(shard)”机制可将一个索引内部的数据分布地存储于多个节点上。
  • 它通过将一个索引切分为多个底层物理的Lucene索引完成索引数据的分割存储功能。
  • 这每一个物理的Lucene索引称为一个分片(shard)。
  • 分片分布到不同的节点上。构成分布式搜索。
  • 每个分片其内部都是一个全功能且独立的索引,因此可由集群中的任何主机存储。
  • 创建索引时,用户可指定其分片的数量,默认数量为5个。
  • 分片的数量只能在索引创建前指定,并且索引创建后不能更改。
  • Shard有两种类型:primary和replica,即主shard及副本shard。

3.1.1 主分片(primary shard)

  • 用于文档存储。
  • 每个新的索引会自动创建5个Primary shard,当然此数量可在索引创建之前通过配置自行定义。
  • 一旦创建完成,其Primary shard的数量将不可更改。

3.1.2 备份片(replica shard)

  • 是Primary Shard的副本,用于冗余数据及提高搜索性能。
  • 每个Primary shard默认配置了一个Replica shard,但也可以配置多个,且其数量可动态更改。
  • ES会根据需要自动增加或减少这些Replica shard的数量。

副本的作用

  1. 提高系统的容错性,当个某个节点某个分片损坏或丢失时可以从副本中恢复。
  2. 提高es的查询效率,es会自动对搜索请求进行负载均衡。

3.2 负载均衡

  1. 每个索引被分成了5个分片;
  2. 每个分片有一个副本;
  3. 5个分片基本均匀分布在datanode节点上;

如果其中一个datanode节点故障, 剩余的分片将会重新进行均衡分配。

3.3 故障探测

ES有两种集群故障探查机制:

  1. 通过master进行的,master会ping集群中所有的其他node,确保它们是否是存活着的。
  2. 每个node都会去ping master来确保master是存活的,否则会发起一个选举过程。

有下面三个参数用来配置集群故障的探查过程:

ping_interval : 每隔多长时间会ping一次node,默认是1s
ping_timeout : 每次ping的timeout等待时长是多长时间,默认是30s
ping_retries : 如果一个node被ping多少次都失败了,就会认为node故障,默认是3次

3.4 索引存储

3.4.1 不可变性

  • 倒排索引被写入磁盘后是不可改变的,它永远不会修改。

为什么索引被写入磁盘后不可改变?有什么优点和缺点?

优点

  • 提升查询性能。 一旦索引被读入内核的文件系统缓存,便会留在那里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。

缺点

  • 想修改一个Doc,那就必须重建整个待排索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。

解决方案

  • ES解决不变形和更新索引的方式是使用多个索引,利用新增的索引来反映修改,在查询时从旧的到新的依次查询,最后来一个结果合并。这就是段产生的由来。

3.4.2 存储结构

  • ES底层是基于Lucene,最核心的概念就是Segment(段),每个段本身就是一个倒排索引。
  • ES中的 Index 由多个段的集合和 commit point (提交点)文件组成。
  • 提交点文件中有一个列表存放着所有已知的段。

3.4.3 存储流程

  1. 当一个写请求发送到 es 后,es 将数据写入 memory buffer 中,并添加事务日志( translog ),此时写入的数据还不能被查询到。
  2. 默认设置下,es 每1秒钟将 memory buffer 中的数据 refresh 到 Linux 的 File system cache ,并清 空 memory buffer,此时写入的数据就可以被查询到了。
  3. 默认设置下,es 每30分钟调用 fsync 将 File system cache 中的数据 flush 到硬盘。
  4. translog 默认设置下,每一个 index 、delete、 update 或 bulk 请求都会直接 fsync 写入硬盘。

:为什么需要File System cache ?
:如果每次一条数据写入内存后立即写到硬盘文件上,由于写入的数据肯定是离散的,因此写入硬盘的操作也就是随机写入了。硬盘随机写入的效率相当低,会严重降低es的性能。

:问什么需要Translog?
:File system cache 依然是内存数据,一旦断电,则 File system cache 中的数据全部丢失。因此需要通过translog 来保证即使因为断电 File system cache 数据丢失,es 重启后也能通过日志回放找回丢失的数据。

为了保证 translog 不丢失数据,在每一次请求之后执行 fsync 确实会带来一些性能问题。对于一些允许丢失几秒钟数据的场景下,可以通过设置 index.translog.durability 和index.translog.sync_interval 参数让 translog 每隔一段时间才调用 fsync 将事务日志数据写入硬盘。

3.5 DOC操作

3.5.1 doc新增

Doc会先被搜集到内存中的Buffer内,这个时候还无法被搜索到,每隔一段时间,会将buffer提交,在flush磁盘后打开新段使得搜索可见,详细过程如下:

1)创建一个新段(segment),作为一个追加的倒排索引,写入到磁盘(文件系统缓存)
2)将新的包含新段的Commit Point(提交点)写入磁盘(文件系统缓存)
3)磁盘进行fsync,主要是将文件系统缓存中等待的写入操作全部物理写入到磁盘,保证数据不会在发生错误时丢失
4)这个新的段被开启, 使得段内文档对搜索可见
5)将内存中buffer清除,又可以把新的Doc写入buffer了

通过这种方式,可以使得新文档从被索引到可被搜索间的时间间隔在数分钟,但是还不够快。因为磁盘需要 fsync ,这个就成为性能瓶颈。我们前面提到过Doc会先被从buffer刷入段写入文件系统缓存(很快),那么就自然想到在这个阶段就让文档对搜索可见,随后再被刷入磁盘(较慢)。

3.5.2 refresh

Lucene支持对新段写入和打开-可以使文档在没有完全刷入硬盘的状态下就能对搜索可见,而且是一个开销较小的操作,可以频繁进行。 这种对新段的巧妙操作过程被称为refresh,默认执行的时间间隔是1秒,这就是ES被称为近实时搜索的原因。

可以使用refreshAPI进行手动操作,但一般不建议这么做。还可以通过合理设置refresh_interval在近实时搜索和索引速度间做权衡。

3.5.3 doc删除(修改)

  • 删除一个ES文档不会立即从磁盘上移除,它只是被标记成已删除。
  • 因为段是不可变的,所以文档既不能从旧的段中移除,旧的段也不能更新以反映文档最新的版本。
  • ES的做法是,每一个提交点包括一个.del文件(还包括新段),包含了段上已经被标记为删除状态的文档。
  • 当一个文档被做删除操作,实际上只在.del文件中将该文档标记为删除,依然会在查询时被匹配到,只不过在最终返回结果之前会被从结果中删除。
  • ES将会在用户之后添加更多索引的时候,在后台进行要删除内容的清理。

问题:而且每秒自动刷新创建新的段,用不了多久段的数量就爆炸了,每个段消费大量文件句柄,内存,cpu资源。更重要的是,每次搜索请求都需要依次检查每个段。段越多,查询越慢。

解决方案:ES通过后台合并段解决这个问题。

3.5.3 段合并

ES利用段合并的时机来真正从文件系统删除那些version较老或者是被标记为删除的文档。被删除的文档(或者是version较老的)不会再被合并到新的更大的段中。

ES对一个不断有数据写入的索引处理流程如下: 索引过程中,refresh会不断创建新的段,并打开它们。 合并过程会在后台选择一些小的段合并成大的段,这个过程不会中断索引和搜索。

段合并之前,旧有的Commit和没Commit的小段皆可被搜索。
段合并后的操作: 新的段flush到硬盘 编写一个包含新段的新提交点,并排除旧的较小段。 新的段打开供搜索 旧的段被删除 合并完成后新的段可被搜索,旧的段被删除。

3.6 集群路由

  1. 客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点);
  2. coordinating node,对document进行路由,将请求转发给对应的node(有primary shard);
  3. 实际的node上的primary shard处理请求,然后将数据同步到replica node;
  4. coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端。

3.6.1 document如何路由

路由算法公式:shard = hash(routing)%number_of_primary_shards。

例子: 一个索引index ,有3个primary shard : p0,p1,p2 增删改查 一个document文档时候,都会传递一个参数 routing number, 默认就是document文档 _id,(也可以手动指定) Routing = _id,假设: _id = 1 算法: Hash(1) = 21 % 3 = 0 表示 请求被 路由到 p0分片上面。

自定义路由请求
PUT /index/item/id?routing = _id (默认)
PUT /index/item/id?routing = user_id(自定义路由)---- 指定把某些值固定路由到某个分片上面。

primary shard不可变原因 即使加服务器也不能改变主分片的数量

3.7 倒序索引


例子:表的数据如下

ID Name Age Sex
1 Kate 24 Female
2 John 24 Male
3 Bill 29 Male

ES 存储如下:

Name:

Term Posting List
Kate 1
John 2
Bill 3

Age:

Term Posting List
24 [1,2]
29 3

Sex:

Term Posting List
Female 1
Male [2,3]

3.7.1 Posting List

  1. Elasticsearch分别为每个field都建立了一个倒排索引;
  2. Kate, John, 24, Female这些叫term;
  3. 而[1,2]就是Posting List;
  4. Posting list就是一个int的数组,存储了所有符合某个term的文档id。

3.7.2 Term Dictionary

问题:通过posting list这种索引方式似乎可以很快进行查找,但是,如果这里有上千万的记录呢?如果是想通过name来查找呢?

答案:Elasticsearch为了能快速找到某个term,将所有的term排个序,二分法查找term,logN的查找效率,就像通过字典查找一样,这就是Term Dictionary。

3.7.3 Term Index

问题:Term Dictionary看起来似乎和传统数据库通过B-Tree的方式类似啊,为什么说比B-Tree的查询快呢?
答案:B-Tree通过减少磁盘寻道次数来提高查询性能,Elasticsearch也是采用同样的思路,直接通过内存查找term,不读磁盘,但是如果term太多,term dictionary也会很大,放内存不现实,于是有了Term Index,就像字典里的索引页一样,A开头的有哪些term,分别在哪页,可以理解term index是一颗树:

这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。

所以term index不需要存下所有的term,而仅仅是他们的一些前缀与Term Dictionary的block之间的映射关系,再结合FST(Finite State Transducers)的压缩技术,可以使term index缓存到内存中。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘随机读的次数。

3.7.4 FST(Finite State Transducers)

FSTs are finite-state machines that map a term (byte sequence) to an arbitrary output。

  • ⭕️表示一种状态
  • –>表示状态的变化过程
  • 字母/数字表示状态变化和权重
  1. 将单词分成单个字母通过⭕️和–>表示出来,0权重不显示。
  2. 如果⭕️后面出现分支,就标记权重;
  3. 最后整条路径上的权重加起来就是这个单词对应的序号。

FST以字节的方式存储所有的term,这种压缩方式可以有效的缩减存储空间,使得term index足以放进内存,但这种方式也会导致查找时需要更多的CPU资源。

3.7.5 Frame Of Reference

增量编码压缩,将大数变小数,按字节存储。

首先,Elasticsearch要求posting list是有序的,这样做的一个好处是方便压缩。

原理就是通过增量,将原来的大数变成小数仅存储增量值,再精打细算按bit排好队,最后通过字节存储,而不是大大咧咧的尽管是2也是用int(4个字节)来存储。

还有一种压缩技术:Roaring bitmaps

3.7.5 联合索引

多个field索引的联合查询,倒排索引如何满足快速查询的要求呢?

  • 利用跳表(Skip list)的数据结构快速做“与”运算。
  • 利用bitset按位“与”。

跳表的数据结构

将一个有序链表level0,挑出其中几个元素到level1及level2,每个level越往上,选出来的指针元素越少,查找时依次从高level往低查找,比如55,先找到level2的31,再找到level1的47,最后找到55,一共3次查找,查找效率和2叉树的效率相当,但也是用了一定的空间冗余来换取的。

如果使用bitset,就很直观了,直接按位与,得到的结果就是最后的交集。

ES基础语法

4.1 添加数据

PUT /humen/user/1
{"first_name" : "John","last_name" :  "Smith","age" :        25,"about" :      "I love to go rock climbing","interests": [ "sports", "music" ]
}

返回以下信息,表示创建成功。

{"_index": "humen","_type": "user","_id": "1","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 0,"_primary_term": 1
}

_index 为索引名称。
_type 为类型名称。
_version 为该文档的版本号,初始为 1,每次修改该文档时 _version 增加 1。
_seq_no 为索引的版本号,每次修改了索引下的文档,被修改的文档的 _seq_no 和索引当前的 _seq_no 都被更新为修改前索引 _seq_no + 1。

注意: 当请求方法为 POST,且修改前后数据完全一样时,文档的 _version 和 _seq_no 都不改变。

这种请求方式的写法为:POST /index/{index}/index/{type}/${_id}/_update,请求体格式为:

{"doc":{"first_name": "Bob"}
}

_id 为添加数据时自己设置的,如上 PUT /humen/user/1 中的 1 就是自己设置的字段 _id,也可以在添加字段的时候不设置 _id 值,这时系统会默认返回一个字符串类型的 _id 值。

注意: 此时请求方式不能为 PUT,应当为 POST,否则会返回 405 报错。

4.2 查询数据

查询索引为 humen下的全部文档。

GET /humen/_search

查询索引为 humen 下的类型名为 user 的全部文档。

GET /humen/user/_search

根据 _id 来准确查询文档。

GET /humen/user/_search/1

为查询方法加上参数,参数的 key 和 value 之间用 : 分隔。

GET /humen/user/_search?q=last_name:Smith

将查询参数放在请求体中。

GET /humen/user/_search
{"query" : {"match" : {"last_name" : "Smith"}}
}

4.3 更新数据

更新数据和插入数据又可以用 PUT 和 POST 两种请求方式,且两种请求方式都可以用一下格式来更新数据:

  • PUT/POST /humen/user/_id
    请求方法体同插入数据, 逻辑上等同于将用数据将原数据覆盖掉。如:
{"first_name" : "John","last_name" :  "Smith","age" :        25,"about" :      "I love to go rock climbing","interests": [ "sports", "music" ]
}
  • POST 还可以通过 POST /megacorp/employee/_id/_update
    格式来修改数据,此时请求体格式应当为:
{"doc":{"first_name": "Taoqiang"}
}

将要修改的字段和新的值放在 doc 中,多个字段用逗号分隔。

4.4 删除数据

  • 删除整个索引的数据:DELETE /humen
  • 删除指定 _id 的数据:DELETE /humen/user/_id

正确删除后返回以下类型数据:

{"_index": "megacorp","_type": "employee","_id": "5","_version": 2,"result": "deleted","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 11,"_primary_term": 1
}

4.5 排序分页

GET /test2/product/_search
{"query": {"match": {"name": "test"}},"sort": [{"age": {"order": "desc"}}],#从第几条开始"from": 0,#获取几条数据"size": 2#from + size其实就是limit from,size}

4.6基于Java代码查询

方法名 作用
matchAllQuery 匹配所有文档
queryStringQuery 基于Lucene的字段检索
wildcardQuery 通配符查询匹配多个字符,?匹配1个字符*
termQuery 词条查询
matchQuery 字段查询
idsQuery 标识符查询
fuzzyQuery 文档相似度查询
includeLower includeUpper 范围查询
boolQuery 组合查询(复杂查询)
SortOrder 排序查询

4.6.1 查询所有

/***matchAllQuery()匹配所有文件match_all查询是Elasticsearch中最简单的查询之一。它使我们能够匹配索引中的所有文件* */
@Test
public void searchAll(){SearchResponse searchResponse = client.prepareSearch("humen").setTypes("user").setQuery(QueryBuilders.matchAllQuery()).get();​ // 获取命中次数,查询结果有多少对象SearchHits hits = searchResponse.getHits(); System.out.println("查询结果" + hits.getTotalHits() + "条");Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}

4.6.2 解析查询字符串

/*** 相比其他可用的查询,query_string查询支持全部的Apache Lucene查询语法针对多字段的query_string查询* */
@Test
public void query_String(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.queryStringQuery("孙尚香")).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}
​

4.6.3 通配符查询(wildcardQuery)

/*** *匹配多个字符,?匹配1个字符       注意:避免* 开始, 会检索大量内容造成效率缓慢* */
@Test
public void wildcardQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.wildcardQuery("address", "广东*")).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}
​

4.6.4 词条查询(termQuery)

/*** 词条查询是Elasticsearch中的一个简单查询。它仅匹配在给定字段中含有该词条的文档,而且是确切的、未经分析的词条* */
@Test
public void termQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.termsQuery("name", "张飞","刘备","关羽")).get(); // 获取命中次数,查询结果有多少对象SearchHits hits = searchResponse.getHits();Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}
​

4.6.5 字段匹配查询

/***  match query搜索的时候,首先会解析查询字符串,进行分词,然后查询 。而term query,输入的查询内容是什么,就会按照什么去查询,并不会解析查询内容,对它分词。* multiMatchQuery("text", "field1", "field2"..); 匹配多个字段* */
@Test
public void MatchQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.matchQuery("address", " 上海")).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象System.out.println("查询结果有:" + hits.getTotalHits() + "条");Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}

4.6.6 只查询ID(标识符查询)

/*** 按照id进行查询,通过id返回我们想要的结果* */
@Test
public void idsQuery() {SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.idsQuery().addIds("AWNkQSCJzU0_wTuf7egi")).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}​

4.6.7 相似度查询

/*** 相似度查询:fuzzy查询是模糊查询中的第三种类型,它基于编辑距离算法来匹配文档* */
@Test
public void fuzzyQuery(){SearchResponse searchResponse = client.prepareSearch("tt1").setTypes("doc1").setQuery(QueryBuilders.fuzzyQuery("address", "北京市")).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象System.out.println("查询结果有:" + hits.getTotalHits() + "条");Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}​

4.6.8 范围查询

includeLower(true):包含上界IncludeUpper(true):包含下界/**范围查询使我们能够找到在某一字段值在某个范围里的文档,字段可以是数值型,也可以是基于字符串的includeLower(true):包含上界IncludeUpper(true):包含下界* */
@Test
public void rangeQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.rangeQuery("age").from(18).to(22) .includeLower(true).includeUpper(false)).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}

4.6.9 组合查询(复杂查询)

  /** 组合查询:*  must(QueryBuilders) : ANDmustNot(QueryBuilders): NOTshould(QueryBuilders):OR
​* */@Testpublic void boolQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("address", "程序员")).must(QueryBuilders.termQuery("male", "女"))).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}}​

4.6.10 排序查询

/*** ASC : 正序(从小到大)* DESC: 倒序(从大到小)* */
@Test
public void SortOrderQuery(){SearchResponse searchResponse = client.prepareSearch("sanguo").setTypes("dahan").setQuery(QueryBuilders.matchAllQuery()).addSort("age", SortOrder.ASC).get();SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit next = iterator.next();System.out.println("===============================================");}
}

4.6.11 其他

// 高亮设置
private static final String HIGH_LIGHT_PRE_TAG = "<span style=\"color: #F56B6B\">";
private static final String HIGH_LIGHT_POST_TAG = "</span>";
HighlightBuilder highlightBuilder = new HighlightBuilder().field("field1").field("field2").field("field13").preTags(HIGH_LIGHT_PRE_TAG).postTags(HIGH_LIGHT_POST_TAG);queryBuilder.withHighlightBuilder(highlightBuilder);

分词器

5.1 简介

ES的分词器(Analyzer)一般由三种组件构成:

  1. character filter (字符过滤器):在一段文本分词之前,先进行预处理,比如说最常见的就是 【过滤html标签】,hello --> hello,I & you --> I and you
  2. tokenizers (分词器):默认情况下,英文分词根据空格将单词分开;中文分词按单字隔开,也可以采用机器学习算法来分词。
  3. Token filters (Token过滤器):将切分的单词进行加工,大小写转换,去掉停用词(例如“a”、“and”、“the”等等 ),加入同义词(例如同义词像“jump”和“leap”)

三者的顺序:Character Filters—>Tokenizer—>Token Filter
三者个数:Character Filters(0个或多个) + Tokenizer + Token Filters(0个或多个)

5.2 分类

  • Standard Analyzer - 默认分词器,英文按单词切分,小写处理,过滤标点符号。
  • Simple Analyzer - 按照单词切分(符号被过滤), 小写处理,中文按照标点符号(逗号、句号、包括空格等等)进行分词
  • Stop Analyzer - 小写处理,停用词过滤(the,a,is)
  • Whitespace Analyzer - 按照空格切分,不转小写 , 不去掉标点符号
  • Keyword Analyzer - 不分词,直接将输入当作输出
  • 中文分词器: smartCN、IK 等,推荐的就是 IK分词器。需要安装IK。

5.3 指定分词器

创建索引的时候就要指定分词器

PUT /索引名
{"settings": {},"mappings": {"properties": {"title":{"type": "text","analyzer": "standard" //显示指定分词器}}}
}

5.4 IK分词器

5.4.1 分词划分

IK分词器有两种粒度划分:

  • ik_smart: 会做最粗粒度的拆分
  • ik_max_word: 会将文本做最细粒度的拆分
POST /_analyze
{"text":"中华民族共和国国歌","analyzer":"ik_smart"
}{"tokens" : [{"token" : "中华民族","start_offset" : 0,"end_offset" : 4,"type" : "CN_WORD","position" : 0},{"token" : "共和国","start_offset" : 4,"end_offset" : 7,"type" : "CN_WORD","position" : 1},{"token" : "国歌","start_offset" : 7,"end_offset" : 9,"type" : "CN_WORD","position" : 2}]
}
POST /_analyze
{"text":"中华民族共和国国歌","analyzer":"ik_max_word"
}{"tokens" : [{"token" : "中华民族","start_offset" : 0,"end_offset" : 4,"type" : "CN_WORD","position" : 0},{"token" : "中华","start_offset" : 0,"end_offset" : 2,"type" : "CN_WORD","position" : 1},{"token" : "民族","start_offset" : 2,"end_offset" : 4,"type" : "CN_WORD","position" : 2},{"token" : "共和国","start_offset" : 4,"end_offset" : 7,"type" : "CN_WORD","position" : 3},{"token" : "共和","start_offset" : 4,"end_offset" : 6,"type" : "CN_WORD","position" : 4},{"token" : "国","start_offset" : 6,"end_offset" : 7,"type" : "CN_CHAR","position" : 5},{"token" : "国歌","start_offset" : 7,"end_offset" : 9,"type" : "CN_WORD","position" : 6}]
}

5.4.2 扩展词和关键词

  • 扩展词:就是有些词并不是关键词,但是也希望被ES用来作为检索的关键词,可以将这些词加入扩展词典。
  • 停用词:就是有些关键词,我们并不想让他被检索到,可以放入停用词典中。

设置扩展词典和停用词典在es容器中的config/analysis-ik目录下的IKAnalyzer.cfg.xml中

  1. 修改vim IKAnalyzer.cfg.xml
    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"><properties><comment>IK Analyzer 扩展配置</comment><!--用户可以在这里配置自己的扩展字典 --><entry key="ext_dict">ext_dict.dic</entry><!--用户可以在这里配置自己的扩展停止词字典--><entry key="ext_stopwords">ext_stopword.dic</entry></properties>
  1. 在es容器中config/analysis-ik目录下中创建ext_dict.dic文件 编码一定要为UTF-8才能生效
    vim ext_dict.dic 加入扩展词即可

  2. 在es容器中config/analysis-ik目录中创建ext_stopword.dic文件
    vim ext_stopword.dic 加入停用词即可

  3. 重启es生效。

ES 深度翻页问题解决方案

ES提供了3中解决深度翻页的操作,分别是scroll、sliced scroll 和 search after。

6.1 scroll

scroll api提供了一个全局深度翻页的操作, 首次请求会返回一个scroll_id,使用该scroll_id可以顺序获取下一批次的数据;scroll 请求不能用来做用户端的实时请求,只能用来做线下大量数据的翻页处理,例如数据的导出、迁移和_reindex操作,还有同一个scroll_id无法并行处理数据,所以处理完全部的数据执行时间会稍长一些。
例如:

POST /twitter/_search?scroll=1m
{"size": 100,"query": {"match" : {"title" : "elasticsearch"}}
}

其中scroll=1m是指scroll_id保留上下文的时间。
首次请求会返回一个scroll_id,我们根据这个值去不断拉取下一页直至没有结果返回:

POST /_search/scroll
{"scroll" : "1m", "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

针对scroll api下,同一个scroll_id无法并行处理数据的问题,es又推出了sliced scroll,与scroll api的区别是sliced scroll可以通过切片的方式指定多scroll并行处理。

6.2 sliced scroll

sliced scroll api 除指定上下文保留时间外,还需要指定最大切片和当前切片,最大切片数据一般和shard数一致或者小于shard数,每个切片的scroll操作和scroll api的操作是一致的:

GET /twitter/_search?scroll=1m
{"slice": {"id": 0, "max": 2 },"query": {"match" : {"title" : "elasticsearch"}}
}
GET /twitter/_search?scroll=1m
{"slice": {"id": 1,"max": 2},"query": {"match" : {"title" : "elasticsearch"}}
}

因为支持并行处理,执行时间要比scroll快很多。

6.3 search after

上面两种翻页的方式都无法支撑用户在线高并发操作,search_after提供了一种动态指针的方案,即基于上一页排序值检索下一页实现动态分页:

GET twitter/_search
{"size": 10,"query": {"match" : {"title" : "elasticsearch"}},"sort": [{"date": "asc"},{"tie_breaker_id": "asc"}      ]
}

因为是动态指针,所以不需要像scroll api那样指定上下文保留时间了。
通过上一页返回的date + tie_breaker_id最后一个值做为这一页的search_after:

GET twitter/_search
{"size": 10,"query": {"match" : {"title" : "elasticsearch"}},"search_after": [1463538857, "654323"],"sort": [{"_score": "desc"},{"tie_breaker_id": "asc"}]
}

EleasticSearch相关推荐

  1. 数据治理展示血缘关系的工具_Nebula Graph 在微众银行数据治理业务的实践

    本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...

  2. Ubuntu 16.04下部署Graylog日志服务器

    Graylog 是一个开源的日志管理系统,集中式收集.索引.分析其它服务器发来的日志.它是由 Java 语言编写的,能够接收 TCP.UDP.AMQP 协议发送的日志信息,并且使用 Mongodb 做 ...

  3. elasticsearch v6.5.4配置

    elasticsearch是一款知名的开源全文搜索引擎,应用广泛,因项目需要,需要使用elasticsearch满足应用内搜索,地图搜索.目前还在线上试运营,根据自己的使用部署过程,分享一下经验,梳理 ...

  4. Elasticearch 安装 基础介绍 (一)

    文章目录 1 介绍 2 ELK安装 2.1 Elasticearch安装 2.1.1 windows 安装 2.1.2 linxu安装 2.2 可视化界面 elasticsearch-head 2.3 ...

  5. python接口 同花顺_利用python探索股票市场数据指南

    虽然同花顺之类的金融理财应用的数据足够好了,但还是有自己定制的冲动, 数据自然不会不会比前者好很多,但是按照自己的想法来定制还是不错的. 目标 通过免费的数据接口获取数据,每日增量更新标的历史交易数据 ...

  6. Elastic:配置 Elasticsearch 服务器 logs

    当我们运行 Elasticsearch 集群时,我们可以看到一些 Elasticsearch 的日志,比如我们可以在如下的目录中找到相应的一些 server 日志: <cluster name& ...

  7. ELK之logstash

    下载安装(Redhat/Centos7) rpm --import http://packages.elasticsearch.org/GPG-KEY-elasticsearch cat > / ...

  8. 【ElasticSearch系列】ES插件安装

    上篇文章介绍了一下EleasticSearch以及安装,这篇文章继续,将介绍ES的插件安装. 其实最开始我也不知道要安装什么插件,其实也疑惑,为什么ES不将需要的插件集成到自身,这样就能避免很多问题. ...

  9. Docker学习文档(个人向)

    Docker日常使用文档 1.为什么是docker 在开发的时候,在本机测试环境可以跑,生产环境跑不起来 这里我们拿java Web应用程序举例,我们一个java Web应用程序涉及很多东西,比如jd ...

最新文章

  1. 【工具类】分布式文件存储-FastDFS
  2. Web Service概念梳理
  3. [BZOJ1030] [JSOI2007]文本生成器
  4. mybatis-plus的概念
  5. file 创建 txt文件
  6. 移动边缘计算与计算卸载概述
  7. MSM8953 Android9.0 配置USB2.0 Camera
  8. 免费url长网址缩短压缩工具评测,短链接在线生成器推荐。
  9. Matlab中的ismember和contains傻傻分不清
  10. 【普通人VS程序员】电脑还可以这样关机,神操作,学到了学到了~(爆赞)
  11. 用Qt在Iinux上开发一个带UI的工业控制系统,应该用C++还是QML
  12. Go 语言 入门 基于 GoLand 2023.1 创建第一个Go程序
  13. 冬季12种食物减肥巧妙止饿
  14. 深入理解同步工具类,看这篇就够了
  15. 信息学奥林匹克,精品大学AP课程
  16. xd生成html,7款Adobe XD必备插件
  17. windows下nginx配置OpenSSL自签名证书
  18. 小程序复制文字、保存图片
  19. qs.js库 使用方法
  20. 1710AL电台参数设置流程

热门文章

  1. 洛伦茨曲线半高全宽_关于MatLab如何取某变函数半高全宽(FWHM)并Plot的问题
  2. java命令截图,GitHub - xuege-cn/fmj: FMJ (FFMpeg for Java)。通过Java调用FFMpeg命令的方式来对音视频进行处理(获取信息、截图等等)。...
  3. GDOI2018 游记
  4. 原创 | 国内首批工业互联网十大双跨平台建设现状如何?
  5. 面向对象的五大基本原则
  6. 【沃顿商学院学习笔记】商业基础——Financing:04通货膨胀 Inflation
  7. 竟还有如此沙雕的代码注释!我笑喷了
  8. 计算机组成原理:区分机器字长、存储字长、指令字长 | 位、字节、字与字长
  9. 数据结构之逻辑结构和物理结构
  10. 南理工cs夏令营面试