elasticsearch基本使用

elasticsearch

  • 0
    • 1. 安装
      • 1. Windows版
        • 下载es
        • 安装中文分词工具
        • 安装可视化图形工具Kibana
        • 添加用户名和密码
      • 2. docker版
  • 1 依赖
    • 1.1 maven
    • 1.2 gradle
  • 2 配置类
  • 3 注解说明
    • 3.1 @Document
    • 3.2 @Id
    • 3.3 @Transient
    • 3.4 @PersistenceConstructor
    • 3.5 @Field
      • text:
      • keyword:
    • 3.6 @GeoPointField
    • 3.7 @MultiField
    • 3.8 @Mapping
    • 3.9 @Setting
  • 4 创建映射(实体类)
  • 5 构建queryBuilder条件查询
    • 1. 等值查询
    • 2. 范围查询
    • 3. 模糊查询
    • 4. 多字段(multifield)查询
    • 5. 多条件查询
    • 6. 集合查询
    • 7. should查询
    • 8. should和must配合查询
    • 9. 是否存在查询
    • 10. 分页查询
    • 11 地理位置查询
  • 6 通过RestHighLevelClient查询
    • 模糊查询
    • 高亮查询
    • 地理位置查询

0

elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容

elasticsearch底层是基于lucene来实现的。

什么是elasticsearch?

一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能
什么是elastic stack(ELK)?

是以elasticsearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch
什么是Lucene?
是Apache的开源搜索引擎类库,提供了搜索引擎的核心API

注意:这里只是一个笔记,我是菜鸟,有啥不对的地方提出来,一起学习

1. 安装

1. Windows版

下载es

https://www.elastic.co/cn/downloads/past-releases#elasticsearch
可以选择下载的版本,这里建议7.6.1,高了可能就要jdk1.8以上了
elasticsearch基本使用_第1张图片

解压
es的bin目录下点elasticsearch.bat进行启动或命令行启动
elasticsearch基本使用_第2张图片
elasticsearch基本使用_第3张图片

安装中文分词工具

安装配置中文分词工具(根据不同版本下载不同的分词插件)【elasticsearch默认对中文不友好,所以要安装中文分词器ik】
下载网址:https://github.com/medcl/elasticsearch-analysis-ik/releases?page=8
elasticsearch基本使用_第4张图片

解压将文件名改为analysis-ik
elasticsearch基本使用_第5张图片

安装好后 重启es会看到 加载了 ik分词了
elasticsearch基本使用_第6张图片

在浏览器中输入localhost:9200检测是否运行成功(出现如下的界面则说明elasticsearch运行成功)
elasticsearch基本使用_第7张图片

分词:

  1. ik_smart为最少切分
  2. ik_max_word为最细粒度划分

安装可视化图形工具Kibana

kibana的版本和elasticsearch的版本必须一致
下载网址:https://www.elastic.co/cn/downloads/past-releases#kibana

elasticsearch基本使用_第8张图片

到\bin目录,双击kibana.bat(前提是保证elasticsearch服务已经启动)

elasticsearch基本使用_第9张图片

访问:http://localhost:5601

elasticsearch基本使用_第10张图片

添加用户名和密码

  1. 找到config目录下的elasticsearch.yml添加以下信息
xpack.security.enabled: true
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true

elasticsearch基本使用_第11张图片
2. 重启elasticsearch服务
3. 设置elasticsearch密码
到bin目录下输入cmd进入命令窗口,并执行以下命令

elasticsearch-setup-passwords interactive
  1. 然后就会让你一个个输入密码,都设置一样即可

5.找到 Kibana 目录下的config中的kibana.yml文件添加以下

elasticsearch.username: "elastic"
elasticsearch.password: "123456"

elasticsearch基本使用_第12张图片
不设置的话kibana连不上elasticsearch就启动不了

  1. 再启动就需要输入密码了
  • es的账号是elastic
  • 密码是你设置的

