《一文带你搞懂ElasticSearch:从零到上手搜索引擎》

一:简介

ruby 复制代码
https://www.elastic.co/cn/what-is/elasticsearch 

全文搜索属于最常见的需求,开源的 Elasticsearch 是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它 Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的 接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。 REST API:天然的跨平台。

  • 官方文档:
ruby 复制代码
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html 
  • 官方中文:
ruby 复制代码
https://www.elastic.co/guide/cn/elasticsearch/guide/current/foreword_id.html 
  • 社区中文:
arduino 复制代码
https://es.xiaoleilu.com/index.html 
ruby 复制代码
http://doc.codingdict.com/elasticsearch/0/

注:Elasticsearch的中文文档版本较老,如果想学好Elasticsearch,推荐看英文文档

二:基本概念

1.Index(索引)------ 相当于mysql中的数据库

动词,相当于 MySQL 中的 insert;

名词,相当于 MySQL 中的 Database

2.Type(类型)------ 相当于mysql中的数据表

在 Index(索引)中,可以定义一个或多个类型。 类似于 MySQL 中的 Table;每一种类型的数据放在一起;

3.Document(文档)------ 相当于mysql中的数据

保存在某个索引(Index)下,某种类型(Type)的一个数据(Document),文档是 JSON 格 式的,Document 就像是 MySQL 中的某个 Table 里面的内容;

4.倒排索引机制

倒排索引:从关键字搜索文档(关键字->文档)。优点:搜索耗时短。缺点:不易维护。

那些单词在那些表中有,会标记出来。便于快速查找

三:Docker安装ES

3.1 下载镜像文件

docker pull elasticsearch:7.4.2 存储和检索数据

docker pull kibana:7.4.2 可视化检索数据

bash 复制代码
free -m    #查看服务器还有多大内存

3.2 创建实例

1)ElasticSearch

1.创建文件

bash 复制代码
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data

2.给文件写入值

bash 复制代码
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml

3.进入到elasticsearch.yml文件

bash 复制代码
cat elasticsearch.yml

4.启动elasticsearch容器

bash 复制代码
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \ 
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \ 
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ 
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ 
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ 
-d elasticsearch:7.4.2
  • -p 9200:9200是发送http请求,也就是restAPI的时候暴露的端口
  • -p 9300:9300是elasticsearch在分布式集群状态下节点的通信端口
  • -e "discovery.type=single-node"以单节点运行
  • -e ES_JAVA_OPTS="-Xms64m -Xmx512m"为elasticsearch指定内存,初始64m,最大128m
  • -v文件挂载,将文件信息挂载到指定文件夹
  • -d后台启动下载好的es
    以后再外面装好插件重启即可;

    特别注意:
    -e ES_JAVA_OPTS="-Xms64m -Xmx256m" \ 测试环境下,设置 ES 的初始内存和最大内存,否则导 致过大启动不了 ES
bash 复制代码
docker start elasticsearch  #启动elasticsearch容器

5.elasticsearch启动失败解决方案,修改权限

bash 复制代码
docker logs elasticsearch  #查看启动日志


1.在elasticsearch目录下输入ll,查看权限

2.此时我们将所有的权限改为,既可以读,又能写,又可以执行

bash 复制代码
chmod -R 777 /mydata/elasticsearch/   ##保证权限


127.0.0.1:9200此时就修改过来了,该文件下都有读写权限了。

elasticsearch安装完成

2)Kibana

Kibana是一款可视化工具,相当于mysql的navicat和SQLyog。
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 -d kibana:7.4.2

  • 5601就是Kibana的访问端口,访问服务器的5601就可以访问到Kibana工具
  • http://192.168.56.10:9200 一定改为自己虚拟机的地址

    kibana启动成功

3.3 docker设置容器开机自启

1.elasticsearch

ini 复制代码
docker update elasticsearch --restart=always

2.kibana

ini 复制代码
docker update kibana --restart=always

四:初步检索

4.1 _cat

  • GET 127.0.0.1:9200/_cat/nodes:查看所有节点
  • GET 127.0.0.1:9200/_cat/health:查看 es 健康状况
  • GET 127.0.0.1:9200/_cat/master:查看主节点
  • GET 127.0.0.1:9200/_cat/indices:查看所有索引 show databases;

