❤ 作者主页: 欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于 Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得 关注、 点赞、 收藏、 评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉
一、Elasticsearch概述
1. Elasticsearch
Elaticsearch
,简称为 es
, es
是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es
也使用Java
开发并使用Lucene
作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API
来隐藏Lucene
的复杂性,从而让全文搜索变得简单。
2. Solar
- Solr是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
- Solr可以独立运行,运行在letty. Tomcat等这些Selrvlet容器中 , Solr 索引的实现方法很简单,用POST方法向Solr服务器发送一个描述Field及其内容的XML文档, Solr根据xml文档添加、删除、更新索引。Solr 搜索只需要发送HTTP GET请求,然后对Solr返回xml、json等格式的查询结果进行解析,组织页面布局。
- Solr不提供构建UI的功能, Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。
3. ElasticSearch和Solr对比
4. ElasticSearch vs Solr 总结
es
基本是开箱即用(解压就可以用!) ,非常简单。Solr
安装略微复杂一丢丢。- Solr 利用Zookeeper进行分布式管理,而
Elasticsearch
自身带有分布式协调管理功能。 - Solr 支持更多格式的数据,比如JSON、XML、 CSV ,而Elasticsearch仅支持json文件格式。
- Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑。
Solr 查询快,但更新索引时慢(即插入删除慢) ,用于电商等查询多的应用:
- ES建立索引快(即查询慢) ,即实时性查询快,用于facebook新浪等搜索。
- Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用。
- Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。
二、Elasticsearch安装
1. 下载
Elasticsearch
的官方地址:https://www.elastic.co/cn/
官网的下载比较慢,可以使用华为云的镜像进行下载(找到相对于的版本进行下载即可):Elasticsearch
:ttps://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
2. 安装
下载后对压缩包进行解压即可,相关的目录如下:
3. 启动
解压后,进入 bin目录,点击 elasticsearch.bat
文件,启动es服务:
打开浏览器,在地址栏中输入:http://127.0.0.1:9200/,返回的数据如下:
三、head插件的安装
head插件:即es的可视化界面
npm install
npm run start
- 连接成功后,存在跨域问题:配置es
在elasticsearch.yml中添加如下配置:
#是否允许跨域
http.cors.enabled: true
#允许跨域访问 *代表所有
http.cors.allow-origin: "*"
- 重启es服务器,然后再次连接
四、安装 Kibana
1. Kibana的定义
Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分许及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示Elasticsearch查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引检测。
2. 下载Kibana
官网:https://www.elastic.co/cn/kibana/
通过镜像下载:https://mirrors.huaweicloud.com/kibana/?C=N&O=D
3. 启动
下载后进行压缩,目录如下;
双击bin包下的 kibana.bat
进行启动:
4. 访问测试
在浏览器地址栏输入:http://127.0.0.1:5601/
5. 汉化
在 config
包下修改 kibana.yml
:
再次访问即可显示中文版本:
五、ES核心概念理解
六、IK分词器详解
1. IK分词器的定义
分词: 即把一段中文或别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱编程”会被分为“我”,“爱”,“编”,“程”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
IK提供了两个分词算法:ik_smart
和 ik_max_word
,其中 ik_smart
为最小切分,ik_max_word
为最细粒度划分。
2. 安装
- 下载:https://github.com/medcl/elasticsearch-analysis-ik/releases
- 下载完成后,放入到我们的
elasticsearch
插件中即可。
- 重启观察ES,可以看到ik分词器被加载了
elasticsearch-plugin
可以通过这个命令来查看加载进来的插件
- 使用kinaba测试
ik_smart
:
ik_max_word
:
ik 分词器增加自己的配置: 添加自定义的词添加到扩展字典中。
重启es服务,再次进行测试:
七、Rest风格操作
1. Rest风格的定义
Rest
风格: 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于 客户端和服务器交互类 的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
2. 基本Rest命令说明:
method | url地址 | 描述 |
---|---|---|
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/索引名称/类型名称/文档id/_search | 查询所有数据 |
3. 关于索引的基本操作
- 创建索引(PUT)
指定字段的类型:
- 获取索引信息(GET)
- 修改
- 删除(DELETE)
通过 DELETE命令
实现删除,根据你的请求来判断是删除索引还是删除文档记录。
4. 关于文档的基本操作
- 添加数据
- 获取数据
- 普通查询
- 根据条件查询
5. 复杂查询
hit
里面包含了所有文档的具体信息。
- 过滤结果
- 排序
- 分页
- 多条件查询
- 过滤器filter
- 多条件查询
- 精确查询
term查询
是直接通过倒排索引指定的词条进程精确查找的。
关于分词:
- term:直接查询精确的
- match:会使用分词器解析。(先分析文档,然后通过分析的文档进行查询)
两种类型: text
、keyword
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 name",
"desc": "李四学Java desc2"
}
- 高亮
- 自定义高亮的样式
八、SpringBoot集成ES详解
1. 导入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.0</version>
</dependency>
要保证导入的依赖和我们的es 版本一致:
<!--这边配置下自己对应的版本-->
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.14.0</elasticsearch.version>
</properties>
2. 设置客户端对象
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient() {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("127.0.0.1", 9200, "http"))
);
return client;
}
}
核心配置类(源码):
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
package org.springframework.boot.autoconfigure.elasticsearch;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RestHighLevelClient.class})
@ConditionalOnMissingBean({RestClient.class})
@EnableConfigurationProperties({ElasticsearchRestClientProperties.class})
public class ElasticsearchRestClientAutoConfiguration {
public ElasticsearchRestClientAutoConfiguration() {
}
private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {
PropertiesCredentialsProvider(ElasticsearchRestClientProperties properties) {
if (StringUtils.hasText(properties.getUsername())) {
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(), properties.getPassword());
this.setCredentials(AuthScope.ANY, credentials);
}
properties.getUris().stream().map(this::toUri).filter(this::hasUserInfo).forEach(this::addUserInfoCredentials);
}
private URI toUri(String uri) {
try {
return URI.create(uri);
} catch (IllegalArgumentException var3) {
return null;
}
}
private boolean hasUserInfo(URI uri) {
return uri != null && StringUtils.hasLength(uri.getUserInfo());
}
private void addUserInfoCredentials(URI uri) {
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort());
Credentials credentials = this.createUserInfoCredentials(uri.getUserInfo());
this.setCredentials(authScope, credentials);
}
private Credentials createUserInfoCredentials(String userInfo) {
int delimiter = userInfo.indexOf(":");
if (delimiter == -1) {
return new UsernamePasswordCredentials(userInfo, (String)null);
} else {
String username = userInfo.substring(0, delimiter);
String password = userInfo.substring(delimiter + 1);
return new UsernamePasswordCredentials(username, password);
}
}
}
static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {
private static final PropertyMapper map = PropertyMapper.get();
private final ElasticsearchRestClientProperties properties;
DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
this.properties = properties;
}
public void customize(RestClientBuilder builder) {
}
public void customize(HttpAsyncClientBuilder builder) {
builder.setDefaultCredentialsProvider(new ElasticsearchRestClientAutoConfiguration.PropertiesCredentialsProvider(this.properties));
}
public void customize(Builder builder) {
PropertyMapper var10000 = map;
ElasticsearchRestClientProperties var10001 = this.properties;
var10001.getClass();
var10000.from(var10001::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis).to(builder::setConnectTimeout);
var10000 = map;
var10001 = this.properties;
var10001.getClass();
var10000.from(var10001::getReadTimeout).whenNonNull().asInt(Duration::toMillis).to(builder::setSocketTimeout);
}
}
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean({RestHighLevelClient.class})
static class RestHighLevelClientConfiguration {
RestHighLevelClientConfiguration() {
}
// RestHighLevelClient 高级客户端,也是我们这里要讲,后面项目会用到客户端
@Bean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}
}
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean({RestClientBuilder.class})
static class RestClientBuilderConfiguration {
RestClientBuilderConfiguration() {
}
@Bean
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
return new ElasticsearchRestClientAutoConfiguration.DefaultRestClientBuilderCustomizer(properties);
}
// RestClientBuilder
@Bean
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties, ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
HttpHost[] hosts = (HttpHost[])properties.getUris().stream().map(this::createHttpHost).toArray((x$0) -> {
return new HttpHost[x$0];
});
RestClientBuilder builder = RestClient.builder(hosts);
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
builderCustomizers.orderedStream().forEach((customizer) -> {
customizer.customize(httpClientBuilder);
});
return httpClientBuilder;
});
builder.setRequestConfigCallback((requestConfigBuilder) -> {
builderCustomizers.orderedStream().forEach((customizer) -> {
customizer.customize(requestConfigBuilder);
});
return requestConfigBuilder;
});
builderCustomizers.orderedStream().forEach((customizer) -> {
customizer.customize(builder);
});
return builder;
}
private HttpHost createHttpHost(String uri) {
try {
return this.createHttpHost(URI.create(uri));
} catch (IllegalArgumentException var3) {
return HttpHost.create(uri);
}
}
private HttpHost createHttpHost(URI uri) {
if (!StringUtils.hasLength(uri.getUserInfo())) {
return HttpHost.create(uri.toString());
} else {
try {
return HttpHost.create((new URI(uri.getScheme(), (String)null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment())).toString());
} catch (URISyntaxException var3) {
throw new IllegalStateException(var3);
}
}
}
}
}
3. 关于索引的API操作详解
@SpringBootTest
class EsApiApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
@Test
void testCreateIndex() throws IOException {
// 1、创建索引请求
CreateIndexRequest request = new CreateIndexRequest("kuang_index");
// 2、客户端执行请求 indexResponse, 请求后获得相应
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
// 测试获取索引,只能判断其是否存在 查
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("kuang_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 删除索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("kuang_index");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
}
4. 关于文档的API操作详解
// 测试创建文档
@Test
void testAddDocument() throws IOException {
// 创建对象
User user = new User("狂神说", 3);
// 索引请求
IndexRequest request = new IndexRequest("kuang_index");
// 规则 put /kuang_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueDays(1));
// request.timeout("1");
// 将我们的数据放入请求 json
request.source(JSON.toJSONString(user), XContentType.JSON);
// 客户端发送请求,获取响应结果
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
System.out.println(indexResponse.status());
}
// 获取文档,判断是否存在 get /index/doc/1
@Test
void testIsExists() throws IOException {
GetRequest request = new GetRequest("kuang_index", "1");
// 不获取返回的 _source 的上下文了
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields("_none_");
boolean exists = client.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 获取文档信息
@Test
void testGetDocument() throws IOException {
GetRequest request = new GetRequest("kuang_index", "1");
GetResponse getResponse = client.get(request, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString()); // 打印文档的内容
System.out.println(getResponse); // 返回的全部内容和命令是一样的
}
// 更新文档的信息
@Test
void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("kuang_index", "1");
updateRequest.timeout("1s");
User user = new User("狂神说java", 18);
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(updateResponse.status());
}
// 删除文档记录
@Test
void testDeleteRequest() throws IOException {
DeleteRequest request = new DeleteRequest("kuang_index", "3");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
// 批量插入
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> list = new ArrayList<>();
list.add(new User("kuangshen1", 3));
list.add(new User("kuangshen2", 3));
list.add(new User("kuangshen3", 3));
list.add(new User("qinjiang1", 3));
list.add(new User("qinjiang2", 3));
list.add(new User("qinjiang3", 3));
// 批处理请求
for (int i = 0; i < list.size(); i++) {
// 批量更新和批量删除,就在这里修改对应的请求就可以了
bulkRequest.add(
new IndexRequest("kuang_index")
.id("" + (i + 1))
.source(JSON.toJSONString(list.get(i)), XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulkResponse.hasFailures()); // 是否失败,返回false代表成功
}
// 查询
// HighLightBuilder 构建高亮
// MatchAllQueryBuilder
// xxx QueryBuilder 对应我们刚才看到的命令!
@Test
void testSearch() throws IOException {
// SearchRequest 搜索请求
SearchRequest searchRequest = new SearchRequest("kuang_index");
// SearchSourceBuilder 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// TermQueryBuilder查询条件,我们可以使用 QueryBuilders 工具来实现
// QueryBuilders.termQuery 精确
// QueryBuilders.matchAllQuery 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "qinjiang1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(searchResponse.getHits()));
System.out.println("===========================");
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
创作不易,如果有帮助到你,请给题解==点个赞和收藏==,让更多的人看到!!!
==关注博主==不迷路,内容持续更新中。