这里输入的是es的账号密码而不是kibana的账号密码
输入elastic,和密码才能登录
elasticsearch基本使用_第13张图片

2. docker版

https://blog.csdn.net/abst122/article/details/125508650#14eskibana_5673

1 依赖

elasticsearch对版本比较严谨,版本对不上就报一些奇奇怪怪的错误
官方版本对照:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.versions

1.1 maven

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.3.12.RELEASEversion>
        <relativePath/> 
    parent>
    
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-elasticsearchartifactId>
        dependency>

        <dependency>
            <groupId>org.elasticsearch.clientgroupId>
            <artifactId>elasticsearch-rest-high-level-clientartifactId>
        dependency>

1.2 gradle

    implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
    implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client'

2 配置类

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.demo.security.config")
public class ElasticRestClientConfig extends AbstractElasticsearchConfiguration {

    @Value("${spring.elasticsearch.rest.uris}")
    private String url;
    @Value("${spring.elasticsearch.rest.username}")
    private String username;
    @Value("${spring.elasticsearch.rest.password}")
    private String password;

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        url = url.replace("http://", "");
        String[] urlArr = url.split(",");
        HttpHost[] httpPostArr = new HttpHost[urlArr.length];
        for (int i = 0; i < urlArr.length; i++) {
            HttpHost httpHost = new HttpHost(urlArr[i].split(":")[0].trim(),
                    Integer.parseInt(urlArr[i].split(":")[1].trim()), "http");
            httpPostArr[i] = httpHost;
        }
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        RestClientBuilder builder = RestClient.builder(httpPostArr)
                // 异步httpclient配置
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    // 账号密码登录
                    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    // httpclient连接数配置  未知情况暂时不设置
//                    httpClientBuilder.setMaxConnTotal(50);
//                    httpClientBuilder.setMaxConnPerRoute(20);
                    // httpclient保活策略
                    httpClientBuilder.setKeepAliveStrategy(((response, context) -> Duration.ofMinutes(5).toMillis()));
                    return httpClientBuilder;
                });
        return new RestHighLevelClient(builder);
    }
}

3 注解说明

3.1 @Document

@Document:在类级别应用,以指示该类是映射到数据库的候选对象。最重要的属性是:

  • indexName:用于存储此实体的索引的名称。它可以包含SpEL模板表达式,例如 “log-#{T(java.time.LocalDate).now().toString()}”
  • type:映射类型。如果未设置,则使用小写的类的简单名称。(从版本4.0开始不推荐使用)
  • shards:索引的分片数。
  • replicas:索引的副本数。
  • refreshIntervall:索引的刷新间隔。用于索引创建。默认值为“ 1s”。
  • indexStoreType:索引的索引存储类型。用于索引创建。默认值为“ fs”。
  • createIndex:标记是否在存储库引导中创建索引。默认值为true。请参见使用相应的映射自动创建索引
  • versionType:版本管理的配置。默认值为EXTERNAL。

3.2 @Id

@Id:在字段级别应用,以标记用于标识目的的字段。

3.3 @Transient

@Transient:默认情况下,存储或检索文档时,所有字段都映射到文档,此注释不包括该字段。

3.4 @PersistenceConstructor

@PersistenceConstructor:标记从数据库实例化对象时要使用的给定构造函数,甚至是受保护的程序包。构造函数参数按名称映射到检索到的Document中的键值。

3.5 @Field

@Field:在字段级别应用并定义字段的属性,大多数属性映射到各自的Elasticsearch映射定义(以下列表不完整,请查看注释Javadoc以获得完整参考):

  • name:字段名称,它将在Elasticsearch文档中表示,如果未设置,则使用Java字段名称。
  • type:字段类型,可以是Text(可分词), Keyword(不可分词), Long, Integer, Short, Byte, Double, Float, Half_Float,Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range,Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator,Flattened, Search_As_You_Type。请参阅Elasticsearch映射类型
  • format和日期类型的pattern定义。必须为日期类型定义format
  • store:标记是否将原始字段值存储在Elasticsearch中,默认值为false。
  • analyzer,searchAnalyzer,normalizer用于指定自定义分析和正规化。