4.2 索引一个文档(保存)

ElasticSearch :保存一个数据,保存在哪个索引的哪个类型下,指定用哪个唯一标识。
MySQL :保存一个数据,保存在哪个数据表的哪个数据库里面。

1)插入数据

bash 复制代码
PUT customer/external/1
{ "name": "John Doe" }   ## 发送的数据
  • _代表元数据
  • _index表示在那个索引下
  • _type表示在那个类型下
  • _id表示唯一索引
  • _version表示数据的版本,会通过修改次数逐步叠加
  • result表示创建时的状态,created表示第一次创建,之后修改就是updated
  • shards表示分片

2)在 customer 索引下的 external 类型下保存 1 号数据,1代表的是唯一标识。

3)put和post区别

  • post新增修改二合一,不带id就是新增,带id就是更新。put必须携带id
  • POST 新增。如果不指定 id,会自动生成 id。指定 id 就会修改这个数据,并新增版本号 PUT 可以新增可以修改。
  • PUT 必须指定 id;由于 PUT 需要指定 id,我们一般都用来做修改 操作,不指定 id 会报错。

4.3 查询文档

GET 127.0.0.1:9200/customer/external/1

结果:

json 复制代码
{ 
        "_index": "customer", //在哪个索引 
        "_type": "external", //在哪个类型 
        "_id": "1", //记录 id 
        "_version": 2, //版本号 
        "_seq_no": 1, //并发控制字段,每次更新就会+1,用来做乐观锁 
        "_primary_term": 1, //同上,主分片重新分配,如重启,就会变化 
        "found": true, 
        "_source": { //真正的内容 
                  "name": "John Doe" 
        } 
}

当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

1)更新携带 ?if_seq_no=0&if_primary_term=1

  • _seq_no:用来控制并发,当修改以后,_seq_no就会变化
  • 想要修改,必须首先拿到最新数据,其次传递最新的_seq_no值

2)乐观锁和悲观锁
乐观锁:在关系数据库管理系统里,乐观并发控制(又名"乐观锁",Optimistic Concurrency Control,缩写"OCC")是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

悲观锁:悲观锁(Pessimistic Lock),指在应用程序中显示地为数据资源加锁。悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。悲观锁假定当前事务操纵数据资源时,肯定还有其他事务同时访问该数据资源,为了避免当前事务的操作受到干扰,先锁定资源。

4.4 更新文档

1)方式一

POST 127.0.0.1:9200/customer/external/1/_update

json 复制代码
{
    "doc":{
          "name":"John Doew"
    }
}

结论:

1.如果携带_update,参数中一定要添加"doc"。

2.加_update会对比源数据,与原来一样,则什么都不处理,文档 version 不增加。

2)方式二

POST 127.0.0.1:9200/customer/external/1/

json 复制代码
{
   "name":"John Doe2"
}

3)方式三

PUT 127.0.0.1:9200/customer/external/1/

json 复制代码
{
   "name":"John Doe2"
}

结论:

1.不会检查源数据

2.PUT 操作总会将数据重新保存并增加 version 版本

4)方式四------更新同时增加属性

POST 127.0.0.1:9200/customer/external/1/_update

json 复制代码
{
    "doc":{
          "name":"John Doew"
          "age":"J20"
    }
}

PUT和POST不带_update也可以

4.5 删除文档

DELETE 127.0.0.1:9200/customer/external/1

DELETE 127.0.0.1:9200/customer

  • 可以通过唯一标识删除数据
  • 也可以直接删除掉索引
  • elasticsearch没有提供直接删除文档的方法

4.6 bulk批量API

POST 127.0.0.1:9200/customer/external/_bulk

json 复制代码
{"index":{"_id":"1"}} 
{"name": "John Doe" } 
{"index":{"_id":"2"}} 
{"name": "Jane Doe" }

语法格式:

css 复制代码
{ action: { metadata }}\n 
{ request body }\n 
{ action: { metadata }}\n 
{ request body }\n

