ElasticSearch(狂神说笔记)
ES安装及head插件安装
声明:JDK 1.8以上,最低要求!ElasticSearch 客户端。界面工具!
Java 开发,ElasticSearch的版本和我们之后对应的Java的核心jar包!版本对应!JDK环境是正常!
官网:https://www.elastic.co/cn/elasticsearch/
ELK三件客,解压即用‘ 我用的都是 7.6.1的版本
window下安装
- 解压即用
- 熟悉目录
bin 启动文件
config 配置文件
log4j2 日志配置文件
jvm,options java 虚拟机配置
elasticsearch.yml elasticsearch配置文件 默认端口: 9200
lib 相关jar包
modules 功能模块
plugins 插件!ik 分词器
-
启动 点击elasticsearch.bat
-
访问测试
安装可视化插件 header插件
没有前端 nodejs 是不可以用的
- 下载地址:https://github.com/mobz/elasticsearch-head
- 安装 当前目录下打开cmd
- npm install
- npm run start
- 解决跨域问题,配置elasticsearch.yml
http.cors.enabled: true http.cors.allow-origin: "*"
- 初学,就把es当作一个数据库!(可以建立索引(库),文档(库中的数据))
·这个header我们就把它当做数据展示工具!我们后面所有的查询,Kibana来做·
kibana的安装
了解ELK
ELK是 Elasticsearch,Logstash,Kibana 三大开源框架首字母大写简称。市面上也被称为Elastic Stack。其中ElasticSearch 是一个基于Lucene,分布式,通过Restful 方式进行交互的近实时搜索平台框架。像类似百度,谷歌这种大数据全文搜索引擎的场景都可以使用Elasticsearch作为底层支持框架,可见Elasticsearch提供的搜索能力确实强大,市面上很多时候我们简称Elasticsearch为es。Logstash是ELK的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana可以将elasticsearch的数据通过友好的页面展示出来,提供实时分析的功能。
收集清洗数据–>搜素,存储–>kibana
市面上很多开发只要提到ELK能够一致说出它是一个日志分析架构技术栈总称,但实际上ELK不仅仅适用于日志分析,它还可以支持其他任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。
- 启动 注意:kibana 版本要和Es 一致
- 访问测试 默认端口5601
经验:https://blog.csdn.net/a8761087/article/details/121166512 - 开发工具
ES 核心概念
elasticsearch 是面向文档,关系性数据库和elasticsearch 客观对比!一切都是JSON
集群,节点,索引,类型,文档,分片,映射是什么?
讲解:https://www.cnblogs.com/progor/p/11548269.html
elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)
物理设计:
elasticearch 在后台把每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
一个人就是一个集群!默认的集群名称就是 elasticsearch
逻辑设计:
一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:所以索引->类型->文档ID,通过这个组合我们就能索引到某个具体的文档。
注意:ID不必是整数,实际上它是个字符串
文档
就是一条条数据
user
1 zhangsan 18
2 kuangshen 3
之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的!(就是一个json对象!fastjson进行自动转换!)
- 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段
尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也成为映射类型。
类型
类型是文档的逻辑容器,就像关系型数据库一样,表是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那某elasticsearch是怎么做的呢?elasticsearch 会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。但是elatsicsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归,先定义好字段,然后再使用,别整什么幺蛾子。
索引
就是数据库!
索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了,我们来研究下分片是如何工作的。
物理设计:节点和分片如何工作
一个集群至少有一个节点,而一个节点就是一个ealsticsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有5个分片(primary shard,又称主分片)构成的,每一个主分片会有一个副本(replica shard,又称复制分片)
上图一个有3个节点的集群,可以看到主分片和对应复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下;就能告诉你哪些文档包含特定的关键字。不过,等等,倒排索引是什么鬼?
倒排索引
elasticsearch使用的是一种称为倒排索引的结构,采用Lucenne倒排索引作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。
例如,现在有两个文档,每个文档包含如下内容:
Study every day,good good up to forever # 文档1包含的内容
to forever,study every day,good good up # 文档2包含的内容
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,列出每个词条出现哪个文档:
现在,我们试图搜索to forever,只需要查看包含每个词条的文档
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。
再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:
倒排索引:根据value找到ID
如果要搜索含有python标签的文章,那相对于查找所哟原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章ID即可。完全过滤掉无关的所有数据,提高效率!
elasticsearch的索引和Lucene的索引对比
在elasticsearch中,索引(库)这个词被频繁使用,这就是术语的使用。在elasticsearch中,索引被分为多个分片,每份分片是一个Lucene的索引。所以 一个elasticsearch索引是由多个Lucene索引。别问为什么,谁让elasticsearch使用Lucene作为底层呢!如无特指,说起索引都是指elasticsearch的索引。
接下来的一切操作都在kibana中Dev Tools下的Console里完成。基础操作!
IK 分词器
-
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.1
-
解压放入到es对应的plugins下即可
-
重启观察ES,发现ik插件被加载了
4.当前目录下运行命令行窗口 elasticsearch-plugin list 可以通过这个命令来查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KJPvMmP-1639222954893)(./1638358548932.png)] -
使用kibana测试!
- ik_smart:最少切分
- ik_max_word:最细粒度划分
- 加入自己的字典 .dic 文件
注意字典的格式为 UTF-8=BOM
重启es,看细节
以后的话,我们需要自己配置分词就在自己定义的dic文件中进行配置即可!
Rest风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁 ,更有层次,更易于实现缓存等机制。
基本Rest命令说明:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o6lA1tKq-1639222954894)(./1638362393155.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rxH4Keg-1639222954894)(./1638364104716.png)]
- 创建一个索引
写法过时
PUT /索引名/~类型名~/文档id
{请求体}
# PUT 创建命令 test1 索引 type1 类型 1 id
PUT test1/type1/1
{
"name": "huang",
"age": 28
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HcjK7TIX-1639222954894)(./1638602230146.png)]
- name 这个字段需不需要指定类型!
- 字符串类型
text keyword 不可分割 - 数值类型
long integer short byte double float half_float scaled float - 日期类型
date - 布尔值类型
boolean - 二进制类型
binary - 等等…
指定字段的类型(创建规则)
PUT test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date"
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ifPOz82a-1639222954894)(./1638605716359.png)]
获取具体的索引规则
GET test2
# 查看默认的规则
PUT /test3/_doc/1
{
"name": "狂神说Java",
"age": 28,
"birthday": "1997-01-05"
}
GET test3
-
通过命令elasticsearch 索引情况!通过GET _cat/indices 可以获得es当前很多信息
-
修改索引 POST
# 只会修改指定项,其他内容保证不变
POST /test3/_doc/1/_update
{
"doc": {
"name":"暴徒狂神"
}
}
GET test3/_doc/1/
- 删除索引
通过DELETE命令删除,根据你的请求来判断是删除索引还是删除文档记录!
DELETE test3
DELETE test3/_doc/1
关于文档的操作
基本操作
添加数据
put /kuangshen/_doc/1
{
"name": "狂神说",
"age": 23,
"desc": "一顿操作猛如虎,一看工资2500",
"tags": ["码农", "技术宅", "直男"]
}
put /kuangshen/_doc/2
{
"name": "张三",
"age": 28,
"desc": "法外狂徒",
"tags": ["旅游", "渣男", "交友"]
}
put /kuangshen/_doc/3
{
"name": "李四",
"age": 30,
"desc": "不知道怎么描述",
"tags": ["旅游", "靓女", "唱歌"]
}
简单的条件查询数据,可以根据默认的映射规则,产生基本的查询!
GET kuangshen/_search?q=name:狂神
put 与 post update(修改指定项)的差别
put 其他项置空
post 只修改指定项
doc 文档层面吗?
POST kuangshen/_doc/3/_update
{
"doc":{
"name":"狂神说JAVA"
}
}
复杂查询(排序、分页、高亮、模糊查询、标准查询!)
kibana 里查询的参数体使用Json结构
GET kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"_source": ["name","des"]
}
eq ——> equal 代表相等
在JAVA里面 hit 是一个对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOWo1UMq-1639222954896)(./1638622445624.png)]
之后使用Java操作es,所有的方法和对象就是这里面的key!
- 排序 des降序 asc升序
GET kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"sort": [
{
"FIELD": {
"order": "desc"
}
}
]
}
- 分页 数据下标还是从0开始的,和学的所有数据结构是一样的!
GET kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 0,
"size": 1
}
- 多条件查询 must == and
- 多条件查询 should == or
- 多条件查询 must_not == not
GET kuangshen/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神说"
}
},
{
"match": {
"age": "23"
}
}
]
}
}
}
- 过滤查询1 age > 24 gt greater than 大于 gte >= (e equal)
- 过滤器2 22<age<30 lt less than 小于 lte <= (e equal)
GET kuangshen/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神说"
}
}
],
"filter": {
"range": {
"age": {
"gte": 10
, "lte": 30
}
}
}
}
}
}
多条件查询
GET kuangshen/_search
{
"query": {
"match": {
"tags": "男 技术"
}
}
}
- 精确查询
term 查询是直接通过倒排索引指定的词条进程精确查找的!
关于分词:
term,直接查询精确的
match,会使用分词器解析()
mapping介绍:https://www.knowledgedict.com/tutorial/elasticsearch-index-mapping.html
es 要小写
PUT testdb
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "keyword"
}
}
}
}
PUT testdb/_doc/1
PUT testdb
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "keyword"
}
}
}
}
PUT testdb/_doc/1
{
"name": "狂神说java name",
"desc": "狂神说java desc"
}
PUT testdb/_doc/2
{
"name": "狂神说java name2",
"desc": "狂神说java desc2"
}
GET testdb/_search
{
"query": {
"term": {
"desc": {
"value": "狂神说java desc"
}
}
}
}
keyword 字段不能被分词器解析
- 多个值匹配精确查询
PUT testdb/_doc/3
{
"t1": "22",
"t2": "2020-4-6"
}
PUT testdb/_doc/4
{
"t1": "33",
"t2": "2020-4-7"
}
GET testdb/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"t1": {
"value": "22"
}
}
},
{
"term": {
"t1": {
"value": "33"
}
}
}
]
}
}
}
- 高亮查询
GET kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"highlight": {
"pre_tags": "<p class='key' style='color:red'>",
"post_tags": "</p>",
"fields": {
"name": {}
}
}
}
集成Springboot
官网:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.6/index.html
- 依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
-
对象
-
分析这个类中方法
配置基本项目
问题:一定要保证 我们导入的依赖和我们的es版本一致
自定义依赖版本
<properties>
<java.version>1.8</java.version>
<!-- 自定义版本依赖 保证和本地一致-->
<elasticsearch>7.6.1</elasticsearch>
</properties>
关于文档的操作
配置类
package com.huang.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchClientConfig {
// spring beans 文件(id=restHighLevelClient class=RestHighLevelClient)
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient restHighLevelClient=new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"))
);
return restHighLevelClient;
}
}
package com.huang;
import com.alibaba.fastjson.JSON;
import com.huang.pojo.User;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.resolve.ResolveIndexAction;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class EsOneApplicationTests {
@Test
void contextLoads() {
}
@Autowired
RestHighLevelClient restHighLevelClient;
private String index="kuang_index";
// 新版的方法请参考官网
// 测试索引的创建 Request
@Test
void testCreateIndex() throws IOException {
// 1, 创建索引请求
CreateIndexRequest request=new CreateIndexRequest(index);
CreateIndexResponse createIndexResponse=
restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
// 测试索引存在
@Test
void testExists() throws IOException {
GetIndexRequest getIndexRequest = new GetIndexRequest(index);
boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 删除索引
@Test
void testDelete() throws IOException {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
System.out.println(delete);
}
// 添加文档
@Test
void testAdd() throws IOException {
User user = new User("kuang", 28);
// 创建请求
IndexRequest request = new IndexRequest(index);
// 规则 PUT /index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
// 将数据放入请求 json 对象转换为json
// XContentType.JSON 选择类型
request.source(JSON.toJSONString(user), XContentType.JSON);
// 客户端放入请求,获取响应的结果
IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
System.out.println(response.toString());
System.out.println(response.status());
}
// 获取文档 判断是否存在 GET /index/_doc/1
@Test
void testIsExisits() throws IOException {
GetRequest getRequest = new GetRequest(index, "1");
// 不获取返回的 _source 的上下文了
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 获取文档
@Test
void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest(index, "1");
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString());
System.out.println(getResponse);
}
// 更新文档
@Test
void testUpdate() throws IOException {
UpdateRequest updateRequest = new UpdateRequest(index, "1");
updateRequest.timeout("1s");
User user = new User("java", 18);
updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update);
}
// 删除文档
@Test
void testDeleteDocument() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest(index, "1");
deleteRequest.timeout("1s");
DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(delete);
}
// 批量插入(修改,删除操作类似)数据
@Test
void testBulk() throws IOException {
// 过时了
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> users = new ArrayList<>();
users.add(new User("kkuangshen1",11));
users.add(new User("kkuangshen2",12));
users.add(new User("kkuangshen3",13));
users.add(new User("kkuangshen4",14));
users.add(new User("kkuangshen5",15));
// 批处理请求, 修改,删除,只要在这里修改相应的请求就可以
for (int i = 0; i < users.size(); i++) {
bulkRequest.add(new IndexRequest(index)
.id(String.valueOf(i+2))
.source(JSON.toJSONString(users.get(i)),XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.status());
}
/*
查询
searchRequest 搜索请求
searchSourceBuilder 条件构造
highlightBuilder 构建高亮
termQueryBuilder 精确查询
matchAllQueryBuilder
xxx QueryBuilder 对应我们刚才看到的命令!
* */
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 查询条件, 可以使用QueryBuilders工具类实现
// QueryBuilders.termQuery 精确
// QueryBuilders.matchLLQuery() 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "kuang");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
sourceBuilder.query(termQueryBuilder);
sourceBuilder.from();
sourceBuilder.size();
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 所有的结果封装在searchHits里面
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSON(search.getHits()));
System.out.println("============");
for (SearchHit documentFields : search.getHits().getHits()) {
System.out.println(documentFields);
}
}
}
实战
从这位博主这里找到的资源:https://blog.csdn.net/qq_21197507/article/details/115076913?ops_request_misc=&request_id=&biz_id=102&spm=1018.2226.3001.4187