text:

  • 会分词,然后进行索引
  • 支持模糊、精确查询
  • 不支持聚合
  • 分词器默认standard ,对于中文来说就是按字分词
  • 支持fields属性,可以在fields中添加keyword子类型,以实现精确检索

keyword:

  • 不进行分词,直接索引
  • 支持模糊、精确查询
  • 支持聚合
  • 支持按字数建立索引,以便节约索引空间
  • 看下text分词规律。

3.6 @GeoPointField

将字段标记为geo_point数据类型。

3.7 @MultiField

映射配置为@MultiField 的,会映射为多字段,并使用对应的分析器。
所谓多字段,是说同一个字段同时建立多种索引,比如 text 和 keyword。注意,不能用 FieldType.Auto

3.8 @Mapping

注意:这里可以不需要mapping文件,定义mapping文件后实体类上字段的注解会失效

//设置mapping
@Mapping(mappingPath = "elasticsearch_mapping.json")//设置mapping

3.9 @Setting

//设置setting
@Setting(settingPath = "elasticsearch/settings.json")

4 创建映射(实体类)

@Data
@AllArgsConstructor
@NoArgsConstructor
// @Document是SpringDataES框架标记实体类的注解
// indexName指定的是索引名称,运行时items索引不存在,SpringDataES会自动创建这个索引
@Document(indexName = Constants.ES_HOTEL_INDEX, indexStoreType = Constants.ES_COMMODITY_INDEX_TYPE)
public class Hotel implements Serializable {

    @Id// SpringData标记当前属性为ES主键的注解
    private Long id;

    /**
     * 酒店名称
     */
    @Field(type = FieldType.Text,
            analyzer = "ik_max_word",
            searchAnalyzer = "ik_max_word")// SpringData标记title属性的支持分词的类似和相关分词器
    private String title;

    /**
     * 分类
     */
    @Field(type = FieldType.Keyword)// Keyword是不需要分词的字符串类型
    private String category;

    /**
     * 品牌
     */
    @Field(type = FieldType.Keyword)
    private String brand;

    /**
     * 价格
     */
    @Field(type = FieldType.Double)
    private BigDecimal price;

    // 设置index=false 今后所有不会称为查询条件的列都照此配置
    // 不索引,不代表不保存数据,数据本身仍然是保存在ES的
    @Field(type = FieldType.Keyword,index = false)
    private String imgPath;

    /**
     * 定位
     */
    @GeoPointField
    private GeoPoint localhost;
}

5 构建queryBuilder条件查询

1. 等值查询

查询name=如家:

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("title", "如家"));

2. 范围查询

查询价格大于等于99.9,并且小于等于120:

BoolQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
                                .gte(99.9)// gt大于,不包含边界值
                                .lte(120);// lt小于,不包含边界值

3. 模糊查询

查询姓名中包含有如家

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.wildcardQuery("title", "*如家*"));

这个我试了没起作用,不知啥原因

4. 多字段(multifield)查询

实体类

    /**
     * 商品名称
     */
    @MultiField(mainField = @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word"),
            otherFields = @InnerField(suffix = "inner", type = FieldType.Text, analyzer = "pinyin"))
    private String name;

模糊查询名称和分类

        if (StrUtil.isNotBlank(indistinct)) {
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(indistinct, "memberName", "customerCategory.name");
            queryBuilder.must(multiMatchQueryBuilder);
        }

5. 多条件查询

查询名称为:如家,并且价格在100.5-520之间:

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("title", "如家"))
                .must(QueryBuilders.rangeQuery("price")
                        .gte(100.5)
                        .lte(520));

6. 集合查询

查询城市在北京、上海、杭州,并且价格在100.5-520之间,查询名称为如家