复杂实例:

POST /_bulk

json 复制代码
{ "delete": { "_index": "website", "_type": "blog", "_id": "123"}} 
{ "create": { "_index": "website", "_type": "blog", "_id": "123"}}
{ "title": "My first blog post"} 
{ "index": { "_index": "website", "_type": "blog"}} 
{ "title": "My second blog post"} 
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3}} 
{ "doc" : {"title" : "My updated blog post"}}

bulk API 以此按顺序执行所有的 action(动作)。如果一个单个的动作因任何原因而失败, 它将继续处理它后面剩余的动作。当 bulk API 返回时,它将提供每个动作的状态(与发送 的顺序相同),所以您可以检查是否一个指定的动作是不是失败了。

4.7 样本测试数据

导入测试数据 POST bank/account/_bulk

perl 复制代码
https://gitee.com/xlh_blog/common_content/blob/master/es%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE.json

五:进阶检索

5.1 SearchAPI

ES 支持两种基本方式检索 :

  1. 一个是通过使用 REST request URI 发送搜索参数(uri+检索参数)
  2. 另一个是通过使用 REST request body 来发送它们(uri+请求体)

1)一切检索从_search 开始

  • GET bank/_search 检索 bank 下所有信息,包括 type 和 docs
  • GET bank/_search?q=*&sort=account_number:asc 请求参数方式检索

响应结果解释:

bash 复制代码
took - Elasticsearch 执行搜索的时间(毫秒) 
time_out - 告诉我们搜索是否超时 
_shards - 告诉我们多少个分片被搜索了,以及统计了成功/失败的搜索分片 
hits - 搜索结果 
hits.total - 搜索结果 
hits.hits - 实际的搜索结果数组(默认为前 10 的文档) 
sort - 结果的排序 key(键)(没有则按 score 排序) 
score 和 max_score --相关性得分和最高得分(全文检索用)

2)uri+请求体进行检索

GET bank/_search

css 复制代码
{ 
    "query": { 
        "match_all": {} 
    },
    "sort": [ 
        { 
            "account_number": { 
            "order": "desc" 
            } 
        } 
      ] 
}

HTTP 客户端工具(POSTMAN),get 请求不能携带请求体,我们变为 post 也是一样的 我们 POST 一个 JSON 风格的查询请求体到 _search API。 需要了解,一旦搜索的结果被返回,Elasticsearch 就完成了这次请求,并且不会维护任何 服务端的资源或者结果的 cursor(游标)

5.2 Query DSL

1)基本语法格式

Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSL(domain-specific language 领域特 定语言)。这个被称为 Query DSL。该查询语言非常全面,并且刚开始的时候感觉有点复杂, 真正学好它的方法是从一些基础的示例开始的。

  • 一个查询语句 的典型结构
yaml 复制代码
{ 
    QUERY_NAME: { 
        ARGUMENT: VALUE, 
        ARGUMENT: VALUE,... 
    } 
}
  • 如果是针对某个字段,那么它的结构如下:
yaml 复制代码
{ 
    QUERY_NAME: { 
        FIELD_NAME: { 
            ARGUMENT: VALUE, 
            ARGUMENT: VALUE,... 
        } 
    } 
}

例:GET bank/_search

css 复制代码
{ 
    "query": { 
        "match_all": {} 
     },
    "from": 0, 
    "size": 5, 
    "sort": [ 
        { 
            "account_number": { 
            "order": "desc" 
        } 
    } 
  ] 
}
  • query 定义如何查询,
  • match_all 查询类型【代表查询所有的所有】,es 中可以在 query 中组合非常多的查 询类型完成复杂查询
  • 除了 query 参数之外,我们也可以传递其它的参数以改变查询结果。如 sort,size
  • from+size 限定,完成分页功能
  • sort 排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准

2)返回部分字段

GET bank/_search

css 复制代码
{ 
    "query": { 
        "match_all": {} 
     },
    "from": 0, 
    "size": 5, 
    "_source": ["age","balance"]
}

3)match【匹配查询】

1.基本类型(非字符串),精确匹配

GET bank/_search

