ElasticSearch的进阶使用

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/index.html

我们主要对照上面的官方文档:

我们测试需要的测试数据,可以使用官方为我们提供的test数据:

https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json

对照Mysql数据库,类比回忆一下 ES 的几个基础概念:

ES MYSQL
索引 Index 数据库 Database
类型 Type 表 Table
文档 Document 行数据 rowdata

一、ElasticSearch的2中查询方式

1、ES支持2中基本方式检索:

  • 通过使用 Rest Request URI 发送搜索参数(URI + 检索参数);

  • 通过 Rest Request Body 来发送它们(URI + 请求体);

首先批量保存数据:

POST /bank/account/_bulk      //向索引为bank类型为account批量插入多条document

1.jpg

查看当前索引情况:

GET _cat/indices
结果:
yellow open bank                         s826I5U5T6awpTb123L__w 1 1 1000 0 414.2kb 414.2kb  //1000条数据,共占用414.2kb空间
green  open .kibana_task_manager_1       NplcfbL4T2qAsrPOp6ktjA 1 0    2 0    32kb    32kb
green  open kibana_sample_data_ecommerce FtLjxnndRfe5Uq8vLvZLtQ 1 0 4675 0   4.6mb   4.6mb
green  open .apm-agent-configuration     wPQduhx9THydx8WWKYwYnQ 1 0    0 0    283b    283b
green  open .kibana_1                    NPZGhzh-SqyV0vTtJoiF9g 1 0   55 0 933.7kb 933.7kb

2、一切检索从 _search 开始:

URI + 检索参数方式检索:

GET bank/_search?q=*&sort=account_number:asc

URI + 请求体方式检索:QueryDSL方式:查询领域对象语言;——重点使用方式

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

两种方式的查询结果相同:

2.jpg

具体的对应关键字的定义:

  • took – how long it took Elasticsearch to run the query, in milliseconds

  • timed_out – whether or not the search request timed out

  • _shards – how many shards were searched and a breakdown of how many shards succeeded, failed, or were skipped.

  • max_score – the score of the most relevant document found

  • hits.total.value – how many matching documents were found

  • hits.sort – the document’s sort position (when not sorting by relevance score)

  • hits._score – the document’s relevance score (not applicable when using match_all)

3、分页查询下一页的10条数据:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ],
  "from": 10,
  "size": 10
}

4、QueryDSL领域对象语言查询的基本语法:

3.jpg


二、ES的常用查询方式和聚合

1、match_all:上面已经使用过了——查询所有;

2、match:

当match的查询字段为非String类型的数据时,为精确查询:

4.jpg

当match的查询字段为String类型的数据时,为模糊匹配——全文检索(得益于 ES的“倒排索引”技术)

全文检索的最终结果,默认会按照评分“_score”进行倒序——也是得益于“倒排索引”技术;——会对检索条件进行“分词匹配”

5.jpg

3、match_phrase【短语匹配】——match中对 字段.keyword 也能实现类似的短语匹配效果

所谓短语匹配:就是将查询的多个单词当做一个短语进行匹配,不要分割;

6.jpg

4、multi_match 【多字段匹配】

7.jpg

5、bool【复合查询】

复合查询:可以帮助我们合并多个查询条件:真正常见的使用场景,多个条件都必须同时满足:

  • must:必须满足

  • must_not:必须不满足

  • should:应该满足,满足最好,不满足也行

8.jpg

should的影响点主要在得分 _score 上:

9.jpg

6、filter【过滤】——也属于 bool 复合查询下的选项

filter过滤:看起来和must、must_not很像,但是区别在于:

  • filter:不贡献相关性 得分;

  • must、must_not:贡献相关性 得分;

10.jpg

7、term【精确查询】

和 match 一样,匹配某个属性的值,与 match 的用法区别:

  • match:String类型,需要全文检索的字段,使用match;

  • term:其它非String类型的字段,精确匹配使用term;

以上作为规范和约定,开发中必须遵从;

使用term去查询String(text)字段时,存在analysis数据分析的问题(进行了分词操作),使用term去检索完整的字段是非常困难的;

11.jpg

8、aggregations【执行聚合】

聚合:提供了从数据中分组和提取数据的能力。

最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数功能;

您有执行搜索返回hits(命中结果),并且同时返回聚合结果,把一个响应中的所有hits(命中结果)分隔开的能力;

这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API来避免网络往返;

示例:

  • 基础:搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情;

13.jpg

  • 复杂:按照年龄聚合,并且请求这些年龄段的这些人的平均薪资;

14.jpg

此功能用于做报表、曲线非常适合

  • 复杂:查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资,以及这个年龄段的总的平均薪资

(用来做对比报表)

15.jpg

ElasticSearch中的聚合分析方式有很多,上面只列举了几个常用的,详细使用文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-aggregations.html


三、ES中的映射——mapping

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping.html

1、Mapping映射是用来定义一个文档Document是将如何被处理的;即,属性字段是如何被存储、被索引的;

举例,我们可以使用映射定义:

  • 哪个String类型的字段应该被当做全文检索full text字段;

  • 哪个属性是包含数字、日期或地理位置坐标的;

  • 日期的格式化信息等;

类似于在关系型数据库中,我们要定义每一列的数据类型(Type);——在ES中就是Mapping Type映射类型;

但是必须要注意的是:Mapping Type在ES6.0之后的版本中,就已经被Deprecated了;将会在ES8中彻底被放弃;

16.jpg

2、为什么要移除Type类型的概念?

  • 关系型数据库中,两个数据表是独立的(数据表相当于ES中的类型Type),即使他们里面有相同名称的列,也不影响使用;

  • 但是ES不一样,ES是基于Lucene开发的搜索引擎,不同的type下名称相同的field最终在Lucene中的处理方式是一样的

    两个不同Type下的两个user_name字段,在ES同一个索引下其实是被认为是同一个field的,你必须在两个不同的type中定义相同的field映射。否则,不同的type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene的处理效率下降。

    去掉Type是为了提高ES处理数据的效率的;