List<String> list = Arrays.asList("北京", "上海", "杭州");
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("title", "如家"))
                .must(QueryBuilders.termsQuery("cityName", list))
                .must(QueryBuilders.rangeQuery("price")
                        .gte(100.5)
                        .lte(520));

7. should查询

查询姓名包含如家或者是城市是北京,should相当于或者or

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.wildcardQuery("title", "*如家*"))
                .should(QueryBuilders.termQuery("cityName", "北京"));

8. should和must配合查询

查询品牌为如家,名称包含如家或地址为北京的记录,**minimumShouldMatch(1)**表示最少要匹配到一个should条件

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("brand", "如家"))
                .should(QueryBuilders.wildcardQuery("title", "*如家*"))
                .should(QueryBuilders.termQuery("cityName", "北京"))
                .minimumShouldMatch(1);

9. 是否存在查询

查询name有值,tag不存在值

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.existsQuery("name"))
                .mustNot(QueryBuilders.existsQuery("aaa"));

10. 分页查询

类似jpa:https://blog.csdn.net/abst122/article/details/126423932#Spring_Data_Jpa_1514

        Pageable pageable = PageRequest.of(request.getCurrent()-1, request.getPageSize());
        Page<Hotel> page = searchHotelRepository.findAll(pageable);

11 地理位置查询

地理位置查询https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/