json 复制代码
{ 
    "query": { 
        "match": { 
           "account_number": "20" 
         } 
     }
}
  • match 返回 account_number=20 的

2.字符串,全文检索

GET bank/_search

css 复制代码
{ 
    "query": { 
        "match": { 
           "address": "mill"
         } 
     }
}
  • 最终查询出 address 中包含 mill 单词的所有记录
  • match 当搜索字符串类型的时候,会进行全文检索,并且每条记录有相关性得分。

3.字符串,多个单词(分词+全文检索)

GET bank/_search

css 复制代码
{ 
    "query": { 
        "match": { 
           "address": "mill road"
         } 
     }
}
  • 最终查询出 address 中包含 mill 或者 road 或者 mill road 的所有记录,并给出相关性得分

4)match_phrase【短语匹配】

将需要匹配的值当成一个整体单词(不分词)进行检索

GET bank/_search

css 复制代码
{ 
    "query": { 
        "match_phrase": { 
           "address": "mill road"
         } 
     }
}
  • 查出 address 中包含 mill road 的所有记录,并给出相关性得分
  • match_phrase与address .keyword的区别就是,match_phrase只要包含就能查出来,属于模糊匹配。address .keyword属于精确匹配。

5)multi_match【多字段匹配】

GET bank/_search

json 复制代码
{ 
    "query": { 
        "multi_match": { 
           "query": "mill",
           "fields": ["state","address"]
         } 
  }
}
  • state 或者 address 包含 mill

6)bool【复合查询】

bool 用来做复合查询: 复合语句可以合并任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味 着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。

1.must:必须达到 must 列举的所有条件

GET bank/_search

css 复制代码
{
    "query": {
        "bool": {
            "must": [
                {"match": {"address": "mill"}},
                {"match": {"gender": "M"}}
            ]
        }
    }
}

2.should:应该达到 should 列举的条件,如果达到会增加相关文档的评分,并不会改变 查询的结果。如果 query 中只有 should 且只有一种匹配规则,那么 should 的条件就会 被作为默认匹配条件而去改变查询结果

GET bank/_search

css 复制代码
{
    "query": {
        "bool": {
            "must": [
                {"match": {"address": "mill"}},
                {"match": {"gender": "M"}}
            ],
            "should": [ 
                {"match": { "address": "lane" }} 
            ]
        }
    }
}

3.must_not 必须不是指定的情况

GET bank/_search

css 复制代码
{
    "query": {
        "bool": {
            "must": [
                {"match": {"address": "mill"}},
                {"match": {"gender": "M"}}
            ],
            "should": [ 
                {"match": { "address": "lane" }} 
            ],
            "must_not": [ 
                {"match": { "email": "baluba.com" }} 
            ]
        }
    }
}
  • address 包含 mill,并且 gender 是 M,如果 address 里面有 lane 最好不过,但是 email 必 须不包含 baluba.com

7)filter【结果过滤】

并不是所有的查询都需要产生分数,特别是那些仅用于 "filtering"(过滤)的文档。为了不 计算分数 Elasticsearch 会自动检查场景并且优化查询的执行。

GET bank/_search

css 复制代码
{
    "query": {
        "bool": {
            "must": [
                {"match": {"address": "mill"}},
                {"match": {"gender": "M"}}
            ],
            "filter": {
                "range": {
                    "balance": {
                        "gte": 10000,
                        "lte": 20000
                    }
                }
            }
        }
    }
}

8)term

和 match 一样。匹配某个属性的值。全文检索字段用 match,其他非 text 字段匹配用 term。

GET bank/_search

css 复制代码
{
    "query": {
        "bool": {
            "must": [
                {"term": {"age": {"value": "28"}}}, 
                {"match": {"address": "990 Mill Road"}}
            ]
        }
    }
}

9)aggregations(执行聚合)

聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数。在 Elasticsearch 中,您有执行搜索返回 hits(命中结果),并且同时返 回聚合结果,把一个响应中的所有 hits(命中结果)分隔开的能力。这是非常强大且有效的, 您可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用 一次简洁和简化的 API 来避免网络往返。
1.搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄,但不显示这些人的详情。

