注意:索引就是数据库。类型就是数据库表。文档就是表字段。其中类型的概念并不重。下面的测试对于索引很明确。类型和文档有些分不清。
建议的理解方式:你可以理解为:ES中有两级。第一级是数据库和数据库表的结合,或者说只有数据库表。还有一级就是字段。
这里 Types 的概念已经被逐渐弱化, Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type , Elasticsearch 7.X 中 , Type 的概念已经被删除了。 7.x 默认不再支持自定义索引类型(默认类型为:_doc ) 这里我们使用的是linux的ES 一.索引 *** 作--数据库 *** 作 1.创建索引: (1)向 ES 服务器发 PUT 请求 : http://192.168.23.130:9200/shopping (2)请求后,服务器返回响应 :
(3)对于结果的分析:
# 注意:创建索引库的分片数默认 1 片,在 7.0.0 之前的 Elasticsearch 版本中,默认 5 片 如果重复添加索引,会返回错误信息,见下图: 2.查看所有索引: (1)向 ES 服务器发 GET 请求 :http://192.168.23.130:9200/_cat/indices?v 这里请求路径中的 _cat 表示查看的意思, indices 表示索引,所以整体含义就是查看当前 ES 服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉,服务器响应结果如下。{
"acknowledged"【响应结果】: true, # true *** 作成功
"shards_acknowledged" 【分片结果】 : true, # 分片 *** 作成功 "index" 【索引名称】 : "shopping" }
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
3.查看单个索引(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping
查看索引向 ES 服务器发送的请求路径和创建索引是一致的。但是 HTTP 方法不一致。这里 可以体会一下 RESTful 的意义。(2)请求后,服务器返回响应 :
(3)对于结果的分析:
{ "shopping" 【索引名】 : { "aliases" 【别名】 : {}, "mappings" 【映射】 : {}, "settings" 【设置】 : { "index" 【设置 - 索引】 : { "creation_date" 【设置 - 索引 - 创建时间】 : "1614265373911", "number_of_shards" 【设置 - 索引 - 主分片数量】 : "1", "number_of_replicas" 【设置 - 索引 - 副分片数量】 : "1", "uuid" 【设置 - 索引 - 唯一标识】 : "eI5wemRERTumxGCc1bAk2A", "version" 【设置 - 索引 - 版本】 : { "created": "7080099" }, "provided_name" 【设置 - 索引 - 名称】 : "shopping" } } } }4.删除索引
(1)向 ES 服务器发 DELETe 请求 :http://127.0.0.1:9200/shopping
(2)请求后,服务器返回响应 :
(3)对于结果的分析:重新访问索引时,服务器返回响应:索引不存在
二.文档 *** 作--数据库表和字段 *** 作的合体 注意:这里 Types 的概念已经被逐渐弱化, Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type , Elasticsearch 7.X 中 , Type 的概念已经被删除了。由于类型是在索引下的(类似于数据库下才有数据库表。索引下才可以有类型)。因此这里我们创建了一个shopping的索引。若你在上面已经删除,请再次执行一下下面这个命令:
见到下面的结果代表数据库创建完成:
1.创建文档 索引已经创建好了,接下来我们来创建文档,并添加数据。这里的文档可以类比为关系型数 据库中的表数据,添加的数据格式为 JSON 格式。(1)向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc
注意:没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机 生成一个
(2) 请求后,服务器返回响应 :(3)对于结果的分析:
{ "_index" 【索引】 : "shopping", "_type" 【 类型 - 文档 】 : "_doc", "_id" 【唯一标识】 : "Xhsa2ncBlvF_7lxyCE9G", # 可以类比为 MySQL 中的主键,随机生成 "_version" 【版本】 : 1, "result" 【结果】 : "created", # 这里的 create 表示创建成功 "_shards" 【分片】 : { "total" 【分片 - 总数】 : 2, "successful" 【分片 - 成功】 : 1, "failed" 【分片 - 失败】 : 0 }, "_seq_no": 0, "_primary_term": 1 }
注意:a.此处发送请求的方式必须为 POST,不能是 PUT,否则会发生错误
b.上面的数据创建后,由于没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机 生成一个。
===========================================================================================================================
如果想要自定义唯一性标识,需要在创建时指定: http://127.0.0.1:9200/shopping/phone/1 注意:若我们运行下面的URL,会出错。 原因是一个索引下只能有一个类型!! 所以我们还以上面的测试: http://127.0.0.1:9200/shopping/_doc/1此处需要注意:如果增加数据时明确数据主键,那么请求方式也可以为 PUT
2.查看文档 查看文档时,需要指明文档的唯一性标识,类似于 MySQL 中数据的主键查询。查看请保证有那个被查询的文档! (1) 向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/shopping /_doc/1(2)请求后,服务器返回响应 :
(3)对于结果的分析:
{ "_index" 【索引】 : "shopping", "_type" 【文档类型】 : "_doc", "_id": "1", "_version": 2, "_seq_no": 2, "_primary_term": 2, "found" 【查询结果】 : true, # true 表示查找到, false 表示未查找到 "_source" 【文档源信息】 : { "title": "华为手机 ", "category": "华为 ", "images": "http://www.gulixueyuan.com/hw.jpg", "price": 4999.00 } }3.修改文档
(1)向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc/1
注意:添加和修改文档方法是一样的!URL相同,请求体参数不同就自动修改了。
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
{ "_index": "shopping", "_type": "_doc", "_id": "1", "_version" 【版本】 : 2, "result" 【结果】 : "updated", # updated 表示数据被更新 "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 2, "_primary_term": 2 }4.修改字段 修改数据时,也可以只修改某一给条数据的局部信息
(1) 向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_update/1(这里的语法有些不懂)
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
5.删除文档 删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。 (1) 向 ES 服务器发 DELETE 请求 : http://127.0.0.1:9200/shopping /_doc/1(2)请求后,服务器返回响应 :
(3)对于结果的分析:
{ "_index": "shopping", "_type": "_doc", "_id": "1", "_version" 【版本】 : 4, # 对数据的 *** 作,都会更新版本 "result" 【结果】 : "deleted", # deleted 表示数据被标记为删除 "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 4, "_primary_term": 2 }(4) 删除后再查询当前文档信息 :
(5) 如果删除一个并不存在的文档
6.条件删除文档{
"_index": "shopping", "_type": "_doc", "_id": "1", "_version": 1, "result" 【结果】 : "not_found", # not_found 表示未查找到 "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 5, "_primary_term": 2 }
一般删除数据都是根据文档的唯一性标识进行删除,实际 *** 作时,也可以根据条件对多条数据进行删除
(0) 首先分别增加多条数据,以供测试使用。
向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc/1
先执行:
测试数据1:
测试数据2:
(1)向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_delete_by_query
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
三.映射 *** 作 有了索引库,等于有了数据库中的 database 。 接下来就需要建索引库(index) 中的映射了,类似于数据库 (database) 中的表结构 (table) 。 创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型 下有哪些字段,每个字段有哪些约束信息,这就叫做映射 (mapping) 。 1.创建映射{
"took" 【耗时】 : 175, "timed_out" 【是否超时】 : false, "total" 【总数】 : 2, "deleted" 【删除数量】 : 2, "batches": 1, "version_conflicts": 0, "noops": 0, "retries": { "bulk": 0, "search": 0 }, "throttled_millis": 0, "requests_per_second": -1.0, "throttled_until_millis": 0, "failures": [] }
(0)向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student
这是创建索引,为后面做准备,因为没有索引,不能创建映射。
(1)向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student/_mapping
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
映射数据说明: 1.字段名:任意填写,下面指定许多属性,例如:title 、 subtitle 、 images 、 price 2.type:类型, Elasticsearch 中支持的数据类型非常丰富,说几个关键的: (1)String 类型,又分两种: text:可分词 keyword :不可分词,数据会作为完整字段进行匹配 (2) Numerical :数值类型,分两类 基本数据类型: long 、 integer 、 short 、 byte 、 double 、 float 、 half_float 浮点数的高精度类型: scaled_float (3) Date:日期类型 (4)Array :数组类型 (5)Object :对象 3.index :是否索引,默认为 true ,也就是说你不进行任何配置,所有字段都会被索引。 true:字段会被索引,则可以用来进行搜索 false:字段不会被索引,不能用来搜索 4. store :是否将数据进行独立存储,默认为 false 原始的文本会存储在 _source 里面,默认情况下其他提取出来的字段都不是独立存储 的,是从 _source 里面提取出来的。当然你也可以独立的存储某个字段,只要设置 "store": true 即可,获取独立存储的字段要比从 _source 中解析快得多,但是也会占用 更多的空间,所以要根据实际业务需求来设置。 5. analyzer :分词器,这里的 ik_max_word 即使用 ik 分词器 , 后面会有专门的章节学习2.查看映射 (1) 向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/student /_mapping
(2)请求后,服务器返回响应 :
3.索引映射关联(1) 向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student1
http://127.0.0.1:9200/student2
(2)请求后,服务器返回响应 :
四.高级查询 Elasticsearch 提供了基于 JSON 提供完整的查询 DSL 来定义查询 。 0.定义数据,以供下方测试使用。 1.查询所有文档(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
解释:
{ "query": { "match_all": {} } } # "query" :这里的 query 代表一个查询对象,里面可以有不同的查询属性 # "match_all" :查询类型,例如: match_all( 代表查询所有 ) , match , term , range 等等 # { 查询条件 } :查询条件会根据类型的不同,写法也有差异
(2)请求后,服务器返回响应 :
(3)对于结果的分析:
{ "took 【查询花费时间,单位毫秒】 " : 1116, "timed_out 【是否超时】 " : false, "_shards 【分片信息】 " : { "total 【总数】 " : 1, "successful 【成功】 " : 1, "skipped 【忽略】 " : 0, "failed 【失败】 " : 0 }, "hits 【搜索命中结果】 " : { "total" 【搜索条件匹配的文档总数】 : { "value" 【总命中计数的值】 : 3, "relation" 【计数规则】 : "eq" # eq 表示计数准确, gte 表示计数不准确 }, "max_score 【匹配度分值】 " : 1.0, "hits 【命中结果集合】 " : [ 。。。 } ] } }2.匹配查询
match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
3.字段匹配查询multi_match 与 match 类似,不同的是它可以在多个字段中查询。
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
4.关键字精确查询
term 查询,精确的关键词匹配查询,不对查询条件进行分词。
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
5.多关键字精确查询 terms 查询和 term 查询一样,但它允许你指定多值进行匹配。 如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于 mysql 的 in (1) 向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
6.指定查询字段 默认情况下, Elasticsearch 在搜索的结果中,会把文档中保存在 _source 的所有字段都返回。 如果我们只想获取其中的部分字段,我们可以添加 _source 的过滤
(1) 向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
7.过滤字段我们也可以通过:
includes:来指定想要显示的字段 excludes:来指定不想要显示的字段 includes:(1) 向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
excludes:
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
8.组合查询 `bool` 把各种其它查询通过 `must` (必须 )、 `must_not` (必须不)、 `should` (应该)的方 式进行组合。 (1)向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/student/_search(2)请求后,服务器返回响应 :
这里我们出现了一个错误,暂时不清楚如何解决,先放在这里。
9.范围查询
(1) 向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
这里我们出现了一个错误,暂时不清楚如何解决,先放在这里。
10.模糊查询 返回包含与搜索字词相似的字词的文档。 编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:更改字符(box → fox)
删除字符(black → lack)
插入字符(sic → sick)
转置两个相邻字符(act → cat)
为了找到相似的术语, fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体 或扩展。然后查询返回每个扩展的完全匹配。 通过 fuzziness 修改编辑距离。一般使用默认值 AUTO ,根据术语的长度生成编辑距离。 a.(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
b.
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
11.单字段排序 sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。 desc 降序, asc 升序。 (1)向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/student/_search(2)请求后,服务器返回响应 :
12.多字段排序 假定我们想要结合使用 age 和 _score 进行查询,并且匹配的结果首先按照年龄排序,然后 按照相关性得分排序 。 (1) 向 ES 服务器发 GET 请求 : http://127.0.0.1:9200/student/_search (2) 请求后,服务器返回响应 :
13.高亮查询
在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。
如:在百度搜索"京东"
Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式 ( 高亮 ) 的设置。 在使用 match 查询的同时,加上一个 highlight 属性:pre_tags:前置标签
post_tags:后置标签
fields:需要高亮的字段
title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
14.分页查询 from :当前页的起始索引,默认从 0 开始。 from = (pageNum - 1) * size size :每页显示多少条
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
15.聚合查询 聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by ,当然还有很 多其他的聚合,例如取最大值、平均值等等。
a.对某个字段取最大值 max
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
b.对某个字段取最小值 min
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
c.对某个字段求和 sum
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
d.对某个字段取平均值 avg
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
e.对某个字段的值进行去重之后再取总数
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
f.State 聚合
stats 聚合,对某个字段一次性返回 count , max , min , avg 和 sum 五个指标(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
16.桶聚合查询 桶聚和相当于 sql 中的 group by 语句a.terms 聚合,分组统计
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
b.在 terms 分组下再进行聚合
(1)向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search
(2)请求后,服务器返回响应 :
五.JavaApi *** 作ES(没有集成springdata)
见springboot整合,模块名为springboot-es
六.Java的springboot,springdata集成elasticsearch也可见springboot整合,模块名为springboot-springdata-es
Elasticsearch 软件是由 Java 语言开发的,所以也可以通过 Java API 的方式对 Elasticsearch 服务进行访问1.创建SpringBoot工程
2.pom文件
4.0.0 org.springframework.boot spring-boot-starter-parent2.5.7 com.example springboot-es0.0.1-SNAPSHOT springboot-es Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starterorg.springframework.boot spring-boot-starter-testtest org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-data-elasticsearchorg.springframework.boot spring-boot-devtoolsruntime true junit junitorg.springframework spring-testorg.springframework.boot spring-boot-testorg.springframework.boot spring-boot-maven-pluginorg.projectlombok lombok
3.application.properties
# es 服务地址 elasticsearch.host=192.168.23.130 # es 服务端口 elasticsearch.port=9200 # 配置日志级别,开启 debug 日志 logging.level.com.example=debug
4.主启动类
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootEsApplication { public static void main(String[] args) { SpringApplication.run(SpringbootEsApplication.class, args); } }
5.数据实体类:beans.Product
package com.example.beans; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Product { private Long id;//商品唯一标识 private String title;//商品名称 private String category;//分类名称 private Double price;//商品价格 private String images;//图片地址 }
6.配置类:config.ESConfig
package com.example.config; import lombok.Data; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; @ConfigurationProperties(prefix = "elasticsearch") // 若application.properties中配置的前缀是elasticsearch会自动关联配置信息 @Configuration @Data public class ESConfig extends AbstractElasticsearchConfiguration { // 由于上面类注解的添加,使得这两个变量读取application.properties中的配置 private String host ; private Integer port ; //重写父类方法 @Override public RestHighLevelClient elasticsearchClient() { RestClientBuilder builder = RestClient.builder(new HttpHost(host, port)); RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder); return restHighLevelClient; } }
7.DAO 数据访问对象:dao.ProductDao
package com.example.dao; import com.example.beans.Product; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; @Repository public interface ProductDao extends ElasticsearchRepository{ }
8.实体类映射 *** 作:beans.Product
package com.example.beans; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Data @NoArgsConstructor @AllArgsConstructor @ToString // 做一个文档关联。索引名为product。主分片为3个,副本是1个 @document(indexName = "product", shards = 3, replicas = 1) public class Product { //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id" @Id private Long id;//商品唯一标识 @Field(type = FieldType.Text) private String title;//商品名称 @Field(type = FieldType.Keyword) // 设为关键词,即这个词不能被分开在查询的时候 private String category;//分类名称 @Field(type = FieldType.Double) private Double price;//商品价格 @Field(type = FieldType.Keyword, index = false) // index=false为不能被查询。不可以通过图片地址查询 private String images;//图片地址 }
=========================下面就进行测试了===========================
9.索引 *** 作:创建test.ESIndextTest
package com.example.test; import com.example.beans.Product; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.test.context.junit4.SpringRunner; // 索引 *** 作--第二个方法存疑 @RunWith(SpringRunner.class) // 测试没有这个似乎就不会打印日志 @SpringBootTest // 测试没有这个报错了??!! public class ESIndextTest { //注入 ElasticsearchRestTemplate @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; //创建索引并增加映射配置 @Test public void createIndex(){ //创建索引,系统初始化会自动创建索引 System.out.println("创建索引"); } // TODO @Test public void deleteIndex(){ //创建索引,系统初始化会自动创建索引 boolean flag = elasticsearchRestTemplate.indexOps(Product.class).delete();// 删除索引 System.out.println("删除索引 = " + flag); } }
ESIndexTest中有下图两个方法。
运行第一个方法后。在kibanna中,执行:GET /_cat/indices?v 可见下图:
运行第二个方法后。在kibanna中,执行:GET /_cat/indices?v 可见下图:
10.文档 *** 作:创建ESProductDaoTest
package com.example.test; import com.example.beans.Product; import com.example.dao.ProductDao; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.List; // 文档 *** 作 @RunWith(SpringRunner.class) @SpringBootTest public class ESProductDaoTest { @Autowired private ProductDao productDao; @Test public void save(){ Product product = new Product(); product.setId(2L); product.setTitle("华为手机"); product.setCategory("手机"); product.setPrice(2999.0); product.setImages("http://www.atguigu/hw.jpg"); productDao.save(product); } //修改 @Test public void update(){ Product product = new Product(); product.setId(1L); product.setTitle("小米 2 手机"); product.setCategory("手机"); product.setPrice(9999.0); product.setImages("http://www.atguigu/xm.jpg"); productDao.save(product); } //根据 id 查询 @Test public void findById(){ Product product = productDao.findById(1L).get(); System.out.println(product); } //查询所有 @Test public void findAll(){ Iterableproducts = productDao.findAll(); for (Product product : products) { System.out.println(product); } } //删除 @Test public void delete(){ Product product = new Product(); product.setId(1L); productDao.delete(product); } //批量新增 @Test public void saveAll(){ List productList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Product product = new Product(); product.setId(Long.valueOf(i)); product.setTitle("["+i+"]小米手机"); product.setCategory("手机"); product.setPrice(1999.0+i); product.setImages("http://www.atguigu/xm.jpg"); productList.add(product); } productDao.saveAll(productList); } //分页查询 @Test public void findByPageable(){ //设置排序(排序方式,正序还是倒序,排序的 id) Sort sort = Sort.by(Sort.Direction.DESC,"id"); int currentPage=0;//当前页,第一页从 0 开始,1 表示第二页 int pageSize = 5;//每页显示多少条 //设置查询分页 PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort); //分页查询 Page productPage = productDao.findAll(pageRequest); for (Product Product : productPage.getContent()) { System.out.println(Product); } } }
(1)
a.执行下方的方法:
b.在kibanna中,执行:GET /product/_search 可见下图:可见新增已实现
(2)
a.执行下方的方法:
b.在kibanna中,执行:GET /product/_search 可见下图:可见新增/修改已完成
(3)执行下方的方法:
控制台中可看到下图 :
(4)执行下方的方法:
控制台中可看到下图 :
(5)
a.执行下方的方法:
b.在kibanna中,执行:GET /product/_search 可见下图:可见删除完成
(6)
a.执行下方的方法:
b.在kibanna中,执行:GET /product/_search 可见下图:可见批量新增完成
(7)执行下方的方法:
查看控制台见下图:
11.文档搜索:由于其中一些方法过时,就不演示了。
七.Elasticsearch 环境和集群配置这里不考虑windows的集群配置,只考虑linux的集群配置。
1.Linux单机我们安装ES时是在linux的docker中的。对于直接安装在Linux中的建议有以下配置。安装在docker中的也建议。
若在docker中:docker exec -it 镜像名或容器id /bin/bash:进入docker里的那个容器里
对于在docker中运行的下面的配置还是不进行了吧!实在想要配置的话第(1)和(2)的a,b不要进行,因为没配置的情况下是可以运行的!至于(2)的c,d,e是增强项,对是否能运行影响不大,这几个影响的是性能。
(1)创建用户
因为安全问题, Elasticsearch 不允许 root 用户直接运行,所以要创建新用户,在 root 用 户中创建新用户。useradd sunlight # 新增 sunlight这个非root 用户 passwd 201315 # 为 sunlight 用户设置密码,密码为201315 userdel -r sunlight # 删除sunlight用户 chown -R 用户所有者:用户所在的组 文件或目录名 -R为递归整个目录的意思
(2)修改配置文件
a.修改/opt/module/es/config/elasticsearch.yml 文件
# 加入如下配置 cluster.name: elasticsearch node.name: node-1 network.host: 0.0.0.0 http.port: 9200 cluster.initial_master_nodes: ["node-1"]
b.修改/etc/security/limits.conf
# 在文件末尾中增加下面内容 # 每个进程可以打开的文件数的限制。*代表所有用户 * soft nofile 65536 * hard nofile 65536c.修改 /etc/security/limits.d/20-nproc.conf
# 在文件末尾中增加下面内容 # 每个进程可以打开的文件数的限制。*代表所有用户 * soft nofile 65536 * hard nofile 65536 # *** 作系统级别对每个用户创建的进程数的限制 * hard nproc 4096 # 注:* 带表 Linux 所有用户名称d.修改 /etc/sysctl.conf
# 在文件中增加下面内容 # 一个进程可以拥有的 VMA(虚拟内存区域)的数量,默认值为 65536 vm.max_map_count=655360e.重新加载
sysctl -p2.Linux集群
这里我们在sunlight2和sunlight3上实现两个服务器的集群。
(0)解压安装。方法如上。
(1)创建用户--两服务器的通用配置
因为安全问题, Elasticsearch 不允许 root 用户直接运行,所以要在每个节点中创建新用 户,在 root 用户中创建新用户useradd sunlight # 新增 sunlight这个非root 用户 passwd 201315 # 为 sunlight 用户设置密码,密码为201315 userdel -r sunlight # 删除sunlight用户 chown -R 用户所有者:用户组 文件或目录名 -R为递归整个目录的意思(此处用户所有者和用户组都要修改)
下图指令执行是必要的,否则运行会报错。
(2)修改配置文件
a.修改/opt/module/es/config/elasticsearch.yml 文件,分发文件
# 加入如下配置 #集群名称 cluster.name: cluster-es #节点名称,每个节点的名称不能重复 node.name: node-1 #ip 地址,每个节点的地址不能重复 network.host: linux1 #是不是有资格主节点 node.master: true node.data: true http.port: 9200 # head 插件需要这打开这两个配置 http.cors.allow-origin: "*" http.cors.enabled: true http.max_content_length: 200mb #es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master cluster.initial_master_nodes: ["node-1"] #es7.x 之后新增的配置,节点发现 discovery.seed_hosts: ["linux1:9300","linux2:9300","linux3:9300"] gateway.recover_after_nodes: 2 network.tcp.keep_alive: true network.tcp.no_delay: true transport.tcp.compress: true #集群内同时启动的数据任务个数,默认是 2 个 cluster.routing.allocation.cluster_concurrent_rebalance: 16 #添加或删除节点及负载均衡时并发恢复的线程个数,默认 4 个 cluster.routing.allocation.node_concurrent_recoveries: 16 #初始化数据恢复时,并发恢复线程的个数,默认 4 个 cluster.routing.allocation.node_initial_primaries_recoveries: 16
sunlight2:
# 加入如下配置 #集群名称 cluster.name: cluster-es #节点名称,每个节点的名称不能重复 node.name: sunlight2 #ip 地址,每个节点的地址不能重复 network.host: 192.168.23.150 #是不是有资格主节点。主节点;数据节点;端口号 node.master: true node.data: true http.port: 9200 # head 插件需要这打开这两个配置。跨域 http.cors.allow-origin: "*" http.cors.enabled: true http.max_content_length: 200mb #es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master。[]内的为服务器集群中你想要选举的服务器节点名称。 cluster.initial_master_nodes: ["sunlight2"] #es7.x 之后新增的配置,节点发现。服务器集群中各服务器的ip和端口号。这是服务器集群,在三台机器上,所以端口当然可以一样,因为服务器不同,IP地址不同。我们这里是两台服务器 #例子:discovery.seed_hosts: ["linux1:9300","linux2:9300","linux3:9300"] discovery.seed_hosts: ["192.168.23.150:9300","192.168.23.160:9300"] gateway.recover_after_nodes: 2 network.tcp.keep_alive: true network.tcp.no_delay: true transport.tcp.compress: true #下面的配置参数不太重要,加上即可。 #集群内同时启动的数据任务个数,默认是 2 个 cluster.routing.allocation.cluster_concurrent_rebalance: 16 #添加或删除节点及负载均衡时并发恢复的线程个数,默认 4 个 cluster.routing.allocation.node_concurrent_recoveries: 16 #初始化数据恢复时,并发恢复线程的个数,默认 4 个 cluster.routing.allocation.node_initial_primaries_recoveries: 16
sunlight3:
# 加入如下配置 #集群名称 cluster.name: cluster-es #节点名称,每个节点的名称不能重复 node.name: sunlight3 #ip 地址,每个节点的地址不能重复 network.host: 192.168.23.160 #是不是有资格主节点。主节点;数据节点;端口号 node.master: true node.data: true http.port: 9200 # head 插件需要这打开这两个配置。跨域 http.cors.allow-origin: "*" http.cors.enabled: true http.max_content_length: 200mb #es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举 master。[]内的为服务器集群中你想要选举的服务器节点名称。 cluster.initial_master_nodes: ["sunlight2"] #es7.x 之后新增的配置,节点发现。服务器集群中各服务器的ip和端口号。这是服务器集群,在三台机器上,所以端口当然可以一样,因为服务器不同,IP地址不同。我们这里是两台服务器 #例子:discovery.seed_hosts: ["linux1:9300","linux2:9300","linux3:9300"] discovery.seed_hosts: ["192.168.23.150:9300","192.168.23.160:9300"] gateway.recover_after_nodes: 2 network.tcp.keep_alive: true network.tcp.no_delay: true transport.tcp.compress: true #下面的配置参数不太重要,加上即可。 #集群内同时启动的数据任务个数,默认是 2 个 cluster.routing.allocation.cluster_concurrent_rebalance: 16 #添加或删除节点及负载均衡时并发恢复的线程个数,默认 4 个 cluster.routing.allocation.node_concurrent_recoveries: 16 #初始化数据恢复时,并发恢复线程的个数,默认 4 个 cluster.routing.allocation.node_initial_primaries_recoveries: 16
b.修改/etc/security/limits.conf ,分发文件--两服务器的通用配置
# 在文件末尾中增加下面内容 # 每个进程可以打开的文件数的限制。*为所有用户,此处写sunlight也行。 * soft nofile 65536 * hard nofile 65536
c.修改/etc/security/limits.d/20-nproc.conf,分发文件--两服务器的通用配置
# 在文件末尾中增加下面内容 # 每个进程可以打开的文件数的限制。*为所有用户,此处写sunlight也行。 * soft nofile 65536 * hard nofile 65536 # *** 作系统级别对每个用户创建的进程数的限制 * hard nproc 4096 # 注:* 带表 Linux 所有用户名称
d.修改/etc/sysctl.conf--两服务器的通用配置
# 在文件中增加下面内容 # 一个进程可以拥有的 VMA(虚拟内存区域)的数量,默认值为 65536 vm.max_map_count=655360
e.重新加载--两服务器的通用配置。在root用户下
sysctl -p
注意:对于上面的配置可以考虑把两台服务器的配置截图一下。
(3)启动软件
分别在不同节点上启动 ES 软件cd /opt/software/elasticsearch #前台 启动 bin/elasticsearch # 后台启动 bin/elasticsearch -d注意:切换到非root用户,否则报错,错误如下:
(4)测试集群:
192.168.23.150:9200 和 192.168.23.150:9200/_cat/nodes
192.168.23.160:9200 和 192.168.23.160:9200/_cat/nodes
下面为两个主机配置集群各自的实际配置文件:
sunlight2:a.修改/opt/module/es/config/elasticsearch.yml 文件,分发文件
vim /opt/software/elasticsearch-7.8.0/config/elasticsearch.yml
b.修改/etc/security/limits.conf ,分发文件--两服务器的通用配置
vim /etc/security/limits.conf
c.修改/etc/security/limits.d/20-nproc.conf,分发文件--两服务器的通用配置
vim /etc/security/limits.d/20-nproc.conf
d.修改/etc/sysctl.conf--两服务器的通用配置
vim /etc/sysctl.conf
sunlight3:a.修改/opt/module/es/config/elasticsearch.yml 文件,分发文件
vim /opt/software/elasticsearch-7.8.0/config/elasticsearch.yml
b.修改/etc/security/limits.conf ,分发文件--两服务器的通用配置
vim /etc/security/limits.conf
c.修改/etc/security/limits.d/20-nproc.conf,分发文件--两服务器的通用配置
vim /etc/security/limits.d/20-nproc.conf
d.修改/etc/sysctl.conf--两服务器的通用配置
vim /etc/sysctl.conf
最后两台机器一起执行下面的指令,在root用户下才能执行成功。
e.重新加载--两服务器的通用配置。
sysctl -p八.Elasticsearch 进阶 1.核心概念:
(1)分片是微服务,副本是集群
2.系统架构:P0,P1,P2是分片。R1,R2,R3是副本,即备份。
PX和RX不能放在同一台服务器上,否则没意义。
3.分布式集群:下面使用的还是集群:sunlight2和sunlight3。由于这两个在linux安装时安装的是不带kibanna的,因此我们在postman中测试
(1)单节点集群
我们在包含一个空节点的集群内创建名为 users 的索引,为了演示目的,我们将分配 3 个主分片和一份副本(每个主分片拥有一个副本分片)。a.PUT方式创建:
b.GET查询创建结果:
c.通过 elasticsearch-head 插件查看集群情况:把课件的压缩包解压并在谷歌浏览器导入即可。
d.对elasticsearch-head 插件 的解析:
i.本机--两台服务器的图示:3 个主分片和一份副本(每个主分片拥有一个副本分片):
星号是master节点。后面的数字加粗的为主分片,未加粗的为副本分片。
ii.三台服务器的图示:3 个主分片和一份副本(每个主分片拥有一个副本分片):
(2)水平扩容:主分片的数目在索引创建时就已经确定了下来。实际上,这个数目定义了这个索引能够 存储 的最大数据量。(实际大小取决于你的数据、硬件和使用场景。) 但是,读 *** 作—— 搜索和返回数据——可以同时被主分片 或 副本分片所处理,所以当你拥有越多的副本分片 时,也将拥有越高的吞吐量。
在运行中的集群上是可以动态调整副本分片数目的,我们可以按需伸缩集群。
即主分片数目不能二次修改,副本数量可以二次修改。
a.让我们把 副本数从默认的 1 增加到 2。
b.水平扩容后结果:
i.本机--两台服务器水平扩容后结果:
ii.三台服务器水平扩容后结果:
(3)应对故障:若某一服务器启动后宕机的解决方案
a.我们这里把其中一个服务关闭见下图:
i.我们这是两台服务器挂了其中一个!关闭的是sunlight3
ii.下图是三台服务器挂了一个
注意:其中一台服务器挂了以后并不影响各个功能的使用,但是会影响性能。
b.把关闭的服务再启动
i.我们这里重启的是sunlight3
ii.下图是三台服务器重启其中一个:
(4)路由计算
若有三个分片和三个副本。一个JSON数据怎么放是一个问题。若放入P0中,则取数据应该也能取到。若此时取数据去R0取,就出现存了数据存放了但取不到的情况。
当我们创建文档时,它如何决定这个文档应当被存储在分片 1 还是分片 2 中呢?我们用一个路由 计算算法:路由计算:hash(id) % 主分片数量 = [0 , 1, 2]
(5)分片控制
我们假设有一个集群由三个节点组成。 它包含一个叫 emps 的索引,有两个主分片, 每个主 分片有两个副本分片。相同分片的副本不会放在同一节点。(6)数据写流程
(6)数据读流程
(6)数据更新流程&&数据批量 *** 作流程
4.分片原理(1)倒排索引
分词算法: ik_max_word和ik_smart
词条:索引中最小存储和查询单元
词典:字典,词条的集合。B+,HashMap
倒排表:词条和ID的对照表
(2)文档搜索:见PDF
(3)动态更新索引:见PDF
如何在保留不变性的前提下实现倒排索引的更新? 答案是 : 用更多的索引。通过增加新的补充索引来反映新近的修改,而不是直接重写整 个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。(4)近实时分析
(5)文档分析
分析器实际上是将三个功能封装到了一个包里:字符过滤器,分词器,Token 过滤器。
a.字符过滤器:首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉 HTML,或者将 & 转化成 and 。
b.分词器 其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候, 可能会将文本拆分成词条。
c.Token 过滤器:最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化 Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)。
(6)测试分析器(测试分词器)--内置的
这里我们只使用了sunlight2
a.发送请求:
b.返回结果:
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "",
"position": 0
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "",
"position": 1
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "",
"position": 2
}
]
}
c.解释:
token 是实际存储到索引中的词条。 position 指明词条在原始文本中出现的位置。 start_offset 和 end_offset 指明字符在原始字符串中的位置。(7)指定分析器--IK分词器:
对于中文的未用IK分词器的:
a.发送请求:
b.返回结果:
{
"tokens": [
{
"token": "测",
"start_offset": 0,
"end_offset": 1,
"type": "
", "position": 0
},
{
"token": "试",
"start_offset": 1,
"end_offset": 2,
"type": "
", "position": 1
},
{
"token": "单",
"start_offset": 2,
"end_offset": 3,
"type": "
", "position": 2
},
{
"token": "词",
"start_offset": 3,
"end_offset": 4,
"type": "
", "position": 3
}
]
}
c.解释:ES 的默认分词器无法识别中文中测试、单词这样的词汇,而是简单的将每个字拆完分为一 个词。
这样的结果显然不符合我们的使用要求,所以我们需要下载 ES 对应版本的中文分词器。 我们这里采用 IK 中文分词器,下载地址为 : https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0 将解压后的后的文件夹放入 ES 根目录下的 plugins 目录下,重启 ES 即可使用。 我们这里只在sunlight2中引入了分词器,sunlight和sunlight3没有引入。但是切记既然配置了集群,最好每个服务器都开启。 当引入分词器后!重启ES 下面是引入IK分词器后的测试: a.发送请求:b.返回结果:
c.解释:{
"tokens": [
{
"token": "测试",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "单词",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
}
]
}
ik_max_word :会将文本做最细粒度的拆分 ik_smart :会将文本做最粗粒度的拆分再一个测试:用于测试上面二者的不同 (1)URL:
(2)结果:
(1)URL:{
"tokens": [
{
"token": "中国人",
"start_offset": 0,
"end_offset": 3,
"type": "CN_WORD",
"position": 0
},
{
"token": "中国",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "国人",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 2
}
]
}
(2)结果:
(8)IK分词器:扩展词汇 a.未扩展词汇:{
"tokens": [
{
"token": "中国人",
"start_offset": 0,
"end_offset": 3,
"type": "CN_WORD",
"position": 0
}
]
}
b.扩展词汇的 *** 作: 首先进入 ES 根目录中的 plugins 文件夹下的 ik 文件夹,进入 config 目录,创建 custom.dic 文件,写入弗雷尔卓德。同时打开 IKAnalyzer.cfg.xml 文件,将新建的 custom.dic 配置其中, 重启 ES 服务器。 下面是本机的实际 *** 作: i.cd 到对应目录 ii.vim custom.dic{
"tokens": [
{
"token": "弗",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "雷",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "尔",
"start_offset": 2,
"end_offset": 3,
"type": "CN_CHAR",
"position": 2
},
{
"token": "卓",
"start_offset": 3,
"end_offset": 4,
"type": "CN_CHAR",
"position": 3
},
{
"token": "德",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 4
}
]
}
iii.打开 IKAnalyzer.cfg.xml 文件,将新建的 custom.dic 配置其中
iv.重启ES
最后测试: a.URLb.结果:
a.URL{
"tokens": [
{
"token": "弗雷尔卓德",
"start_offset": 0,
"end_offset": 5,
"type": "CN_WORD",
"position": 0
}
]
}
b.结果:
(9)自定义分词器 你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单 元过滤器来创建自定义的分析器。 一个 分析器 就是在一个包里面组合了三种函数的一个包装器, 三种函数按照顺序被执行: 字符过滤器=>分词器=>词单元过滤器 演示一下,不需掌握: a.URL: Body内容:{
"tokens": [
{
"token": "皓皓月当空空",
"start_offset": 0,
"end_offset": 6,
"type": "CN_WORD",
"position": 0
},
{
"token": "皓皓",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "皓月当空",
"start_offset": 1,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "皓月",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 3
},
{
"token": "当空",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 4
},
{
"token": "空空",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 5
}
]
}
{ #使用时注释要删掉! "settings": { "analysis": { "char_filter": { "&_to_and": { "type": "mapping", "mappings": [ "&=> and "] #转义 } }, "filter": { "my_stopwords": { "type": "stop", "stopwords": [ "the", "a" ] #过滤掉这个 } }, "analyzer": { "my_analyzer": { #名称是my_analyzer "type": "custom", #类型是custom "char_filter": [ "html_strip", "&_to_and" ], "tokenizer": "standard", "filter": [ "lowercase", "my_stopwords" ] } } } } }
b.结果:
验证一下:
a.URL:
b.结果:
{
"tokens": [
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "",
"position": 1
},
{
"token": "and",
"start_offset": 10,
"end_offset": 11,
"type": "",
"position": 2
},
{
"token": "brown",
"start_offset": 12,
"end_offset": 17,
"type": "",
"position": 3
},
{
"token": "fox",
"start_offset": 18,
"end_offset": 21,
"type": "",
"position": 4
}
]
}
c.解释:
结合发送的URL的body体看结果,注意转义,过滤器,分词器的配置的影响。具体和详细的使用见其他,此处略作演示。
(10)文档处理--解决文档冲突的问题
a.悲观锁和乐观锁
b.外部系统版本控制:下图非来自本机,注意URL后面的
?version=当前版本或大于当前版本&version_type=external
(11)Kibanan的使用 见另一篇文章。装上kibanan后或许可以不用配置直接使用。 九.ES优化(1).....
(2)重要配置:
十.ES面试题见本地PDF
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)