ES7.x中:

URL中的type参数为可选。比如:索引一个文档,可以提供文档类型,也可以不提供;

ES8.x中:

不再支持URL中的tyoe参数;

解决:

将索引从多类型迁移到单类型,每种类型的文档创建一个独立索引!!!


四、Mapping的使用

1、映射的查看:

GET bank/_mapping

17.jpg

2、映射的创建:

虽然有了ES的自动类型猜测机制,但是有时候猜测的结果并不是我们所期望的,所以我们就可以手动自己创建映射Mapping:

支持的属性类型:

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping-types.html

18.jpg

创建了一个新的索引my_index,并且指定了映射;

3、向已存在的索引中添加映射:

19.jpg

4、映射的更新:

  对于已经存在的映射字段:我们是不能更新的,想更新的话,必须创建新的索引进行数据迁移;  

因为映射中可能已经有数据了,这时候更新了映射(相当于mysql数据表中字段的数据类型),那么肯定是会有问题的;

5、创建新索引+数据迁移:

需求: 之前的bank索引下的数据,都有一个类型_type为account:

20.jpg

而在新版本的ES中不建议使用_type类型,所以我们正好进行一次,数据迁移,同时将age的映射从“long”修改为“integer”等:

PUT /newbank
{
  "mappings": {
    "properties": {
      "account_number": {
        "type": "long"
      },
      "address": {
        "type": "text"
      },
      "age": {
        "type": "integer"
      },
      "balance": {
        "type": "long"
      },
      "city": {
        "type": "keyword"
      },
      "email": {
        "type": "keyword"
      },
      "employer": {
        "type": "keyword"
      },
      "firstname": {
        "type": "text"
      },
      "gender": {
        "type": "keyword"
      },
      "lastname": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "state": {
        "type": "keyword"
      }
    }
  }
}

执行成功:

21.jpg

执行数据迁移:(将老索引中的数据,迁移到新的索引中)

POST _reindex
{
  "source": {
    "index": "bank",
    "type": "account"
  },
  "dest": {
    "index": "newbank"
  }
}

22.jpg

之后我们再查看新的索引中的数据时候,就会发现,每一条文档的_type属性全部变为了默认的_doc

23.jpg


五、ES分词-安装ik分词

1、分词——是ElasticSearch中实现全文检索的核心

分词——将一段完整的话,拆分为一个一个的单词;

分词操作 在ES中是使用 tokenizer(分词器)来进行的,tokenizer接受一个字节流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流;

使用默认的分词器whitespace tokenizer可以将文本“Quick brown fox!”分割为 [Quick, brown, fox!] 数组;

该tokenizer(分词器)还负责记录各个 term 词条的顺序或 position 位置(用于 phrase短语和 word proximity 近邻词查询),以及term(词条)所代表的原始 word(单词)的start(起始)和 end(结束)的character offsets(字符偏移量)(用于高亮显示搜索的内容)。

ElasticSearch提供了很多内置的分词器,可以用来构建 custom analyzers(自定义分词器);

2、分词器的使用:

默认的分词器(Standard Analyzer

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/analysis-standard-analyzer.html

POST _analyze
{
  "analyzer": "standard",
  "text": "my name is jiguiquan, i'm 25 years old"
}

24.jpg

但是这种分词规则其实并不好用:比如,不支持中文分词(ES官方默认带的分词器都不能很好地支持中文分词)

这时候我们就需要安装一些额外的好用的、支持中文分词的分词器“ik分词器”;

3、ik分词器 安装

ik分词器的下载地址:

https://github.com/medcl/elasticsearch-analysis-ik/releases

注意下载的分词器版本必须严格和我们安装的ElasticSearch保持一致:7.4.2

这里我们跳转链接到使用Docker安装ElseticSearch的章节:

http://www.jiguiquan.com/archives/1103

4、ik分词器支持2中分词模式:

  • ik-smart:智能分词

26.jpg

  • ik-max-word:分得尽可能多的词组;

27.jpg

但是,显然,这些还是不能完成“吉桂权”这样的固定名字的词组识别,这时候我们就需要为ik创建自定义的词库,使其能够满足我们得需求,完成分词功能;


六、自定义ES-ik分词器的扩展词库

1、修改 /mydata/elasticsearch/plugins/ik/config/ 目录下的 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"></entry>

         <!–用户可以在这里配置自己的扩展停止词字典–>

        <entry key="ext_stopwords"></entry>

        <!–用户可以在这里配置远程扩展字典 –>

        <!– <entry key="remote_ext_dict">words_location</entry> –>

        <!–用户可以在这里配置远程扩展停止词字典–>

        <!– <entry key="remote_ext_stopwords">words_location</entry> –>

</properties>

这里,我们为了提供远程词库服务,有2种方式:

  • 写一个服务项目,专门用来处理获取服务的请求;

  • 使用nginx服务进行暴露(显然这种方式最为简单)

2、使用docker安装nginx容器的方法详见:

http://www.jiguiquan.com/archives/1084

3、在宿主机的 /mydata/nginx/html/ 目录下创建 /es/fenci/txt 文件:

吉桂权
乔碧萝
寒月

测试访问:  http://192.168.174.141/es/fenci.txt  

30.jpg

4、修改步骤 1 中,将标红配置修改为上面的“分词文件可访问地址”

31.jpg

5、重启 elasticsearch 容器:

docker restart elasticsearch

重新测试分词效果:

32.jpg

显然自定义扩展词库生效了!

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