GET bank/_search

css 复制代码
{
    "query":{
        "match":{
            "address":"mill"
        }
    },
    "aggs":{
        "group_by_state":{
            "terms":{
                "field":"age"
            }
        },
        "avg_age":{
            "avg":{
                "field":"age"
            }
        }
    },
    "size":0
}

解释:

arduino 复制代码
1.size:0 不显示搜索数据 
2.aggs:执行聚合。聚合语法如下 
3."aggs": { 
      "aggs_name 这次聚合的名字,方便展示在结果集中": {
       "AGG_TYPE 聚合的类型(avg,term,terms)": {} } 
  },

2.按照年龄聚合,并且请求这些年龄段的这些人的平均薪资

GET bank/account/_search

json 复制代码
{
    "query":{
        "match_all":{

        }
    },
    "aggs":{
        "age_avg":{
            "terms":{
                "field":"age",
                "size":1000
            },
            "aggs":{
                "banlances_avg":{
                    "avg":{
                        "field":"balance"
                    }
                }
            }
        }
    },
    "size":1000
   }

3.查出所有年龄分布,并且这些年龄段中 M 的平均薪资和 F 的平均薪资以及这个年龄 段的总体平均薪资

GET bank/account/_search

json 复制代码
{
    "query":{
        "match_all":{

        }
    },
    "aggs":{
        "age_agg":{
            "terms":{
                "field":"age",
                "size":100
            },
            "aggs":{
                "gender_agg":{
                    "terms":{
                        "field":"gender.keyword",
                        "size":100
                    },
                    "aggs":{
                        "balance_avg":{
                            "avg":{
                                "field":"balance"
                            }
                        }
                    }
                },
                "balance_avg":{
                    "avg":{
                        "field":"balance"
                    }
                }
            }
        }
    },
    "size":1000
}

5.3 Mapping

1)字段类型



2)映射
Mapping 是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和 索引的。相当于mysql的设计表,查看字段类型

比如,使用 mapping 来定义:

  • 哪些字符串属性应该被看做全文本属性(full text fields)。
  • 哪些属性包含数字,日期或者地理位置。
  • 文档中的所有属性是否都能被索引(_all 配置)。
  • 日期的格式。
  • 自定义映射规则来执行动态添加属性。

查看 mapping 信息:

GET bank/_mapping
修改 mapping 信息:

ruby 复制代码
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html


3)新版本改变
Es7 及以上移除了 type 的概念。

关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用, 但 ES 中不是这样的。elasticsearch 是基于 Lucene 开发的搜索引擎,而 ES 中不同 type 下名称相同的 filed 最终在 Lucene 中的处理方式是一样的。

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

Elasticsearch 7.x

  • URL 中的 type 参数为可选。比如,索引一个文档不再要求提供文档类型。

Elasticsearch 8.x

  • 不再支持 URL 中的 type 参数。

解决:

  • 将索引从多类型迁移到单类型,每种类型文档一个独立索引
  • 将已存在的索引下的类型数据,全部迁移到指定位置即可。详见数据迁移

4)创建映射

PUT /my_index

json 复制代码
{
  "mappings": {
    "properties": {
      "age":{"type": "integer"},
      "email":{"type": "keyword"},
      "name":{"type":"text"}
    }
  }
}

5)添加新的映射

PUT /my_index/_mapping

json 复制代码
{
    "properties": {
       "employee-id":{
          "type":"keyword",
          "index":false
        }
    }
}
  • "index":false表示该字段不需要被索引,不会被检索到。此时不能用employee-id进行检索。
  • index默认都是true

6)更新映射

对于已经存在的映射字段,我们不能更新。更新必须创建新的索引进行数据迁移
7)数据迁移

先创建出 new_twitter 的正确映射。然后使用如下方式进行数据迁移

POST _reindex [固定写法]

json 复制代码
{
  "source": {
    "index": "bank",
    "type": "account"
  },
  "dest": {
    "index": "newbank"
  }
}

GET /newbank/_search

  • 不用type,老的数据迁移过来

六:分词

一个 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(自定义分词器)。

