文章目录
前言
记载ElasticSearch的使用
ElasticSearch的安装:https://blog.csdn.net/weixin_43287895/article/details/126465516
ElasticSearch
Elasticsearch 是位于 Elastic Stack 核心的分布式搜索和分析引擎。Logstash 和 Beats 有助于收集、聚合和丰富您的数据并将其存储在 Elasticsearch 中。Kibana 使您能够以交互方式探索、可视化和分享对数据的见解,并管理和监控堆栈。Elasticsearch 是索引、搜索和分析魔法发生的地方。(百度百科)
分布式搜索引擎:Solr
数据清洗ELK
用于全文搜索、结构化搜索、分析以及将这三者混合使用
通过RestFulAPI
ElasticSearch和Solr区别
架构选择!
当对已经有了的数据进行搜索时,Solr更快
当实时建立索引时,Solr会产生io阻塞,查询性能较差,ElasticSearch具有明显优势
随着数据量的增加,Solr的搜索效率会变得较低,ElasticSearch没有明显的变化
传统项目Solr转为ElasticSearch会有10倍的效率提高
区别
Solr利用Zookeeper进行分布式管理,ElasticSearch自身带有分布式协调管理功能
Solr支持更多的格式的数据,比如:JSON、XML、CSV,而ElasticSearch仅支持json文件格式
Solr官方功能更多,而ElasticSearch专注于核心功能,高级功能由第三方插件提供,如图形化界面需要Kibana
Solr查询快,但更新索引时慢(插入删除慢)。用于电商较多查询的应用
ElasticSearch建立索引快(即查询慢),即实时性查询快,用于facebook新浪等搜索
Solr是传统搜索应用的有力解决方案,但ElasticSearch用于新型的实时搜索应用
Solr比较成熟,ElasticSearch更新较快,学习成本高
ElasticSearch是基于Lucene做了一些封装和增强
ElasticSearch安装
安装
需要JDK1.8以上
ElasticSearch客户端
Kibana 界面工具
Java的jar包要版本对应和ElasticSearch
目录
bin 启动文件
config 配置文件
log4j2 日志配置文件
jvm.options JVM配置文件,电脑不好的话,需要修改
elasticsearch.yml elasticsearch配置文件 9200 跨域
lib 相关jar包
modules 功能模块
plugins 插件 ik分词器
logs 日志
bin/ElasticSearch.bat运行即可,9200端口
# 创建网络
docker network create elastic
#拉取镜像
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.16.0
# docker 安装
docker run -d --name es01 --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xms512m" docker.elastic.co/elasticsearch/elasticsearch:7.16.0
安装可视化插件head插件
地址:https://github.com/mobz/elasticsearch-head
需要安装node.js
然后按github安装即可
运行在9100
npm install
npm run start
# docker安装
docker run -d --name es-head -p 9100:9100 docker.io/mobz/elasticsearch-head:5
docker exec -it es01 /bin/bash
yum install vim
vim config/elasticsearch.yml
#修改为下面代码,注意冒号后有一个空格
#这是yml的固定格式,在vim中可以使用ctrl+n进行代码补全
http.host: 0.0.0.0
http.cors.enabled: true
http.cors.allow-origin: "*"
#退出es容器
exit
#重启es容器
docker restart es01
解决head插件不显示数据
# 进入head里
apt update
apt install vim
cd usr/src/app/_site
vim vendor.js
# 显示行号
:set nu
# 跳转6886行
:6886
# 修改6886行
contentType: "application/x-www-form-urlencoded
改成
contentType: “application/json;charset=UTF-8”
# 跳转6886行
:7573
# 修改7573行
var inspectData = s.contentType === “application/x-www-form-urlencoded”
改成
var inspectData = s.contentType === “application/json;charset=UTF-8”
跨域问题解决
打开ElasticSeacher.yml
http.cors.enabled: true # 开启
http.cors.allow-origin: "*" # 允许所有人
重启ES服务,再次连接
可以将es当成一个数据库(可以建立索引(库),文档(数据))
head可以当做一个数据展示工具,后面所有的查询在Kibana做
安装Kibana
针对于ElasticSearch的开源分析及可视化平台,用来搜索,查看交互存储在ElasticSearch索引中的数据
Kibana要和ElasticSearch版本一致
解压,运行bat
运行在5601
访问测试即可
开发工具测试
postman,curl或者head或者谷歌浏览器插件
汉化
在config配置中修改kibana.yml
i18n为zh-CN
原因是在x-pack中有i18n的json文件,i18n是国际化,在x-pack的plugins中有translations文件夹,文件夹中translations包含zh-CN.json
# docker 安装
docker pull docker.elastic.co/kibana/kibana:7.16.0
# 运行
docker run -d --name kib01 --net elastic -p 5601:5601 -e ELASTICSEARCH_HOSTS=http://172.21.0.2:9200 docker.elastic.co/kibana/kibana:7.16.0
docker exec -it kib01 /bin/bash
vi /usr/share/kibana/config/kibana.yml
# 修改
server.name: kibana
server.host: "0.0.0.0"
elasticsearch.hosts: [ "http://172.21.0.2:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: zh-CN
#设置kibana中文显示,也可以不加
# 需要重启
docker restart kib01
# 出现Kibana server is not ready yet
nmcli connection modify docker0 connection.zone trusted
systemctl stop NetworkManager.service
firewall-cmd --permanent --zone=trusted --change-interface=docker0
systemctl start NetworkManager.service
nmcli connection modify docker0 connection.zone trusted
systemctl restart docker.service
安装IK分词器
即把一段中文或者别的划分成一个一个关键字,我们在搜索的时候会把自己的信息进行分词,会把数据库中的或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如:你好,会被分成,你,好。
IK分词器提供了两个分词算法:ik_smart和ik_max_word
ik_smart为最少切分
ik_max_word 为最细粒度划分
下载完毕后,放到elasticsearch/plugins
重启观察ES,可以看到日志输出有ik分词器加载
cmd命令,可以通过elasticsearch plugins list,查看加载的插件
使用kibana测试
ik_smart
ik_max_word
# docker 安装
docker exec -it es01 /bin/bash
apt-get update
apt-get -y install wget
cd plugins/
mkdir ik/
cd ik/
#我们在官网中找到与es相对应版本的ik
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.16.0/elasticsearch-analysis-ik-7.16.0.zip
unzip elasticsearch-analysis-ik-7.16.0.zip
rm elasticsearch-analysis-ik-7.16.0.zip
#退出容器,最后重启es即可
docker restart es01
自己需要的词,需要自己加到分词器的字典中
ik分词器增加自己的配置
在ik的config中
IKAnalyzer.cfg.xml文件是ik的配置
需要自己写一个dic文件,如mydic.dic
里面写自己的词汇
然后在IKAnalyzer.cfg.xml配置识别
<entry key="ext_dict">mydic.dic</entry>
之后重启es
ES核心概念
集群,节点,索引,类型,文档,分片,映射
elasticsearch是面向文档
关系行数据库和elasticsearch对比
Realtional DB | ElasticSearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | types 弃用了 |
行(rows) | documents |
字段(columns) | fields |
一切都是json
物理设计
elasticsearch在后台把每个索引划分成多个分片,每个分片可以在集群中的不同服务器之间迁移
一个人就是一个集群
默认的集群名称是elasticsearch
逻辑设计
一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文章的时候,可以通过这样的顺序找到它:索引----->类型---->文档ID,通过这个组合我们就能索引到某个具体的文档。ID不必是整数,实际上是字符串
文档
elasticsearch是面向文档的,索引和搜索数据的最小单位就是文档
就是一条条数据
文档重要属性:
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的(就是json字符串,fastjson自动转换)
- 灵活的结构,文档不依赖预先定义的模式,在关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,可以忽略字段,或者动态的添加一个字段
类型
类型是文档的逻辑容器,不太常用
字符串类型
text、keyword
数值类型
long,integer,short,byte,double,float,half float,scaled float
日期类型
date
布尔值类型
boolean
二进制类型
binary
等等
索引
就是数据库
elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后他们被存储到各个分片上了。
物理设计:节点和分片
一个集群至少有一个节点,而一个节点就是一个elasticsearch进程,节点可以有多个索引默认的,如果创建索引,那么索引就有5个分片,默认的,拥有主分片和复制分片(主分片的副本)
主分片和对应的复制分片都不会在同一个节点内,这样有利于一个节点挂了,数据也不会丢失。
实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字
倒排索引
倒排索引的意思,也就说完全过滤掉了不需要的搜索内容,通过内容整合,把有关的放在一起,搜索的时候就不会一个一个去找,直接找相关部分的一个整体集合,如果我搜A,绝对不会出现B
elasticsearch使用的是一种被称为倒排索引的结构,采用Lucene倒排索引作为底层。这种结构适用于快速的全文索引,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表,例如有两个文档
Study every day, good good up to forever
To forever,study every day,good good up
term | doc1 | doc2 |
---|---|---|
Study | √ | × |
To | × | × |
every | √ | √ |
forever | √ | √ |
day | √ | √ |
study | × | √ |
good | √ | √ |
every | √ | √ |
to | √ | × |
up | √ | √ |
如果试图搜索to forever,只需要查看包含每个词条的文档
term | doc1 | doc2 |
---|---|---|
to | √ | × |
forever | √ | √ |
total | 2 | 1 |
两个文档都匹配,但是第一个文档比第二个文档匹配程度高,如果没有别的条件,现在,这两个包含关键字的文档将都被返回
也就是说匹配程度高,权重高,score高
Rest风格说明
method | ||
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过id |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
索引操作
创建索引
PUT /索引名(数据库名)/类型名(略)/文档id
{
请求体
}
PUT /test1/type1/1
{
"name": "你好",
"age": 3
}
建立索引和对应的字段,不添加值,用于创建规则
PUT /test2
# mapping为固定写法
{
"mappings": {
"properties":{
"name":{
"type": "long"
},
"age":{
"type": "long"
},
"birthday": {
"type": "date"
}
}
}
}
查看默认的信息
PUT /test3/_doc/1
# 因为类型的弃用,以后都用_doc替代
{
"name": "你好",
"age" : 12,
"birth" : "1997-01-02"
}
GET test3
发现类型虽然都没写,但是自动进行了匹配,ES会默认配置字段类型
GET _cat/indices?v
# 可以查询一些默认的配置
修改索引
# 修改提交也可以使用PUT直接覆盖进行修改,修改后,version会变化,result会变化成updated
PUT /test1/type1/1
{
"name": "你好123",
"age": 3
}
# POST 修改,命令中加入_update,doc是固定格式
POST /test3/_doc/1/_update
{
"doc":{
"name": "世界"
}
}
删除索引
# 根据请求,判断是删除索引,还是删除文档
DELETE test1
文档操作
添加数据
PUT /test1/user/3
{
"name": "王五",
"age": 44,
"desc": "喜爱器",
"tags": ["技术","下棋","核查"]
}
获取数据
GET /test1/user/1
# 通过id查询
GET /test1/user/_search?q=name:"小垃圾"
# 通过条件
更新数据
PUT /test1/user/3
{
"name": "王五",
"age": 55,
"desc": "喜爱器",
"tags": ["技术","下棋","核查"]
}
# 如果不传递值,就会被覆盖
POST /test1/user/1/_update
{
"doc":{
"name": "小垃圾"
}
}
# 一般常用
复杂搜索
查询出来的结果有score,score是匹配度,匹配度越高分值越高
GET test1/user/_search
{
# 查询参数体,用json构建
"query": {
"match": {
"name": "小"
}
}
}
hit:
索引和文档信息
查询结果的总数
查询出来的具体的文档
数据中的都可以遍历出来了
分数:通过分数判断谁更符合结果
结果的过滤
GET test1/user/_search
{
"query": {
"match": {
"name": "小"
}
},
"_source": ["name","desc"]
}
}
# 查询的时候会显示全部信息,加入_source后,只会显示所列举的信息
排序
sort
GET test1/_search
{
"query": {
"match": {
"name": "小"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
分页
from:从第几条开始
size:返回多少条数据
GET test1/_search
{
"query": {
"match": {
"name": "小"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
, "from": 0
, "size": 1
}
布尔值查询
must,可以做出精确查询的操作,所有的条件都要符合,相当于and
GET test1/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "小"
}
},
{
"match": {
"age": 2
}
}
]
}
}
}
should操作,相当于or
GET test1/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "小"
}
},
{
"match": {
"age": 2
}
}
]
}
}
}
过滤器
filter和range
GET test1/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "小"
}
}
],
"filter": [
{
"range": {
"age": {
"gt": 10
}
}
}
]
}
}
}
gt 大于
gte 大于等于
lt 小于
lte 小于等于
匹配多个条件
需要匹配多个条件的时候,只需要空格隔开即可
只要满足一个就可以被查出,可以通过分值来判断匹配程度
GET test1/_search
{
"query": {
"match": {
"tags": "男 技术"
}
}
}
精准查询
term 查询是通过倒排索引查询指定查询
关于分词:
term,直接查询精确的
match:会使用分词器解析(先分析文档,在通过分析的文档继续查询)
两个类型:
text,keyword
text可以被分词器解析
keyword不会被分词器解析
PUT test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "keyword"
}
}
}
}
# _doc 默认的文档类型
PUT test2/_doc/1
{
"name": "小垃圾",
"desc": "什么都不会"
}
PUT test2/_doc/2
{
"name": "小垃圾",
"desc": "什么都不会2"
}
GET _analyze
{
"analyzer": "keyword",
"text": ["小垃圾"]
}
GET _analyze
{
"analyzer": "standard",
"text": ["小垃圾"]
}
keyword,没有被分析,当成一个整体
standard
GET test2/_search
{
"query": {
"term": {
"desc" :"什么都不会"
}
}
}
# 只会查出来一个,因为desc是keyword不会被解析
GET test2/_search
{
"query": {
"term": {
"name" :"小"
}
}
}
# 可以查出来两个,因为name是text类型,会被分词器解析
多个值匹配精确查询
PUT /test2/_doc/3
{
"t1":"22",
"t2": "2022-7-26"
}
PUT /test2/_doc/4
{
"t1":"33",
"t2": "2022-7-27"
}
GET test2/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"t1": "22"
}
},
{
"term": {
"t1": "33"
}
}
]
}
}
}
高亮
highlight
GET test1/_search
{
"query": {
"match": {
"name": "小垃圾"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
高亮部分会自动添加
如果想要修改成自己的标签
可以用pre_tags和post_tags
GET test1/_search
{
"query": {
"match": {
"name": "小垃圾"
}
},
"highlight": {
"pre_tags": "<p class='key' style='color:red'>",
"post_tags": "</p>",
"fields": {
"name": {}
}
}
}
集成Springboot
依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.16.3</version>
</dependency>
新建项目
需要修改javac为1.8,JavaScript为es6,project使用8
看springboot的es版本是否和自己的版本对应
自定义es版本和本地保持一致
修改pom文件的
properties属性就可以
点击parent标签的version,再点击artifactID,复制elasticsearch.version,复制到pom的properties里即可
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.16.0</elasticsearch.version>
</properties>
配置文件
@Configuration
public class ElasticSearchConfig {
@Bean
public RestHighLevelClient restHighLevelClien(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("47.93.57.13", 9200, "http")
));
return client;
}
}
测试
新建索引
@SpringBootTest
class DemoApplicationTests {
@Autowired
@Qualifier("restHighLevelClien")
private RestHighLevelClient client;
@Test
void testCreateIndex() throws IOException {
//索引的创建
//1.创建索引的请求
CreateIndexRequest request = new CreateIndexRequest("my_index");
//2.执行创建请求,获得响应
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
}
判断索引是否存在
//判断索引是否存在
@Test
void testExistIndex() throws IOException {
GetIndexRequest my_index = new GetIndexRequest("my_index");
boolean exists = client.indices().exists(my_index, RequestOptions.DEFAULT);
System.out.println(exists);
}
删除索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest my_index = new DeleteIndexRequest("my_index");
AcknowledgedResponse delete = client.indices().delete(my_index, RequestOptions.DEFAULT);
System.out.println(delete);
}
文档操作
添加文档
@Test
void testAddDoc() throws IOException {
// put/my_test/_doc/1
//创建对象
User user = new User("小垃圾",3);
//创建请求
IndexRequest my_test = new IndexRequest("my_test");
//规则
my_test.id("1");
my_test.timeout("1s");
//将数据放入,用fastjson
my_test.source(JSON.toJSONString(user), XContentType.JSON);
//发送请求,获取响应的结果
IndexResponse index = client.index(my_test, RequestOptions.DEFAULT);
System.out.println(index.toString());
System.out.println(index.status());
}
获取文档
//获取文档的信息
@Test
void testgetDoc() throws IOException {
GetRequest getRequest = new GetRequest("my_index","1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString());
System.out.println(getResponse.getSourceAsMap());
System.out.println(getResponse);
}
更新文档
@Test
void testupdateDoc() throws IOException {
GetRequest getRequest = new GetRequest("my_index","1");
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.timeout("1s");
User user = new User("小东西",1);
updateRequest.doc(JSON.toJSONString(user));
UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update.status());
System.out.println(update);
}
删除文档
@Test
void testDeleteDoc() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("my_index","1");
deleteRequest.timeout("1s");
DeleteResponse delete = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
批量插入数据
//批量插入数据
@Test
void testBlukDoc() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> list = new ArrayList<>();
list.add(new User("年后1",3));
list.add(new User("chish",5));
for (int i = 0; i < list.size(); i++) {
bulkRequest.add(
new IndexRequest("my_index")
.id(""+(i+1))
.source(JSON.toJSONString(list.get(i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures());//是否失败,成功返回false
}
查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
//高亮,查询等
//HighlightBuilder高亮
//TermQueryBuilder精确
//MatchAllQueryBuilder匹配所有
@Test
void testSearchdoc() throws IOException {
SearchRequest searchRequest = new SearchRequest("my_index");
//MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "小垃圾");
//精确
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(termQueryBuilder);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(searchSourceBuilder);
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(search.getHits()));
System.out.println("==================");
for (SearchHit hit : search.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}