谷粒商城微服务分布式高级篇ElasticSearch二——重要概念及原理

面向文档

应用中的对象很少只是简单的键值列表,更多时候它拥有复杂的数据结构,比如包含日期、地理位置、另一个对象或者数组。

总有一天你会想到把这些对象存储到数据库中。将这些数据保存到由行和列组成的关系数据库中,就好像是把一个丰富,信息表现力强的对象拆散了放入一个非常大的表格中:你不得不拆散对象以适应表模式(通常一列表示一个字段),然后又不得不在查询的时候重建它们。

Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一。

ELasticsearch使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。它简洁、简单且容易阅读。

以下使用JSON文档来表示一个用户对象:

{
    "email":      "john@smith.com",
    "first_name": "John",
    "last_name":  "Smith",
    "info": {
        "bio":         "Eco-warrior and defender of the weak",
        "age":         25,
        "interests": [ "dolphins", "whales" ]
    },
    "join_date": "2014/05/01"
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

尽管原始的user对象很复杂,但它的结构和对象的含义已经被完整的体现在JSON中了,在Elasticsearch中将对象转化为JSON并做索引要比在表结构中做相同的事情简单的多。

NOTE

尽管几乎所有的语言都有相应的模块用于将任意数据结构转换为JSON,但每种语言处理细节不同。具体请查看“serialization” or“marshalling”两个用于处理JSON的模块。Elasticsearch官方客户端会自动为你序列化和反序列化JSON。

索引

在Elasticsearch中存储数据的行为就叫做索引(indexing),不过在索引之前,我们需要明确数据应该存储在哪里。

在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields

   
   
  • 1
  • 2

Elasticsearch集群可以包含多个索引(indices)(数据库),每一个索引可以包含多个类型(types)(),每一个类型包含多个文档(documents)(行)),然后每个文档包含多个字段(Fields)()

索引含义的区分
你可能已经注意到索引(index)这个词在Elasticsearch中有着不同的含义,所以有必要在此做一下区分:

索引(名词) 如上文所述,一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是indices 或indexes

索引(动词) 索引一个文档表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的INSERT关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档

倒排索引: 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。默认情况下,文档中的所有字段都会被索引(拥有一个倒排索引),只有这样他们才是可被搜索的

将数据内容打碎成关键字,将索引建立在内容关键字上,通过关键字关联主键,叫做倒排索引

   
   
  • 1

分布式特性

Elasticsearch为分布式而生,而且它的设计隐藏了分布式本身的复杂性。

Elasticsearch致力于隐藏分布式系统的复杂性。以下这些操作都是在底层自动完成的:

1、将你的文档分区到不同的容器或者分片(shards)中,它们可以存在于一个或多个节点中。
2、将分片均匀的分配到各个节点,对索引和搜索做负载均衡。
3、冗余每一个分片,防止硬件故障造成的数据丢失。
4、将集群中任意一个节点上的请求路由到相应数据所在的节点。
5、无论是增加节点,还是移除节点,分片都可以做到无缝的扩展和迁移