6.1 安装 ik 分词器

注意:不能用默认 elasticsearch-plugin install xxx.zip 进行自动安装。ik分词器要和es版本保持一致

es安装地址:

arduino 复制代码
https://github.com/medcl/elasticsearch-analysis-ik

1)进入到es的plugin中

bash 复制代码
cd /mydata/elasticsearch/plugin

2)安装wget

复制代码
yum install wget


3)将下载好的ik分词器拖入到plugins目录下

4)解压ik分词器

python 复制代码
unzip elasticsearch-analysis-ik-7.4.2.zip

6.2 测试分词器

6.2.1 使用默认

POST _analyze

json 复制代码
{ 
    "text": "我是中国人" 
}

6.2.2 使用分词器

POST _analyze

json 复制代码
{ 
    "analyzer": "ik_smart",
    "text": "我是中国人" 
}

6.2.3 另外一个分词器

POST _analyze

json 复制代码
{ 
    "analyzer": "ik_max_word",
    "text": "我是中国人" 
}

能够看出不同的分词器,分词有明显的区别,所以以后定义一个索引不能再使用默 认的 mapping 了,要手工建立 mapping, 因为要选择分词器。

6.3 在nginx里配置es

nginx的安装: https://editor.csdn.net/md/?articleId=123170992

6.3.1 在nginx的html文件夹下新建es文件夹

bash 复制代码
cd /mydata/nginx/html
mkdir es

6.3.2 在es当中新建fenci.text

bash 复制代码
cd /mydata/nginx/html/es
复制代码
vi fenci.html

6.4 自定义词库

6.4.1 进入到ik分词器里面

bash 复制代码
cd /mydata/elasticsearch/plugins/ik/config

6.4.2 修改IKAnalyzer.cfg.xml

复制代码
vi IKAnalyzer.cfg.xml 

将注释去掉,并且修改为自己配置的词库

xml 复制代码
<!--用户可以在这里配置远程扩展字典 --> 
<entry key="remote_ext_dict">http://192.168.128.130/fenci/myword.txt</entry>

七:Elasticsearch-Rest-Client

1)9300:TCP

spring-data-elasticsearch:transport-api.jar;

  • springboot 版本不同, transport-api.jar 不同,不能适配 es 版本
  • 7.x 已经不建议使用,8 以后就要废弃
  • 不建议9300端口操作es

2)9200:HTTP(推荐使用9200端口操作es)

  • JestClient:非官方,更新慢
  • RestTemplate:模拟发 HTTP 请求,ES 很多操作需要自己封装,麻烦
  • HttpClient:同上
  • Elasticsearch-Rest-Client:官方 RestClient,封装了 ES 操作,API 层次分明,上手简单

结论:

最终选择 Elasticsearch-Rest-Client(elasticsearch-rest-high-level-client)

ruby 复制代码
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html

7.1 新建微服务

7.2 导入es的rest-high-level-client的依赖

xml 复制代码
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.2</version>
        </dependency>

顺便将es的版本改为7.4.2

xml 复制代码
    <properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>

7.3 添加es的配置

1)在search包下新建config包,并且新建GulimailElasticSearchConfig配置文件

2)添加@Configuration注解

3)引入common包

xml 复制代码
        <dependency>
            <groupId>com.sysg.gulimail</groupId>
            <artifactId>gulimail-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

4)在application.properties添加配置

ini 复制代码
#应用名称
spring.application.name=gulimail-search
#注册发现中心地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

5)在主启动类添加@EnableDiscoveryClient注解,开启服务注册发现功能

6)编写config配置

java 复制代码
@Configuration
public class GulimailElasticSearchConfig {
    @Bean
    public RestHighLevelClient esRestClient(){
        
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("127.0.0.1",9200,"http")));
        return client;
    }
}

给容器中注入一个RestHighLevelClient

7)在test中测试es

less 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class GulimailSearchApplicationTests {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Test
    public void contextLoads() {
        System.out.println(restHighLevelClient);
    }
}

八:测试es的增删改查

8.1 配置ES安全头信息RequestOptions

