网站建设有限公司,有播放量就有收益的自媒体平台,郑州网站建设公司排行,深圳市营销策划有限公司Elasticsearch Elasticsearch简介一、基本概念1、index(索引)2、Type(类型)3、Document(文档)4、倒排索引二、Docker 安装 EL1、拉取镜像2、创建实例三、初步探索1、_cat2、索引一个文档(保存)3、查询文档3、更新文档4、删除文档索引5、_bulk 批量 AP6、样本测试数据四、进…Elasticsearch Elasticsearch简介一、基本概念1、index(索引)2、Type(类型)3、Document(文档)4、倒排索引二、Docker 安装 EL1、拉取镜像2、创建实例三、初步探索1、_cat2、索引一个文档(保存)3、查询文档3、更新文档4、删除文档索引5、_bulk 批量 AP6、样本测试数据四、进阶检索1、 Search API2、Query DSL1基本语法格式2返回部分字段3match 匹配查询4match_phrase 短语匹配5多字段匹配6bool 复合查询7filter 结果过滤8term扩展keyword9 aggregations执行聚合3、Mapping 映射1新版本改变2创建索引并指定映射3添加新字段映射4更新字段映射5数据迁移4、分词1安装 ik 分词2自定义词库五、Elasticsearch-Rest-Client1、SpringBoot 整合 ES2、使用 SpringBoot 测试 ElasticSearch1增加索引2更新索引3删除索引4复杂查询六、拓展Docker 安装 Nginx视频来源: 【Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目】 简介
Elasticsearch 是什么 | Elastic
全文搜索属于最常见的需求开源的 Elasticsearch 是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它 Elastic 的底层是开源库 Lucene。但是你没法直接用 Lucene必须自己写代码去调用它的 接口。Elastic 是 Lucene 的封装提供了 REST API 的操作接口开箱即用。 REST
官方文档https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
官方中文https://www.elastic.co/guide/cn/elasticsearch/guide/current/foreword_id.html
社区中文 https://es.xiaoleilu.com/index.html http://doc.codingdict.com/elasticsearch/0/
一、基本概念
1、index(索引)
动词相当于 mysql 中的 insert
名词相当于 mysql 中的 database
2、Type(类型)
在 Index索引中可以定义一个或多个类型。
类似于 MySQL 中的 Table每一种类型的数据放在一起
3、Document(文档)
保存在某个索引Index下某种类型Type的一个数据Document文档是 JSON 格 式的Document 就像是 MySQL 中的某个 Table 里面的内容 4、倒排索引
Elasticsearch 检索速度快是因为使用了一种倒排索引的数据结构
保存的记录:
1-红海行动
2-探索红海行动
3-红海特别行动
4-红海记录篇
5-特工红海特别探索
Elasticsearch 会基于这些保存的数据额外维护一张表这些表会将保存的数据拆分成单词并记录命中的记录【如图所示】。
比如红海行动 会拆分成 红海、行动 , 探索红海行动 被拆分成 探索、红海、行动 当检索时
1、红海特工行动
会将检索的内容也拆分成单词红海、特工、行动 并在表中查询每个单词所命中的记录。 并按照相关性得分排序即每个文档跟查询的匹配程度
二、Docker 安装 EL
使用 su 切换到 root 用户登录
1、拉取镜像
docker pull elasticsearch:7.4.2 # 存储和检索数据 docker pull kibana:7.4.2 # 可视化检索数据2、创建实例
创建 EL
1、创建与elasticsearch挂载的目录
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
mkdir -p /mydata/elasticsearch/plugins2、修改权限避免由于权限问题elasticsearch报错
chmod -R 777 /mydata/elasticsearch/ 保证权限3、0.0.0.0 是所有主机均可访问 elasticsearch 接口
echo http.host: 0.0.0.0 /mydata/elasticsearch/config/elasticsearch.yml4、创建实例
9200:9200 提供给别的服务访问的接口
9300:9300 elasticsearch内部数据的访问
discovery.typesingle-node 单节点模式启动
ES_JAVA_OPTS“-Xms64m -Xmx512m” 设置 elasticsearch 启动所占用的内存。如果不设置会占用你所有的内存。导致OOM
docker run --name elasticsearch --restartalways -p 9200:9200 -p 9300:9300 \
-e discovery.typesingle-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 5、访问 9200 端口 创建 Kibana 实例
# 改成自己的 ip 地址
docker run --name kibana --restartalways -e ELASTICSEARCH_HOSTShttp://192.168.56.111:9200 -p 5601:5601 \
-d kibana:7.4.2将 Kibana设置成中文
# 进入容器
docker exec -it Kibana容器id bash
cd config/
vi kibana.yml
# 在配置文件中增加
i18n.locale: zh-CN
#重启实例即可
docker restart 容器id访问 IP地址:5601 三、初步探索
1、_cat
只需要发送对应的请求即可。 GET /_cat/nodes查看所有节点 _
GET /_cat/health查看 es 健康状况 _
GET /_cat/master查看主节点 _
GET /_cat/indices查看所有索引 show databases;
2、索引一个文档(保存)
PUT 方式
保存一个数据保存在哪个索引的哪个类型下指定用哪个唯一标识
# 在 customer 索引下的 external 类型下保存 1 号数据
PUT customer/external/1
{ name: John Doe
}1使用 PUT 请求带 id第一次发送请求时增加操作版本号为1.、第二次发送请求就是更新操作版本号为2 2PUT 方式必须携带ID否则就会报错 POST 方式
POST 请求也可以新增数据
# 在 customer 索引下的 external 类型下保存 1 号数据
POST customer/external/1
{ name: John Doe
}1POST 请求携带 id 当第一次发送POST请求是新增操作当第二次发送POST请求是更新操作并且版本号1 第二次携带 id 发送POST 请求 2POST方式可以不携带 id el 会自动随机生成一个不重复的 id也就是说如果不携带ID发送 POST 请求每一个都是新增 3、查询文档
请求
GET /customer/external/1返回结果
{ _index: customer, //在哪个索引_type: external, //在哪个类型_id: 1, //记录 id_version: 2, //版本号_seq_no: 1, //并发控制字段每次更新就会1用来做乐观锁_primary_term: 1, //同上主分片重新分配如重启就会变化found: true, _source: { //真正的内容name: John Doe}
}使用 _seq_no _primary_term 用来做乐观锁 ?if_seq_no0if_primary_term1
假设请求A发送修改请求:
PUT http://192.168.56.111:9200/customer/external/1?if_seq_no1if_primary_term1返回结果此时 _seq_no 被修改成了5
{_index: customer,_type: external,_id: 1,_version: 3,result: updated,_shards: {total: 2,successful: 1,failed: 0},_seq_no: 5,_primary_term: 1
}请求B 同样发送修改请求
请求B认为没有别的请求修改数据认为 _seq_no 仍然为 1
PUT http://192.168.56.111:9200/customer/external/1?if_seq_no1if__primary_term1返回结果报错reason就是: 需要 seqNo [1] 但是 seqNo [5]
{error: {root_cause: [{type: version_conflict_engine_exception,reason: [1]: version conflict, required seqNo [1], primary term [1]. current document has seqNo [5] and primary term [1],index_uuid: LDc8rC6ERbm0KN5XfSLmuQ,shard: 0,index: customer}],type: version_conflict_engine_exception,reason: [1]: version conflict, required seqNo [1], primary term [1]. current document has seqNo [5] and primary term [1],index_uuid: LDc8rC6ERbm0KN5XfSLmuQ,shard: 0,index: customer},status: 409
}3、更新文档
第一种方式 POST请求带_update
POST customer/external/1/_update
{ doc:{ name: John Doew}
}第二种方式: POST请求不带_update
POST customer/external/1
{ name: John Doe2
}第三种方式: PUT请求不带_update
PUT customer/external/1
{ name: John Doe
}三者区别:
POST请求带_update 会对比源文档数据如果一样就不进行任何操作。 包括 version、 _seq_no 也不会变化
不带 _update 每发送一次请求version、 _seq_no 都会变化
{_index: customer,_type: external,_id: 1,_version: 12,result: noop, // 结果是no operation没有进行任何操作_shards: {total: 0,successful: 0,failed: 0},_seq_no: 14,_primary_term: 1
}更新时增加属性:
POST customer/external/1/_update
{ doc: { name: Jane Doe, age: 20 }
}POST/PUT 不带 _update 也可以
POST customer/external/1
{ name: Jane Doe, age: 20
}PUT customer/external/1/
{ name: Jane Doe, age: 20
}4、删除文档索引
删除文档
DELETE customer/external/1返回结果
{_index: customer,_type: external,_id: 1,_version: 13,result: deleted,_shards: {total: 2,successful: 1,failed: 0},_seq_no: 15,_primary_term: 1
}删除索引
DELETE customer返回结果
{acknowledged: true
}5、_bulk 批量 AP
语法格式
action: 动作可以是 delete-删除index-新增update-修改
metadata 操作的数据比如
{“_id”:“1”} 操作 id 为1 的数据{“_index”: “user”, _type: “man”} 操作 索引为 user类型为 man 下的所有数据
request body 请求体
{ action: { metadata }}
{ request body }{ action: { metadata }}
{ request body }案例演示1
// _bulk 表示批量操作
POST /customer/external/_bulk
// index 新增id1
{index:{_id:1}}
// 请求体新增的内容
{name: John Doe}{index:{_id:2}}
{name: Jane Doe}返回结果
bulk API 以此按顺序执行所有的 action动作。如果一个单个的动作因任何原因而失败 它将继续处理它后面剩余的动作。当 bulk API 返回时它将提供每个动作的状态与发送 的顺序相同所以可以检查是否一个指定的动作是不是失败了。
{took : 120, //花费时间errors : false, // 是否有异常items : [{index : {_index : customer,_type : external,_id : 1,_version : 1,result : created,_shards : {total : 2,successful : 1,failed : 0},_seq_no : 0,_primary_term : 1,status : 201}},{index : {_index : customer,_type : external,_id : 2,_version : 1,result : created,_shards : {total : 2,successful : 1,failed : 0},_seq_no : 1,_primary_term : 1,status : 201}}]
}
案例演示2
第一个删除操作第二个新增操作第三个新增操作第四个修改操作
POST /_bulk
{ 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}}
{ doc : {title : My updated blog post} }6、样本测试数据
准备了一份顾客银行账户信息的虚构的 JSON 文档样本。每个文档都有下列的 schema 模式:
{ account_number: 0, balance: 16623, firstname: Bradshaw, lastname: Mckenzie, age: 29, gender: F, address: 244 Columbus Place, employer: Euron, email: bradshawmckenzieeuron.com, city: Hobucken, state: CO
}测试数据yangzhaoguang/ES-: ES测试数据 (github.com)
四、进阶检索
1、 Search API
ES 支持两种基本方式检索 : 一个是通过使用 REST request URI 发送搜索参数uri检索参数 另一个是通过使用 REST request body 来发送它们uri请求体
1uri 检索参数
// * 查询所有sort 排序
GET bank/_search?q*sortaccount_number:asc返回结果 took Elasticsearch 执行搜索的时间毫秒
time_out 告诉我们搜索是否超时
_shards 告诉我们多少个分片被搜索了以及统计了成功/失败的搜索分片
hits 搜索结果 hits.total - 搜索结果
hits.hits - 实际的搜索结果数组默认为前 10 的文档
sort 结果的排序 key键没有则按 score 排序
score 和 max_score 相关性得分和最高得分全文检索用
2uri 请求体
GET bank/_search
{query: {match_all: {}},sort: [{account_number: {order: desc},balance: {order: asc}}]
}在请求体中 封装查询的条件使用 Query DSL 语句
2、Query DSL
1基本语法格式
Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSLdomain-specific language 领域特 定语言。这个被称为 Query DSL。
结构语法
{QUERY_NAME: {ARGUMENT: VALUE, ARGUMENT: VALUE,... }
}query 定义如何查询 match_all 查询类型【代表查询所有的所有】
sort 排序多字段排序会在前序字段相等时后续字段内部排序否则以前序为准
“from”: 10, “size”: 5
从第十条记录开始查询五条数据。使用 from size 可以达到分页效果。
GET bank/_search
{query: {match_all: {}},sort: [{balance: {order: desc}}],from: 10,size: 5}2返回部分字段
_source 可指定返回的字段
GET bank/_search
{query: {match_all: {}},sort: [{balance: {order: desc}}],from: 10,size: 5,_source: [account_number,balance]
}3match 匹配查询
match 精确匹配
如果字段值是数字类型的就是精确匹配。匹配 account_number 2 的记录
# match 精确匹配
GET bank/_search
{query: {match: {account_number: 2}}
}match 模糊匹配
字段值是 字符串类型的就是模糊匹配。值得注意的是El 会将 Laurel Avenue 进行分词将所有包含 Laurel 或者 Avenue 或者 Laurel Avenue 都查出来。
# match 模糊匹配
GET bank/_search
{query: {match: {address: Laurel Avenue}}
}4match_phrase 短语匹配
不会将查询条件分词 查询 address 包含 Laurel Avenue 短语 的记录
# match_phrase 短语匹配不会分词
GET bank/_search
{query: {match_phrase: {address: Laurel Avenue}}
}5多字段匹配
query 查询条件
fields 查询字段
multi_match 也会分词查询address 和 city 里只要包含 Laurel、Belvoir、Laurel Belvoir 都能查询出来。
# multi_match 多字段匹配
GET bank/_search
{query: {multi_match: {query: Laurel Belvoir,fields: [address,city]}}
}6bool 复合查询
bool 用来做复合查询
复合语句 可以合并 任何 其它 查询语句包括复合语句了解这一点是很重要的。这就意味 着复合语句之间可以互相嵌套可以表达非常复杂的逻辑 。
must: 必须符合的条件
查询的数据必须符合gender为Fage为35 的记录
# bool 复合查询
GET bank/_search
{query: {bool: {must: [{match: {gender: F}},{match: {age: 35}}]}}
}must_not 必须不是指定的条件
查询的数据必须符合gender为Fage为35 并且 city 不为 Ironton 的记录
# bool 复合查询
GET bank/_search
{query: {bool: {must: [{match: {gender: F}},{match: {age: 35}}],must_not: [{match: {city: Ironton}}]}}
}should 查询的数据当中应当包含此条件如果包含此条件会提高相关性得分不包含也是可以的。
# bool 复合查询
GET bank/_search
{query: {bool: {must: [{match: {gender: F}},{match: {age: 35}}],must_not: [{match: {city: Ironton}}],should: [{match: {city: Darrtown}}]}}
}
7filter 结果过滤
过滤掉不符合条件的数据和 must一样但是 filter 不会计算相关性得分
查询出符合 10age30 的记录
# filter 不会计算相关性得分
GET bank/_search
{query: {bool: {filter: {range: {age: {gte: 10,lte: 30}}}}}
}8term
和 match 一样。匹配某个属性的值。全文检索字段用 match其他非 text 字段匹配用 term。
使用 match
GET bank/_search
{query: {match: {address: 880 Holmes Lane}}
}返回结果 一共有 16 条数据。 使用 term
GET bank/_search
{query: {term: {address: 880 Holmes Lane}}
}返回结果 0 条数据
{took : 0,timed_out : false,_shards : {total : 1,successful : 1,skipped : 0,failed : 0},hits : {total : {value : 0,relation : eq},max_score : null,hits : [ ]}
}官方是这样说的 避免使用 term 对文本字段查询 默认情况下Elasticsearch会作为分析的一部分更改文本字段的值。这会使查找文本字段值的精确匹配变得困难。 对于查询文本字段使用 match 使用 term 查找非文本字段
# 使用 term 查询非文本字段(精确查找)
GET bank/_search
{query: {term: {age: 28}}
}因此以后规定使用 term 查询非文本字段使用 match 查询文本字段。 扩展keyword
每一个字段都有 keyword 属性用来实现精确查找。
查询 address 为 880 Holmes Lane 的记录
# keywords
GET bank/_search
{query: {match: {address.keyword: 880 Holmes Lane}}
}他 与 match_phrase 的区别
match_phrase 指明查询条件是一个短语是一个包含关系。而 keyword 指明查询条件是一个 equals 关系。
# keywords
GET bank/_search
{query: {match: {address.keyword: 880 Holmes}}
}GET bank/_search
{query: {match_phrase: {address.keyword: 880 Holmes}}
} 使用 keyword : 会查询出 address 与 880 Holmes 相等的记录 使用 match_phrase 会查询出 address 包含 880 Holmes 短语的记录 9 aggregations执行聚合
聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数。在 Elasticsearch 中您有执行搜索返回 hits命中结果并且同时返 回聚合结果把一个响应中的所有 hits命中结果分隔开的能力。这是非常强大且有效的 您可以执行查询和多个聚合并且在一次使用中得到各自的任何一个的返回结果使用 一次简洁和简化的 API 来避免网络往返。
语法格式
文档Aggregations | Elasticsearch Guide 7.4] | Elastic
aggregations : {aggregation_name : {aggregation_type : {aggregation_body}[,meta : { [meta_data_body] } ]?[,aggregations : { [sub_aggregation] } ]?}[,aggregation_name_2 : { ... } ]*
}aggregation_name 为本次聚合设置一个名字自定义
aggregation_type 聚合的类型官方提供的聚合类型
aggregation_body 实现聚合
aggregation_name_2 可以设置多个聚合
案例一搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄但不显示这些人的详情。
GET bank/_search
{query: {match: {address: mill}},aggs: {aggAge: {terms: {field: age,size: 10}},ageAvg: {avg: {field: age}}},size: 0
}
第一个聚合 aggAge 统计 age 的分布情况并显示前10条记录
第二个聚合 ageAvg 统计 age 的平均年龄
size: 0 不显示查询记录的详情返回结果 案例二按照年龄聚合并且请求这些年龄段的这些人的平均薪资
在 aggAge 聚合里面嵌套 balanceAgg 聚合先查看年龄分步情况基于以上的结果计算平均薪资
# **案例二**按照年龄聚合并且请求这些年龄段的这些人的平均薪资
GET bank/_search
{query: {match_all: {}},aggs: {aggAge: {terms: {field: age,size: 10},aggs: {balanceAgg: {avg: {field: balance}}}}},size: 0
} 案例三查出所有年龄分布并且这些年龄段中 M 的平均薪资和 F 的平均薪资以及这个年龄 段的总体平均薪资
# 查出所有年龄分布并且这些年龄段中 M 的平均薪资和 F 的平均薪资以及这个年龄 段的总体平均薪资
GET bank/_search
{query: {match_all: {}},aggs: {ageAgg: {terms: {field: age,size: 100},aggs: {genderAgg: {terms: {field: gender.keyword},aggs: {balanceAgg: {avg: {field: balance}}}}}}},size: 0
}3、Mapping 映射
Mapping 是用来定义一个文档document以及它所包含的属性field是如何存储和 索引的。
比如
哪些字段是 text类型哪些字段是 boolean 类型
ES 中的映射类型有 1新版本改变
Es7 及以上移除了 type 的概念。
关系型数据库中两个数据表示是独立的即使他们里面有相同名称的列也不影响使用 但 ES 中不是这样的。elasticsearch 是基于 Lucene 开发的搜索引擎而 ES 中不同 type 下名称相同的 filed 最终在 Lucene 中的处理方式是一样的。
两个不同 type 下的两个 user_name在 ES 同一个索引下其实被认为是同一个 filed 你必须在两个不同的 type 中定义相同的 filed 映射。否则不同 type 中的相同字段 名称就会在处理中出现冲突的情况导致 Lucene 处理效率下降。去掉 type 就是为了提高 ES 处理数据的效率。
2创建索引并指定映射
# 创建索引并指定映射
PUT /my_index
{mappings: {properties: {age: {type: integer},address: {type: text}}}
}3添加新字段映射
“index”: false 表示不会被索引到
# 增加新的映射关系
PUT /my_index/_mapping
{properties: {email: {type: keyword,index: false}}
}4更新字段映射
对于已经存在的映射是无法更新的。否则就会报错
{error: {root_cause: [{type: resource_already_exists_exception,reason: index [my_index/c7ZZpqz9RzGdKJS_FTE3qA] already exists,index_uuid: c7ZZpqz9RzGdKJS_FTE3qA,index: my_index}],type: resource_already_exists_exception,reason: index [my_index/c7ZZpqz9RzGdKJS_FTE3qA] already exists,index_uuid: c7ZZpqz9RzGdKJS_FTE3qA,index: my_index},status: 400
}想要更新映射唯一的方法就是创建新的映射关系然后将数据迁移过去。 除了支持的映射参数外您不能更改现有字段的映射或字段类型。更改现有字段可能会使已编入索引的数据失效。如果需要更改字段的映射请使用正确的映射创建一个新索引然后将数据重新索引到该索引中。 5数据迁移
基本语法
POST _reindex
{
source: { index: twitter},
dest: {index: new_twitter}
}如果是 ES6之前指定了 Type
POST _reindex
{
source: { index: twitter,type : type},
dest: {index: new_twitter}
}1、创建新的映射
# 创建新的映射
PUT /new_bank
{mappings: {properties: {address: {type: text},age: {type: integer},balance: {type: long},city: {type: keyword},email: {type: keyword},employer: {type: keyword},firstname: {type: keyword},gender: {type: keyword},lastname: {type: keyword},state: {type: keyword}}}
}2、迁移数据
# 数据迁移
POST _reindex
{source: {index: bank,type: account},dest: {index: new_bank}
}4、分词
一个 tokenizer分词器接收一个字符流将之分割为独立的 tokens词元通常是独立 的单词然后输出 tokens 流。
例如whitespace tokenizer 遇到空白字符时分割文本。它会将文本 “Quick brown fox!” 分割 为 [Quick, brown,fox!]
ES 提供的分词器都是只支持英文分词如果是中文就有些不尽人意了。
# 使用分词器
POST _analyze
{analyzer: standard,text: 我爱中国
}分词结果按照我们中国人的逻辑 正常是 [我 , 爱 ,中国] 1安装 ik 分词
查看对应版本安装: Releases · medcl/elasticsearch-analysis-ik (github.com)
由于我们在启动 ES 时将 plugins 与我们主机目录进行了挂载只需要通过 XFTP 将压缩包 传入 主机的 /mydata/elasticsearch/plugins 目录进行解压即可。 使用命令解压缩
unzip 压缩包名称我们可以进入到 ES 容器内部查看插件是否安装
docker exec -it 容器id /bin/bash
# 切换到 bin 目录下
# 查看安装的 插件
elasticsearch-plugin list安装好后重启 ES 容器
docker restart 容器id使用 ik 分词器
# 使用 ik 分词器
POST _analyze
{analyzer: ik_smart,text: 我爱中国
}POST _analyze
{analyzer: ik_max_word,text: 我爱中国
}2自定义词库
虽然使用了 ik 分词器但是一些词语的分词还是达不到我们想要的效果这是因为 ES 中的默认词库中没有我们想要的单词因此我们需要自定义一个词库每次分词都先在自定义词库中查询。
比如期待的分词效果就是 [尚硅谷, 电商, 项目]
POST _analyze
{analyzer: ik_smart,text: 尚硅谷电商项目
}
但是分词结果 我们可以利用 Nginx 做转发将分词的查询请求发送给 Nginx
1、首先在 /mydata/nginx/html/es 目录下创建 fenci.txt 用来做我们的词库我写的词语 2、Nginx 默认是从 html 目录下访问 index.html 页面我们就可以指定词库的路径 192.168.56.111/es/fenci.txt,从页面上也是可以访问到的 3、修改 /mydata/elasticsearch/plugins/ik/config 下的 IKAnalyzer.cfg.xml 文件配置我们自定义的词库访问地址 4、重启 ES 容器
5、测试效果
POST _analyze
{analyzer: ik_smart,text: 尚硅谷电商项目
}
分析达到我们想要的效果了 五、Elasticsearch-Rest-Client
1、9300TCP
spring-data-elasticsearch:transport-api.jarspringboot 版本不同 transport-api.jar 不同不能适配 es 版本7.x 已经不建议使用8 以后就要废弃
2、9200HTTP
JestClient非官方更新慢RestTemplate模拟发 HTTP 请求ES 很多操作需要自己封装麻烦HttpClient同上
3、Elasticsearch-Rest-Client官方 RestClient封装了 ES 操作API 层次分明上手简单
最终选择 Elasticsearch-Rest-Clientelasticsearch-rest-high-level-client https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
1、SpringBoot 整合 ES
1导入依赖
要在 properties 指明 elasticSearch的全局版本号。因为在 SpringBoot 中的默认版本号 是 6.4.3 propertiesjava.version8/java.versionelasticsearch.version7.4.2/elasticsearch.version/properties!--elasticSearch--dependencygroupIdorg.elasticsearch.client/groupIdartifactIdelasticsearch-rest-high-level-client/artifactIdversion${elasticsearch.version}/version/dependency2编写配置
Configuration
public class GulimallElasticSearchConfig {Beanpublic RestHighLevelClient esClient() {RestHighLevelClient client new RestHighLevelClient(RestClient.builder(new HttpHost(192.168.56.111, 9200, http)));return client;}
}2、使用 SpringBoot 测试 ElasticSearch
ElasticSearch 的所有 API 使用方法都能在 官方文档中找到Update API | Java REST Client 7.4] | Elastic
1增加索引
1创建共享的请求部分 放在 配置类中 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();}2测试新增索引 Testpublic void addIndex() throws IOException {// 参数为索引名IndexRequest request new IndexRequest(users);// 设置idrequest.id(1);// 第一种封装数据方式// request.source(name,张三,age,17,address,河北);// 第二种封装数据方式User user new User();user.setName(张三);user.setAge(22);user.setAddress(二仙桥);// 将对象转换为 jsonString toJSONString JSON.toJSONString(user);request.source(toJSONString, XContentType.JSON);// 发送请求新增索引IndexResponse response restHighLevelClient.index(request, GulimallElasticSearchConfig.COMMON_OPTIONS);System.out.println(response);}Dataclass User {private String name;private Integer age;private String address;}3查看结果 2更新索引 /** 更新索引* */Testpublic void test() throws IOException {// indexidUpdateRequest updateRequest new UpdateRequest(users,1);User user new User();user.setName(李四);String jsonString JSON.toJSONString(user);updateRequest.doc(jsonString,XContentType.JSON);UpdateResponse updateResponse restHighLevelClient.update(updateRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);System.out.println(updateResponse);}3删除索引 Testpublic void delIndex() throws IOException {DeleteRequest deleteRequest new DeleteRequest(users,1);DeleteResponse deleteResponse restHighLevelClient.delete(deleteRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);System.out.println(deleteRequest);}4复杂查询
1、构建普通的查询条件
// 创建查询请求
SearchRequest searchRequest new SearchRequest();
// 构建DSL检索条件
SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder); 2、聚合条件
“构建聚合”提供了所有可用聚合及其对应的AggregationBuilder对象和AggregationBuilders帮助方法的列表。
SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();TermsAggregationBuilder aggregation AggregationBuilders.terms(by_company).field(company.keyword);
aggregation.subAggregation(AggregationBuilders.avg(average_age).field(age));searchSourceBuilder.aggregation(aggregation);3、返回信息
通过执行搜索返回的SearchResponse提供了有关搜索执行本身的详细信息以及对返回文档的访问。首先有关于请求执行本身的有用信息如HTTP状态代码、执行时间或请求是否提前终止或超时
RestStatus status searchResponse.status();
TimeValue took searchResponse.getTook();
Boolean terminatedEarly searchResponse.isTerminatedEarly();
boolean timedOut searchResponse.isTimedOut();获取分片信息
int totalShards searchResponse.getTotalShards();
int successfulShards searchResponse.getSuccessfulShards();
int failedShards searchResponse.getFailedShards();
for (ShardSearchFailure failure : searchResponse.getShardFailures()) {// failures should be handled here
}对应图中: 要访问返回的文档我们需要首先获取响应中包含的SearchHits
SearchHits hits searchResponse.getHits();SearchHits提供所有点击的全局信息如点击总数或最高得分
TotalHits totalHits hits.getTotalHits();
// the total number of hits, must be interpreted in the context of totalHits.relation
long numHits totalHits.value;
TotalHits.Relation relation totalHits.relation;
float maxScore hits.getMaxScore();对应图中 获取查询出来的文档记录
SearchHit[] searchHits hits.getHits();
for (SearchHit hit : searchHits) {// do something with the SearchHit
}返回的聚合信息
Aggregations aggregations searchResponse.getAggregations();
// 通过聚合名获取聚合信息
Terms byCompanyAggregation aggregations.get(ageAgg);
// 获取key
Bucket elasticBucket byCompanyAggregation.getBucketByKey(key); Avg averageAge elasticBucket.getAggregations().get(BalanceAgg);
// 获取 value
double avg averageAge.getValue();案例一 在 bank 索引中按照年龄聚合并且请求这些年龄段的这些人的平均薪资 Testpublic void search() throws IOException {// 案例: 按照年龄聚合并且请求这些年龄段的这些人的平均薪资SearchRequest searchRequest new SearchRequest(bank);// 1、sourceBuilder 构建DSL检索条件SearchSourceBuilder sourceBuilder new SearchSourceBuilder();// query中的match// sourceBuilder.query(QueryBuilders.matchQuery(address,mill));// query 中的 match_phrase// sourceBuilder.query(QueryBuilders.matchPhraseQuery(address,Holmes Lane ));// 2、sourceBuilder 同样可以构建分页信息// sourceBuilder.from(10);// sourceBuilder.size(20);// query中的match_allsourceBuilder.query(QueryBuilders.matchAllQuery());// 3、sourceBuilder封装聚合条件// subAggregation 可嵌套聚合TermsAggregationBuilder aggregation AggregationBuilders.terms(ageAgg).field(age).size(10).subAggregation(AggregationBuilders.avg(BalanceAgg).field(balance));sourceBuilder.aggregation(aggregation);searchRequest.source(sourceBuilder);System.out.println(查询条件: sourceBuilder);// 执行检索SearchResponse response restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);System.out.println(响应结果: response);// 4、分析结果SearchHits hits response.getHits();SearchHit[] searchHits hits.getHits();for (SearchHit searchHit : searchHits) {// 查询结果String sourceAsString searchHit.getSourceAsString();// 通过JSON转换工具将json——》实体类AccountBean accountBean JSON.parseObject(sourceAsString, AccountBean.class);System.out.println(Account对象: accountBean);}// 5、查看聚合信息Aggregations aggregations response.getAggregations();// 获取指定的聚合Terms ageAgg aggregations.get(ageAgg);for (Terms.Bucket bucket : ageAgg.getBuckets()) {System.out.println(年龄: bucket.getKeyAsString());System.out.println(人数: bucket.getDocCount());// 获取嵌套的聚合Avg balanceAgg bucket.getAggregations().get(BalanceAgg);System.out.println(平均薪资: balanceAgg.getValue());}}案例二 搜索 bank 索引中包含 mill 的所有人的年龄分布以及平均年龄但不显示这些人的详情。 /** 案例* */Testpublic void caseTwo() throws IOException {// 搜索 bank 中包含 mill 的所有人的年龄分布以及平均年龄但不显示这些人的详情。SearchRequest searchRequest new SearchRequest(bank);// 构建DSL 查询条件SearchSourceBuilder sourceBuilder new SearchSourceBuilder();sourceBuilder.query(QueryBuilders.matchQuery(address,mill));sourceBuilder.size(0);// 构建聚合TermsAggregationBuilder aggregation1 AggregationBuilders.terms(ageAgg).field(age);AvgAggregationBuilder aggregation2 AggregationBuilders.avg(avgAgg).field(age);sourceBuilder.aggregation(aggregation1);sourceBuilder.aggregation(aggregation2);searchRequest.source(sourceBuilder);System.out.println(条件: sourceBuilder);// 发送请求SearchResponse searchResponse restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);// 获取聚合结果Terms ageAgg searchResponse.getAggregations().get(ageAgg);for (Terms.Bucket bucket : ageAgg.getBuckets()) {System.out.println(年龄 bucket.getKey());System.out.println(人数 bucket.getDocCount());}Avg avgAgg searchResponse.getAggregations().get(avgAgg);System.out.println(平均年龄: avgAgg.getValue());}六、拓展Docker 安装 Nginx
1、先随便启动一个 Nginx 复制出里面的配置文件
docker run -p 80:80 --name nginx -d nginx:1.102、创建 /mydata/nginx/conf 目录, 并切换到此目录下将 Nginx 配置文件都拷贝文件夹里
docker container cp nginx:/etc/nginx .3、删除容器并重新启动 Nginx 容器
docker run -p 80:80 --name nginx --restartalways \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.104、访问 虚拟机 IP 地址即可访问 Nginx