zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

Elasticsearch 学习笔记(总)

2023-09-11 14:20:19 时间

一、Elasticsearch概述

1. Elasticsearch

Elaticsearch,简称为 eses 是一个开源的高扩展分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理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 总结

  1. es基本是开箱即用(解压就可以用!) ,非常简单。Solr安装略微复杂一丢丢。

  2. Solr 利用Zookeeper进行分布式管理,Elasticsearch自身带有分布式协调管理功能

  3. Solr 支持更多格式的数据,比如JSON、XML、 CSV ,而Elasticsearch仅支持json文件格式

  4. Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑。

  5. Solr 查询快,但更新索引时慢(即插入删除慢) ,用于电商等查询多的应用:

    • ES建立索引快(即查询慢) ,即实时性查询快,用于facebook新浪等搜索。
    • Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用。
  6. 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的可视化界面

  1. 下载地址:https://github.com/mobz/elasticsearch-head/archive/master.zip
  2. 启动
npm install
npm run start
  1. 连接成功后,存在跨域问题:配置es
在elasticsearch.yml中添加如下配置:

#是否允许跨域
http.cors.enabled: true
#允许跨域访问 *代表所有
http.cors.allow-origin: "*"
  1. 重启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_smartik_max_word,其中 ik_smart 为最小切分,ik_max_word 为最细粒度划分。

2. 安装

  1. 下载:https://github.com/medcl/elasticsearch-analysis-ik/releases

  2. 下载完成后,放入到我们的 elasticsearch 插件中即可。
    在这里插入图片描述
     

  3. 重启观察ES,可以看到ik分词器被加载了
    在这里插入图片描述
     

  4. elasticsearch-plugin 可以通过这个命令来查看加载进来的插件
    在这里插入图片描述
     

  5. 使用kinaba测试
    ik_smart
    在这里插入图片描述
     
    ik_max_word:
    在这里插入图片描述

 
ik 分词器增加自己的配置: 添加自定义的词添加到扩展字典中。
在这里插入图片描述
 
重启es服务,再次进行测试:
在这里插入图片描述
在这里插入图片描述
 

七、Rest风格操作

1. Rest风格的定义

Rest 风格: 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于 客户端和服务器交互类 的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

2. 基本Rest命令说明:

methodurl地址描述
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. 关于索引的基本操作

  1. 创建索引(PUT)
    在这里插入图片描述
     
    指定字段的类型:
    在这里插入图片描述
     
  2. 获取索引信息(GET)
    在这里插入图片描述
     
  3. 修改
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
     
  4. 删除(DELETE)
    通过 DELETE命令 实现删除,根据你的请求来判断是删除索引还是删除文档记录。
     

4. 关于文档的基本操作

  1. 添加数据
    在这里插入图片描述
  2. 获取数据
  • 普通查询
    在这里插入图片描述

  • 根据条件查询
    在这里插入图片描述
     

5. 复杂查询

hit 里面包含了所有文档的具体信息。
在这里插入图片描述

 

  1. 过滤结果
    在这里插入图片描述
     

  2. 排序
    在这里插入图片描述
     

  3. 分页
    在这里插入图片描述
     

  4. 多条件查询
    在这里插入图片描述
     

  5. 过滤器filter
    在这里插入图片描述

 
6. 多条件查询
在这里插入图片描述
 
7. 精确查询
term查询 是直接通过倒排索引指定的词条进程精确查找的。

关于分词:

  • term:直接查询精确的
  • match:会使用分词器解析。(先分析文档,然后通过分析的文档进行查询)

两种类型: textkeyword

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"
}

 
在这里插入图片描述
在这里插入图片描述
 
8. 高亮
在这里插入图片描述
 
9. 自定义高亮的样式
在这里插入图片描述
 

八、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());
        }
    }

 
 
创作不易,如果有帮助到你,请给题解点个赞和收藏,让更多的人看到!!!
关注博主不迷路,内容持续更新中。