Elasticsearch用于构建高可用和可扩展的系统。扩展的方式可以是购买更好的服务器(纵向扩展(vertical scale or scaling up))或者购买更多的服务器(横向扩展(horizontal scale or scaling out)

对于大多数数据库而言,横向扩展意味着你的程序将做非常大的改动才能利用这些新添加的设备。对比来说,Elasticsearch天生就是分布式的:它知道如何管理节点来提供高扩展和高可用。这意味着你的程序不需要关心这些

分布式集群

如果我们启动一个单独的节点,它还没有数据和索引,这个集群看起来就像图
在这里插入图片描述
一个节点(node)就是一个Elasticsearch实例,而一个集群(cluster)由一个或多个节点组成,它们具有相同的cluster.name,它们协同工作,分享数据和负载。当加入新的节点或者删除一个节点时,集群就会感知到并平衡数据。

集群中一个节点会被选举为主节点(master),它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,这意味着在流量增长的时候,该主节点不会成为集群的瓶颈。任何节点都可以成为主节点。我们例子中的集群只有一个节点,所以它会充当主节点的角色。

做为用户,我们能够与集群中的任何节点通信,包括主节点。每一个节点都知道文档存在于哪个节点上,它们可以转发请求到相应的节点上。我们访问的节点负责收集各节点返回的数据,最后一起返回给客户端。这一切都由Elasticsearch处理.

集群健康

在Elasticsearch集群中可以监控统计很多信息,但是只有一个是最重要的:集群健康(cluster health)。集群健康有三种状态:green、yellow或red。

GET /_cluster/health

   
   
  • 1

在一个没有索引的空集群中运行如上查询,将返回这些信息:

{
   "cluster_name":          "elasticsearch",
   "status":                "green", <1>
   "timed_out":             false,
   "number_of_nodes":       1,
   "number_of_data_nodes":  1,
   "active_primary_shards": 0,
   "active_shards":         0,
   "relocating_shards":     0,
   "initializing_shards":   0,
   "unassigned_shards":     0
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

status 是我们最感兴趣的字段
status字段提供一个综合的指标来表示集群的的服务状况。三种颜色各自的含义:

颜色意义
green所有主要分片和复制分片都可用
yellow所有主要分片可用,但不是所有复制分片都可用
red不是所有的主要分片都可用

添加索引

为了将数据添加到Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上, 索引只是一个用来指向一个或多个分片(shards)的“逻辑命名空间(logical namespace)”.

一个分片(shard)是一个最小级别“工作单元(worker unit)”,它只是保存了索引中所有数据的一部分。
分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。
文档存储在分片中,并且在分片中被索引,但是我们的应用程序不会直接与它们通信,取而代之的是直接与索引通信。

分片是Elasticsearch在集群中分发数据的关键。把分片想象成数据的容器。文档存储在分片中,然后分片分配到你集群中的节点上。当你的集群扩容或缩小, Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。

分片可以是 主分片(primary shard)或者是复制分片(replica shard)。你索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。

理论上主分片能存储的数据大小是没有限制的,限制取决于你实际的使用情况。分片的最大容量完全取决于你的使用状况:硬件存储的大小、文档的大小和复杂度、如何索引和查询你的文档,以及你期望的响应时间。

复制分片只是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者从别的shard取回文档。

当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整

让我们在集群中唯一一个空节点上创建一个叫做blogs的索引。默认情况下,一个索引被分配5个主分片,但是为了演示的目的,我们只分配3个主分片和一个复制分片(每个主分片都有一个复制分片):

PUT /blogs
{
   "settings" : {
      "number_of_shards" : 3,
      "number_of_replicas" : 1
   }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

附带索引的单一节点集群:
在这里插入图片描述
我们的集群现在看起来就像上图——三个主分片都被分配到Node 1。如果我们现在检查集群健康(cluster-health),我们将见到以下信息:

{
   "cluster_name":          "elasticsearch",
   "status":                "yellow", <1>
   "timed_out":             false,
   "number_of_nodes":       1,
   "number_of_data_nodes":  1,
   "active_primary_shards": 3,
   "active_shards":         3,
   "relocating_shards":     0,
   "initializing_shards":   0,
   "unassigned_shards":     3 <2>
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

集群的健康状态yellow表示所有的主分片(primary shards)启动并且正常运行了——集群已经可以正常处理任何请求——但是复制分片(replica shards)还没有全部可用。事实上所有的三个复制分片现在都是unassigned状态——它们还未被分配给节点。在同一个节点上保存相同的数据副本是没有必要的,如果这个节点故障了,那所有的数据副本也会丢失。

现在我们的集群已经功能完备,但是依旧存在因硬件故障而导致数据丢失的风险

增加故障转移

在单一节点上运行意味着有单点故障的风险——没有数据备份。幸运的是,要防止单点故障,我们唯一需要做的就是启动另一个节点。

为了测试在增加第二个节点后发生了什么,你可以使用与第一个节点相同的方式启动第二个节点,一个节点可以启动多个Elasticsearch实例

只要第二个节点与第一个节点有相同的cluster.name(请看./config/elasticsearch.yml文件),它就能自动发现并加入第一个节点所在的集群。如果没有,检查日志找出哪里出了问题。这可能是网络广播被禁用,或者防火墙阻止了节点通信。

如果我们启动了第二个节点,这个集群看起来就像下图。
双节点集群——所有的主分片和复制分片都已分配:
在这里插入图片描述

第二个节点已经加入集群,三个复制分片(replica shards)也已经被分配了——分别对应三个主分片,这意味着在丢失任意一个节点的情况下依旧可以保证数据的完整性。
文档的索引将首先被存储在主分片中,然后并发复制到对应的复制节点上。这可以确保我们的数据在主节点和复制节点上都可以被检索。
cluster-health现在的状态是green,这意味着所有的6个分片(三个主分片和三个复制分片)都已可用:

{
   "cluster_name":          "elasticsearch",
   "status":                "green", <1>
   "timed_out":             false,
   "number_of_nodes":       2,
   "number_of_data_nodes":  2,
   "active_primary_shards": 3,
   "active_shards":         6,
   "relocating_shards":     0,
   "initializing_shards":   0,
   "unassigned_shards":     0
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

集群的状态是green
我们的集群不仅是功能完备的,而且是高可用的。

横向扩展

随着应用需求的增长,我们该如何扩展?如果我们启动第三个节点,我们的集群会重新组织自己,就像图4:

图4:包含3个节点的集群——分片已经被重新分配以平衡负载:
在这里插入图片描述

Node3包含了分别来自Node 1和Node 2的一个分片,这样每个节点就有两个分片,和之前相比少了一个,这意味着每个节点上的分片将获得更多的硬件资源(CPU、RAM、I/O)。

分片本身就是一个完整的搜索引擎,它可以使用单一节点的所有资源。我们拥有6个分片(3个主分片和三个复制分片),最多可以扩展到6个节点,每个节点上有一个分片,每个分片可以100%使用这个节点的资源。

继续扩展

如果我们要扩展到6个以上的节点,要怎么做?

主分片的数量在创建索引时已经确定。实际上,这个数量定义了能存储到索引里数据的最大数量(实际的数量取决于你的数据、硬件和应用场景)。然而,主分片或者复制分片都可以处理读请求——搜索或文档检索,所以数据的冗余越多,我们能处理的搜索吞吐量就越大。

复制分片的数量可以在运行中的集群中动态地变更,这允许我们可以根据需求扩大或者缩小规模。让我们把复制分片的数量从原来的1增加到2:

PUT /blogs/_settings
{
   "number_of_replicas" : 2
}

 
 
  • 1
  • 2
  • 3
  • 4

图5:增加number_of_replicas到2:
在这里插入图片描述

从图中可以看出,blogs索引现在有9个分片:3个主分片和6个复制分片。这意味着我们能够扩展到9个节点,再次变成每个节点一个分片。这样使我们的搜索性能相比原始的三节点集群增加三倍。

当然,在同样数量的节点上增加更多的复制分片并不能提高性能,因为这样做的话平均每个分片的所占有的硬件资源就减少了(译者注:大部分请求都聚集到了分片少的节点,导致一个节点吞吐量太大,反而降低性能),你需要增加硬件来提高吞吐量。

不过这些额外的复制节点使我们有更多的冗余:通过以上对节点的设置,我们能够承受两个节点故障而不丢失数据。

应对故障

Elasticsearch可以应对节点失效,所以让我们继续尝试。如果我们杀掉第一个节点的进程(以下简称杀掉节点),我们的集群看起来就像这样:
在这里插入图片描述
我们杀掉的节点是一个主节点。一个集群必须要有一个主节点才能使其功能正常,所以集群做的第一件事就是各节点选举了一个新的主节点:Node 2。

主分片1和2在我们杀掉Node 1时已经丢失,我们的索引在丢失主分片时不能正常工作。如果此时我们检查集群健康,我们将看到状态red:不是所有主分片都可用!

幸运的是丢失的两个主分片的完整拷贝存在于其他节点上,所以新主节点做的第一件事是把这些在Node 2和Node 3上的复制分片升级为主分片,这时集群健康回到yellow状态。这个提升是瞬间完成的,就好像按了一下开关。

为什么集群健康状态是yellow而不是green?
我们有三个主分片,但是我们指定了每个主分片对应两个复制分片,当前却只有一个复制分片被分配,这就是集群状态无法达到green的原因,不过不用太担心这个:当我们杀掉Node 2,我们的程序依然可以在没有丢失数据的情况下继续运行,因为Node 3还有每个分片的拷贝。

如果我们重启Node 1,集群将能够重新分配丢失的复制分片,集群状况与上一节的 图5:增加number_of_replicas到2 类似。如果Node 1依旧有旧分片的拷贝,它将会尝试再利用它们,它只会从主分片上复制在故障期间有数据变更的那一部分。

以上学习Elasticsearch权威指南,提取码:jdbw

数据结构

什么是文档?

在Elasticsearch中,文档(document)这个术语有着特殊含义。它特指最顶层结构或者根对象(root object)序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中)。

文档元数据

一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:

描述文档数据的数据叫元数据

_index

索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。

事实上,我们的数据被存储和索引在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在索引(index)中。剩下的细节由Elasticsearch关心既可。

这个名字必须是全部小写,不能以下划线开头,不能包含逗号。

_type

在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一个类(class),这个类定义了属性或与对象关联的数据。user类的对象可能包含姓名、性别、年龄和Email地址。

在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为它们有着相同的结构。同理,在Elasticsearch中,我们使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的。

每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储在同一个索引下,但是类型的映射(mapping)会告诉Elasticsearch不同的文档如何被索引

_type的名字可以是大写或小写,不能包含下划线或逗号。我们将使用blog做为类型名。

6.x后,_type不再起作用

_id

id仅仅是一个字符串,它与_index和_type组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义_id,也可以让Elasticsearch帮你自动生成。

其他元数据

_source

_source:文档的原始Json数据,包括每个字段的内容

默认情况下,Elasticsearch 用 JSON 字符串来表示文档主体保存在 _source 字段中。像其他保存的字段一样,_source 字段也会在写入硬盘前压缩。

映射禁用 _source 字段:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_source": {
                "enabled":  false
            }
        }
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在搜索请求中你可以通过限定 _source字段来请求指定字段:

GET /_search
{
    "query":   { "match_all": {}},
    "_source": [ "title", "created" ]
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

这些字段会从 _source 中提取出来,而不是返回整个 _source 字段

_all

将所有字段内容整合起来,默认禁用(用于对所有字段内容的检索)

一个所有其他字段值的特殊字符串字段。query_string在没有指定字段时默认用 _all 字段查询。

_uid

_type 和 _id 连接成的 type#id

默认情况下,_uid 是被保存(可取回)和索引(可搜索)的。_type 字段被索引但是没有保存,_id_index 字段则既没有索引也没有储存,它们并不是真实存在的。

尽管如此,你仍然可以像真实字段一样查询 _id 字段。Elasticsearch 使用 _uid 字段来追溯 _id。虽然你可以修改这些字段的 indexstore 设置,但是基本上不需要这么做。

_id 字段有一个你可能用得到的设置:path 设置告诉 Elasticsearch 它需要从文档本身的哪个字段中生成 _id

从 doc_id 字段生成 _id:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "_id": {
                "path": "doc_id" <1>
            },
            "properties": {
                "doc_id": {
                    "type":   "string",
                    "index":  "not_analyzed"
                }
            }
        }
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

然后,当你索引一个文档时:_id 值由文档主体的 doc_id 字段生成。

POST /my_index/my_type
{
    "doc_id": "123"
}

 
 
  • 1
  • 2
  • 3
  • 4

_id 正确的生成了

{
    "_index":   "my_index",
    "_type":    "my_type",
    "_id":      "123", <1>
    "_version": 1,
    "created":  true
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

警告:虽然这样很方便,但是注意它对 bulk 请求(见【bulk 格式】)有个轻微的性能影响。处理请求的节点将不能仅靠解析元数据行来决定将请求分配给哪一个分片,而需要解析整个文档主体。

Elasticsearch搜索原理

正排索引和倒排索引

正排索引

记录文档Id到文档内容、单词的关联关系
DAIHAO:1,2

docidcontent
1DAIHAO哈哈哈哈哈
2哈哈哈DAIAHO
3哈哈哈哈哈

倒排索引

记录单词到文档id的关联关系,包含:
单词词典(Term DicTionary):记录所有文档的单词,一般比较大
倒排索引(Posting List):记录单词倒排列表的关联信息

例如:DAIHAO

一、单词词典(Term DicTionary)
DAIHAO

二、倒排索引(Posting List)

DocId(文档id)TF(单词频率)Position(位置)Offset(偏移量)
110<0,2>
310<0,2>

DocId:文档id,文档的原始信息
TF:单词频率,记录该词再文档中出现的次数,用于后续相关性算分
Position:位置,记录Field分词后,单词所在的位置,从0开始
Offset:偏移量,记录单词在文档中开始和结束位置,用于高亮显示等

三、内存结构
使用的B+Tree
B+Tree测试网址
每个文档字段都有自己的倒排索引

分词

分词是指将文本转换成一系列单词(term or token)的过程,也可以叫做文本分析,在es里面称为Analysis

Java是最好的语言->Java,是最好的,语言

 
 
  • 1

分词机制

Character Filter 对原始文本进行处理(洗) 例:去除html标签、特殊字符等
Tokenizer 将原始文本进行分词(拆) 例:培训机构–>培训,机构
Token Filters 分词后的关键字进行加工 例:转小写、删除语气词、近义词和同义词等

分词API

1、直接指定测试(指定分词器)

Request:

POST _analyze
{
  "analyzer": "standard",
  "text":"hello 1111"
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

Response:

{
  "tokens": [
    {
      "token": "hello",			#分词
      "start_offset": 0,		#开始偏移
      "end_offset": 5,			#结束偏移
      "type": "<ALPHANUM>",		#单词类型
      "position": 0				#位置
    },
    {
      "token": "world",
      "start_offset": 6,
      "end_offset": 11,
      "type": "<NUM>",
      "position": 1
    }
  ]
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2、针对索引的字段进行分词测试(利用该字段的分词器)

Request:

POST daihao/_analyze
{
  "field": "name",
  "text":"hello world"
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

Response:

{
  "tokens": [
    {
      "token": "hello",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "world",
      "start_offset": 6,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3、自定义分词器
Request:

POST _analyze
{
  "tokenizer": "standard",
  "filter": ["lowercase"],
  "text":"Hello WORLD"
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Response:

{
  "tokens": [
    {
      "token": "hello",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "world",
      "start_offset": 6,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
} 

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Elasticsearch自带的分词器

分词器(Analyzer)特点
Standard(es默认)支持多语言,按词切分并做小写处理
Simple按照非字母切分,小写处理
Whitespace按照空格来切分
Stop去除语气助词,如the、an、的、这等
Keyword不分词
Pattern正则分词,默认\w+,即非字词符号做分割符
Language常见语言的分词器(30+)

中文分词

分词器名称介绍特点地址
IK实现中英文单词切分自定义词库https://github.com/medcl/elasticsearch-analysis-ik
Jiebapython流行分词系统支持分词和词性标注 支持繁体、自定义、并行分词http://github.com/sing1ee/elasticsearch-jieba-plugin
Hanlp由一系列模型于算法组成的java工具包普及自然语言处理在生产环境中的应用https://github.com/hankcs/HanLP
THULAC清华大学中文词法分析工具包具有中文分词和词性标注功能https://github.com/microbun/elasticsearch-thulac-plugin

Character Filters

在进行Tokenizer之前对原始文本进行处理,如增加、删除或替换字符等

操作含义
HTMLStrip去除html标签和转换html实体
Mapping字符串替换操作
PatternReplace

注意:进行处理后,会影响后续tokenizer解析的position和offset

Request:

POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text":"<div><h1>B<sup>+</sup>Trees</h1></div>"
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Response:

{
  "tokens" : [
    {
      "token" : """

B+Trees

“”",
“start_offset” : 0,
“end_offset” : 38,
“type” : “word”,
“position” : 0
}
]
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Token Filter

对输出的单词(term)进行增加、删除、修改等操作

操作含义
Lowercase将所有term转换为小写
stop删除stop words
NGram和Edge NGram连词分割
Synonym添加近义词的term

Request:

POST _analyze
{
  "tokenizer": "standard",
  "text":"a Hello World",
  "filter": [
      "stop",
      "lowercase",
      {
        "type":"ngram",
        "min_gram":3,
        "max_gram":4
  <span class="token punctuation">}</span>
<span class="token punctuation">]</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Response:

{
  "tokens" : [
    {
      "token" : "hel",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "hell",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "ell",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "ello",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "llo",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "wor",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "worl",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "orl",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "orld",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "rld",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "<ALPHANUM>",
      "position" : 2
    }
  ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

自定义分词api

Request:

PUT my_analyzer
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my":{
          "tokenizer":"punctuation",
          "type":"custom",
          "char_filter":["emoticons"],
          "filter":["lowercase","english_stop"]
        }
      },
      "tokenizer": {
        "punctuation":{
          "type":"pattern",
          "pattern":"[.,!?]"
        }
      },
      "char_filter": {
        "emoticons":{
          "type":"mapping",
          "mappings":[
              ":)=>_happy_",
              ":(=>_sad_"
            ]
        }
      },
      "filter": {
        "english_stop":{
          "type":"stop",
          "stopwords":"_english_"
        }
      }
    }
  }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

测试:

POST my_analyzer/_analyze
{
  "analyzer": "my",
  "text":"l'm a :) person,and you?"
}

{
“tokens”: [
{
“token”: “l’m a happy person”,
“start_offset”: 0,
“end_offset”: 15,
“type”: “word”,
“position”: 0
},
{
“token”: “and you”,
“start_offset”: 16,
“end_offset”: 23,
“type”: “word”,
“position”: 1
}
]
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

分词使用场景

1、索引时分词:创建或更新文档时,会对相应得文档进行分词(指定字段分词)

PUT my_test
{
“mappings”:{
“doc”:{
“properties”:{
“title”:{
“type”:”text”,
“analyzer”:”ik_smart”
}
}
}
}
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2、查询时分词:查询时会对查询语句进行分词

POST my_test/_search
{
“query”:{
“match”:{
“message”:{
“query”:”hello”,
“analyzer”:”standard”
}
}
}
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
PUT my_test
{
“mappings”:{
“doc”:{
“properties”:{
“title”:{
“type”:”text”,
“analyzer”:”whitespace”,
“search_analyzer”:”standard”				#查询指定分词器
}
}
}
}
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

一般不需要特别指定查询时分词器,直接使用索引时分词器即可,否则会出现无法匹配得情况,如果不需要分词将字段type设置成keyword,可以节省空间

该篇主要学习对ES的概念理解,一时不明白没关系很正常,没事多看看。

自己总结的xmind:提取码:v8n8

                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-60ecaf1f42.css" rel="stylesheet">
                            </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值