@Override
    public Page<Hotel> findPage(double latitude, double longitude, String distance, Pageable pageable) {
        // 实现了SearchQuery接口,用于组装QueryBuilder和SortBuilder以及Pageable等
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withPageable(pageable);

        // 间接实现了QueryBuilder接口
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 以某点为中心,搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("localhost");
        distanceQueryBuilder.point(latitude, longitude);
        // 定义查询单位:公里
        distanceQueryBuilder.distance(distance, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        // 按距离升序
        GeoDistanceSortBuilder distanceSortBuilder =
                new GeoDistanceSortBuilder("localhost", latitude, longitude);
        distanceSortBuilder.unit(DistanceUnit.KILOMETERS);
        distanceSortBuilder.order(SortOrder.ASC);
        nativeSearchQueryBuilder.withSort(distanceSortBuilder);

        return searchHotelRepository.search(nativeSearchQueryBuilder.build());
    }

    @Override
    public PageData<HotelResponse> findLocalHostPage(HotelRequest request) {
        Pageable pageable = PageRequest.of(request.getCurrent()-1, request.getPageSize());
        Page<Hotel> page = findPage(request.getLatitude(), request.getLongitude(), request.getDistance(), pageable);

        return new PageData<>(page.getTotalElements(),page.getContent().stream().map(s -> {
            HotelResponse response = new HotelResponse();
            BeanUtil.copyProperties(s, response);
            double lon = s.getLocalhost().getLon();
            double lat = s.getLocalhost().getLat();
            response.setLatitude(lat);
            response.setLongitude(lon);
            // 计算两点之间的距离
            double distance = GeoDistance.ARC.calculate(request.getLatitude(), request.getLongitude(), lat, lon, DistanceUnit.KILOMETERS);// GeoDistance.ARC和GeoDistance.PLANE,前者比后者计算起来要慢,但精确度要比后者高
            response.setDistance(distance);
            return response;
        }).collect(Collectors.toList()));
    }

6 通过RestHighLevelClient查询

使用RestHighLevelClient基本流程:

  1. 创建 SearchRequest搜索请求
    • 创建 SearchRequest 搜索请求,如果不带参数,表示查询所有索引
  2. 创建 SearchSourceBuilder条件构造
    • 创建 SearchSourceBuilder条件构造,构建搜索的条件。
    • 添加大部分查询参数到 SearchSourceBuilder,还可以接收 QueryBuilders构建的查询参数。
  3. 将 SearchSourceBuilder 添加到 SearchRequest中
  4. 执行查询
  5. 解析查询结果

模糊查询

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		//Fuzzy 查找
		FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", "张").fuzziness(Fuzziness.ONE);
 
		searchSourceBuilder.query(fuzzyQueryBuilder);

高亮查询

 public void testHighlight() throws IOException {
 
        //1.创建 SearchRequest搜索请求,并指定要查询的索引
        SearchRequest searchRequest = new SearchRequest("db_idx4");
 
        //2.创建 SearchSourceBuilder条件构造。
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //Term 查找
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("address", "王者");
        searchSourceBuilder.query(termQueryBuilder);
        //自定义高亮 查找
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("");
        highlightBuilder.postTags("");
        highlightBuilder.field("address"); 
        highlightBuilder.requireFieldMatch(false); //多字段时,需要设置为false
        highlightBuilder.field("desc");
        searchSourceBuilder.highlighter(highlightBuilder);
 
        //3.将 SearchSourceBuilder 添加到 SearchRequest中
        searchRequest.source(searchSourceBuilder);
 
        //4.执行查询
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
 
        //5.解析查询结果
        System.out.println("花费的时长:" + searchResponse.getTook());
 
        SearchHits hits = searchResponse.getHits();
        System.out.println("符合条件的总文档数量:" + hits.getTotalHits().value);
        hits.forEach(p -> {
 
            System.out.println("文档原生信息:" + p.getSourceAsString());
            System.out.println("高亮信息:" + p.getHighlightFields());
        });
    }

地理位置查询

  • 方式一:
       SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        SearchRequest request = new SearchRequest("hotel");
        queryBuilder.must(QueryBuilders.geoDistanceQuery("location")
                // 在3km之内
                .distance("3", DistanceUnit.KILOMETERS)
                // 以那个点为中心
                .point(31.256224D, 121.462311D)
                .geoDistance(GeoDistance.ARC)
                // 一个查询的名字,可选
                .queryName("optional_name"));
        sourceBuilder.query(queryBuilder);
        request.source(sourceBuilder);
        // 排序
        searchSourceBuilder.sort(
                // 不同的类型使用不同的SortBuilder
                new GeoDistanceSortBuilder("location", 31.256224D, 121.462311D)
                        .order(SortOrder.DESC)
                        .unit(DistanceUnit.METERS)
                        .geoDistance(GeoDistance.ARC));
        
  • 方式二:
    public PageResult search(RequestParams params) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            // 2.准备DSL
            // 2.1.query
            buildBasicQuery(params, request);

            // 2.2.分页
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page - 1) * size).size(size);

            // 2.3.排序
            String location = params.getLocation();
            if (location != null && !location.equals("")) {
                request.source().sort(SortBuilders
                        .geoDistanceSort("location", new GeoPoint(location))
                        .order(SortOrder.ASC)
                        .unit(DistanceUnit.KILOMETERS)
                );
            }

            // 3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析响应
            return handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
	private void buildBasicQuery(RequestParams params, SearchRequest request) {
        // 1.构建BooleanQuery
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 关键字搜索
        String key = params.getKey();
        if (key == null || "".equals(key)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }
        // 城市条件
        if (params.getCity() != null && !params.getCity().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
        }
        // 品牌条件
        if (params.getBrand() != null && !params.getBrand().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        // 星级条件
        if (params.getStarName() != null && !params.getStarName().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
        }
        // 价格
        if (params.getMinPrice() != null && params.getMaxPrice() != null) {
            boolQuery.filter(QueryBuilders
                    .rangeQuery("price")
                    .gte(params.getMinPrice())
                    .lte(params.getMaxPrice())
            );
        }

        // 2.算分控制
        FunctionScoreQueryBuilder functionScoreQuery =
                QueryBuilders.functionScoreQuery(
                        // 原始查询,相关性算分的查询
                        boolQuery,
                        // function score的数组
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                // 其中的一个function score 元素
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        // 过滤条件
                                        QueryBuilders.termQuery("isAD", true),
                                        // 算分函数
                                        ScoreFunctionBuilders.weightFactorFunction(10)
                                )
                        });
        request.source().query(functionScoreQuery);
    }

你可能感兴趣的:(java,elasticsearch,搜索引擎)