• Elesticsearch使用总结


    写在前面

    ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于[云计算]中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    可以这样来对比elasticsearch和数据库
    索引(indices) ~~~~~~~~ 数据库(databases)
    类型(type) ~~~~~~~~ 数据表(table)
    文档(Document)~~~~~~~~ 行(row)
    字段(Field) ~~~~~~~~ 列(Columns )

    shards:分片数量,默认5
    replicas:副本数量,默认1

    引入库

    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-elasticsearch', version: '2.7.5'
    implementation group: 'org.springframework.data', name: 'spring-data-elasticsearch', version: '4.4.17';
    
    • 1
    • 2

    版本对应要求见如下表格: 传送门

    Spring Data Release TrainSpring Data ElasticsearchElasticsearchSpring FrameworkSpring Boot
    2023.0 (Ullmann)5.1.x8.7.16.0.x3.1.x
    2022.0 (Turing)5.0.x8.5.36.0.x3.0.x
    2021.2 (Raj)4.4.x[1]7.17.35.3.x2.7.x
    2021.1 (Q)4.3.x[1]7.15.25.3.x2.6.x
    2021.0 (Pascal)4.2.x[1]7.12.05.3.x2.5.x
    2020.0 (Ockham)4.1.x[1]7.9.35.3.22.4.x
    Neumann4.0.x[1]7.6.25.2.122.3.x
    Moore3.2.x[1]6.8.125.2.122.2.x
    Lovelace3.1.x[1]6.2.25.1.192.1.x
    Kay3.0.x[1]5.5.05.0.132.0.x
    Ingalls2.1.x[1]2.4.04.3.251.5.x

    配置连接

    spring:
      elasticsearch:
        rest:
          uris:
            - 10.10.80.162:9200
            - 10.10.80.163:9200
            - 10.10.80.164:9200
          username: root
          password: ********
          timeout: 60000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    配置信息读取:

    @RefreshScope
    @ConfigurationProperties(ESProperties.PREFIX)
    public class ESProperties {
    
        public static final String PREFIX = "spring.elasticsearch.rest";
    
        private Boolean enable = true;
        private String[] uris;
    
        private String userName;
    
        /**
         * Secret key是你账户的密码
         */
        private String password;
    
    	private String deviceId;
    	/**请求超时*/
    	private long timeout;
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    连接初始化:

    @AutoConfiguration
    @EnableConfigurationProperties(ESProperties.class)
    @ConditionalOnProperty(value = ESProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
    public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {
        private static final Logger logger = LogManager.getLogger(ElasticSearchConfig.class);
    
        @Resource
        private ESProperties esProperties;
    
        @Override
        @Bean(destroyMethod = "close")
        public RestHighLevelClient elasticsearchClient() {
            final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                    .connectedTo(esProperties.getUris())
                    .withBasicAuth(esProperties.getUserName(), esProperties.getPassword())
                    .withConnectTimeout(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS)
                    .withSocketTimeout(esProperties.getTimeout())
                    .build();
            RestHighLevelClient client = RestClients.create(clientConfiguration).rest();
            try {
                logger.info("connect to elasticsearch:{} ", client.getLowLevelClient().getNodes());
                MainResponse response = client.info(RequestOptions.DEFAULT);
                MainResponse.Version version = response.getVersion();
                logger.info("elasticsearch version:{},lucene version:{}", version.getNumber(), version.getLuceneVersion());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return client;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    文件配置 spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.gamioo.core.elasticsearch.config.ElasticSearchConfig
    
    • 1

    DAO层:

    public interface OperationLogRepository extends ElasticsearchRepository<OperationLog, String> {
    //时间匹配
    Page<OperationLog> findByAddTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);
    //字段后匹配
     Page<OperationLog> findByDeviceIdLike(String deviceId, Pageable pageable);
    }
    //字段前后匹配
     Page<OperationLog> findByDeviceIdContaining(String deviceId, Pageable pageable);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    更复杂一点的操作
    我们可以使用@Query注解进行查询,这样要求我们需要自己写ES的查询语句,需要会ES查询才可以,其实也很简单,不会写查就是了。 看看官方给的例子

    public interface OperationLogRepository extends ElasticsearchRepository<OperationLog, String> {
            @Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
            Page<OperationLog> findByName(String name,Pageable pageable);
    }
    
    • 1
    • 2
    • 3
    • 4

    操作对象:

    @ApiModel(value = "operation_log", description = "操作日志")
    @Document(indexName = "operation_log_*")
    public class OperationLog {
        @Id
        private String id;
        @ApiModelProperty("名称")
        @Field(name = "name")
        private String name;
        @ApiModelProperty("创建时间")
        @Field(name = "addTime", type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ssz")
        private Date addTime;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    逻辑操作:

        @Resource
        private OperationLogRepository repository;
       @Override
        public Page<OperationLog> findAll(Pageable pageable) {
            return repository.findAll(pageable);
        }
    
    
        @Override
        public Page<OperationLog> findByAddTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable) {
            return repository.findByAddTimeBetween(startTime, endTime, pageable);
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    用JPA的话,返回的是整条数据,就会抛出数据量过大的问题,但有时候你只需要部分极少的字段,那时我们就需要自定义EQL进行复杂查询:

        public Page<OperationLog> findByAndAddTimeBetweenAndDevUidIn(LocalDateTime startTime, LocalDateTime endTime, List<String> devUid, Pageable pageable) {
            /* 1 查询结果 */
            NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
            // 1.2 source过滤
            builder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "deviceId", "channel", "fileSize"}, new String[0]));
            // 1.3 搜索条件
            BoolQueryBuilder query = QueryBuilders.boolQuery().must(QueryBuilders.termsQuery("deviceId", devUid)).must(QueryBuilders.rangeQuery("created")
                    //   .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXZ")
                    //  .format("yyyy-MM-dd HH:mm:ss")
                    .timeZone("GMT+8")
                    .gte(startTime)
                    .lte(endTime)
                    .includeLower(true).includeUpper(true));
            //时间范围
            builder.withQuery(query);
            // builder.withQuery(QueryBuilders.matchQuery("title", "手机"));
            // 1.4 分页及排序条件
            builder.withPageable(pageable);
            SearchHits<OperationLog> searchHits = template.search(builder.build(), OperationLog.class);
            List<OperationLog> array = new ArrayList<>();
            for (SearchHit<OperationLog> e : searchHits) {
                array.add(e.getContent());
            }
            return new PageImpl<>(array, pageable, searchHits.getTotalHits());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    其中,QueryBuilders是ES中的查询条件构造器

    类型说明
    QueryBuilders.boolQuery子方法must可多条件联查
    QueryBuilders.termQuery精确查询指定字段
    QueryBuilders.matchQuery按分词器进行模糊查询
    QueryBuilders.rangeQuery按指定字段进行区间范围查询

    Q&A

    1.实际使用中一直报错: missing authentication credentials for REST request
    经过多方查证,最后发现报错原因是:配置ES时没添加用户名密码验证

    2.org.apache.http.ContentTooLongException: entity content is too long [450975428] for the configured buffer limit [104857600]

    3.如何打印ES日志

     #es日志
    <Logger name="org.springframework.data.elasticsearch.client.WIRE" level="trace" includeLocation="true">
    </Logger>
    
    • 1
    • 2
    • 3

    4.NativeSearchQueryBuilder多条件查询,会覆盖前面的条件

    // 1.3 搜索条件
    query.withQuery(QueryBuilders.termsQuery("devUid", devUid));
    //时间范围
    query.withQuery(QueryBuilders.rangeQuery("created")
                    .format("yyyy-MM-dd'T'HH:mm:ssz")
                    .timeZone("GMT+8")
                    .gte(startTime)
                    .lte(endTime)
                    .includeLower(true).includeUpper(true));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    调用了多个withQuery,通过底层的代码可以看出会覆盖前面withQuery,只保留一个withQuery,因为源码实现这样的,

    	public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
    		this.queryBuilder = queryBuilder;
    		return this;
    	}
    
    • 1
    • 2
    • 3
    • 4

    所以得改成如下:

         BoolQueryBuilder query = QueryBuilders.boolQuery().must(QueryBuilders.termsQuery("devUid", devUid)).must(QueryBuilders.rangeQuery("created")
                    .format("yyyy-MM-dd'T'HH:mm:ssz")
                    .timeZone("GMT+8")
                    .gte(startTime)
                    .lte(endTime)
                    .includeLower(true).includeUpper(true));
            //时间范围
            builder.withQuery(query);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. Jet Brains DataGrip 连接到 Elasticsearch 目前指定的版本或许可以连上,但自己定义的版本却连不上,有谁可以帮我?
      在这里插入图片描述
      驱动下载地址为:https://www.elastic.co/cn/downloads/jdbc-client

    总结

    以上就是springboot集成es后的一个简单使用,spring封装过后的spring-boot-starter-data-elasticsearch使用起来还是非常方便简单的。

    JPA自带的这些方法肯定是不能满足我们的业务需求的,那么我们如何自定义方法呢?我们只要使用特定的单词对方法名进行定义,那么Spring就会对我们写的方法名进行解析, 生成对应的实例进行数据处理,有木有很简单?那么接下来就使用Spring官方文档中的实例进行演示,先来看下关键字的说明
    附上JPA操作的表:

    关键字使用示例等同于的ES查询
    AndfindByNameAndPrice{“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
    OrfindByNameOrPrice{“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
    IsfindByName{“bool” : {“must” : {“field” : {“name” : “?”}}}}
    NotfindByNameNot{“bool” : {“must_not” : {“field” : {“name” : “?”}}}}
    BetweenfindByPriceBetween{“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
    LessThanEqualfindByPriceLessThan{“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
    GreaterThanEqualfindByPriceGreaterThan{“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
    BeforefindByPriceBefore{“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
    AfterfindByPriceAfter{“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
    LikefindByNameLike{“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true}}}}}
    StartingWithfindByNameStartingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true}}}}}
    EndingWithfindByNameEndingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true}}}}}
    Contains/ContainingfindByNameContaining{“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true}}}}}
    InfindByNameIn(Collectionnames){“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
    NotInfindByNameNotIn(Collectionnames){“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
    TruefindByAvailableTrue{“bool” : {“must” : {“field” : {“available” : true}}}}
    FalsefindByAvailableFalse{“bool” : {“must” : {“field” : {“available” : false}}}}
    OrderByfindByAvailableTrueOrderByNameDesc{“sort” : [{ “name” : {“order” : “desc”} }],“bool” : {“must” : {“field” : {“available” : true}}}}

    参考:
    ElasticSearch与SpringBoot的集成与JPA方法的使用

  • 相关阅读:
    Nodejs+vue高校机房设备管理系统jt07u
    一口气面了 6 家大厂,拿下 5 家 offer,分享下面试经验,想进大厂其实没有那么难
    如何在Linux下打包发布Qt程序?
    通过 SAP UI5 ODataModel API 在 JavaScript 代码里访问 OData 元数据试读版
    辅助驾驶功能开发-功能规范篇(23)-1-Mobileye NOP功能规范
    “蔚来杯“2022牛客暑期多校训练营9 I题: The Great Wall II
    力扣 454题 四数相加Ⅱ 记录
    如何配置Nacos的健康检查参数?
    MDK(Keil)的MAP文件都记录了啥
    docker镜像制作
  • 原文地址:https://blog.csdn.net/jiangguilong2000/article/details/133971817