作用:当所有请求访问es的时候,会带上请求头信息。此时通过RequestOptions对请求进行一些设置。给每个请求发送之前构建一些授权信息,带上token令牌

ini 复制代码
public static final RequestOptions COMMON_OPTIONS;
static {
    RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
   /*     builder.addHeader("Authorization", "Bearer " + TOKEN);
        builder.setHttpAsyncResponseConsumerFactory(
                new HttpAsyncResponseConsumerFactory
                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));*/
    COMMON_OPTIONS = builder.build();
}

8.2 测试存储数据到es

java 复制代码
   /**
     * 测试存储数据到es
     */
    @Test
    public void indexData() throws IOException {
        //users-需要保存的索引名称
        IndexRequest indexRequest = new IndexRequest("users");
        //设置保存数据的主键
        indexRequest.id("1");
        //添加需要保存的数据
        User user = new User();
        user.setAge(18);
        user.setUserName("sysg");
        user.setGender("男");
          //将user对象转化为json字符串
        String toJSONString = JSON.toJSONString(user);
        indexRequest.source(toJSONString, XContentType.JSON);
        //使用客户端执行保存操作
        IndexResponse index = client.index(indexRequest, GulimailElasticSearchConfig.COMMON_OPTIONS);
        //响应数据
        System.out.println(index);
    }
    @Data
    class User{
        private String userName;
        private String gender;
        private Integer age;
    }

1)首先去kibana中查看user索引

2)在测试类执行保存方法

此时就有数据了!!!
注:更新数据也可以,当主键相同时,就会替换之前的数据!!!!

8.3 ES完成复杂检索

ini 复制代码
/**
     * 测试检索数据
     */
    @Test
    public void searchData() throws IOException {
        //1.创建检索请求
        SearchRequest searchRequest = new SearchRequest();
        //指定检索的索引
        searchRequest.indices("bank");
        //指定检索条件,dsl。此方法需要传入searchSourceBuilder对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();s
        //1.1)构建检索条件
        //查询条件
        sourceBuilder.query(QueryBuilders.matchQuery("address","mill"));
        //聚合条件
        //1.2)按照年龄的值进行聚合
        TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
        sourceBuilder.aggregation(ageAgg);
        //1.3)计算平均薪资
        AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
        sourceBuilder.aggregation(balanceAvg);
        searchRequest.source(sourceBuilder);
        //2.执行检索
        SearchResponse searchResponse = client.search(searchRequest, GulimailElasticSearchConfig.COMMON_OPTIONS);
        //3.分析结果
        /*Map map = JSON.parseObject(searchResponse.toString(), Map.class);*/
        //3.1)获取到所有查询到的数据
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String string = hit.getSourceAsString();
            JSONObject object = JSON.parseObject(string);
            System.out.println(object);
        }
        //3.2)获取这次检索到的分析信息
        Aggregations aggregations = searchResponse.getAggregations();
        /*for (Aggregation aggregation : aggregations.asList()) {
            String name = aggregation.getName();
            System.out.println("当前聚合名字:"+name);
        }*/
        Terms ageAgg1 = aggregations.get("ageAgg");
        
    }
相关推荐
小牛马爱写博客5 分钟前
ELK 企业级日志分析系统部署与实践
elk·elasticsearch·kibana·logstash
间彧16 分钟前
什么是Region多副本容灾
后端
爱敲代码的北17 分钟前
WPF容器控件布局与应用学习笔记
后端
爱敲代码的北17 分钟前
XAML语法与静态资源应用
后端
清空mega19 分钟前
从零开始搭建 flask 博客实验(5)
后端·python·flask
爱敲代码的北23 分钟前
UniformGrid 均匀网格布局学习笔记
后端
一只叫煤球的猫1 小时前
从1996到2025——细说Java锁的30年进化史
java·后端·性能优化
喵个咪1 小时前
开箱即用的GO后台管理系统 Kratos Admin - 数据脱敏和隐私保护
后端·go·protobuf
我是天龙_绍1 小时前
Java Object equal重写
后端
虎子_layor1 小时前
实现异步最常用的方式@Async,快速上手
后端·spring