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';
版本对应要求见如下表格: 传送门
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot |
|---|---|---|---|---|
| 2023.0 (Ullmann) | 5.1.x | 8.7.1 | 6.0.x | 3.1.x |
| 2022.0 (Turing) | 5.0.x | 8.5.3 | 6.0.x | 3.0.x |
| 2021.2 (Raj) | 4.4.x[1] | 7.17.3 | 5.3.x | 2.7.x |
| 2021.1 (Q) | 4.3.x[1] | 7.15.2 | 5.3.x | 2.6.x |
| 2021.0 (Pascal) | 4.2.x[1] | 7.12.0 | 5.3.x | 2.5.x |
| 2020.0 (Ockham) | 4.1.x[1] | 7.9.3 | 5.3.2 | 2.4.x |
| Neumann | 4.0.x[1] | 7.6.2 | 5.2.12 | 2.3.x |
| Moore | 3.2.x[1] | 6.8.12 | 5.2.12 | 2.2.x |
| Lovelace | 3.1.x[1] | 6.2.2 | 5.1.19 | 2.1.x |
| Kay | 3.0.x[1] | 5.5.0 | 5.0.13 | 2.0.x |
| Ingalls | 2.1.x[1] | 2.4.0 | 4.3.25 | 1.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
配置信息读取:
@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;
}
连接初始化:
@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;
}
}
文件配置 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.gamioo.core.elasticsearch.config.ElasticSearchConfig
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);
}
更复杂一点的操作
我们可以使用@Query注解进行查询,这样要求我们需要自己写ES的查询语句,需要会ES查询才可以,其实也很简单,不会写查就是了。 看看官方给的例子
public interface OperationLogRepository extends ElasticsearchRepository<OperationLog, String> {
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
Page<OperationLog> findByName(String name,Pageable pageable);
}
操作对象:
@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;
}
逻辑操作:
@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);
}
用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());
}
其中,QueryBuilders是ES中的查询条件构造器
| 类型 | 说明 |
|---|---|
| QueryBuilders.boolQuery | 子方法must可多条件联查 |
| QueryBuilders.termQuery | 精确查询指定字段 |
| QueryBuilders.matchQuery | 按分词器进行模糊查询 |
| QueryBuilders.rangeQuery | 按指定字段进行区间范围查询 |
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>
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));
调用了多个withQuery,通过底层的代码可以看出会覆盖前面withQuery,只保留一个withQuery,因为源码实现这样的,
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
return this;
}
所以得改成如下:
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);

以上就是springboot集成es后的一个简单使用,spring封装过后的spring-boot-starter-data-elasticsearch使用起来还是非常方便简单的。
JPA自带的这些方法肯定是不能满足我们的业务需求的,那么我们如何自定义方法呢?我们只要使用特定的单词对方法名进行定义,那么Spring就会对我们写的方法名进行解析, 生成对应的实例进行数据处理,有木有很简单?那么接下来就使用Spring官方文档中的实例进行演示,先来看下关键字的说明
附上JPA操作的表:
| 关键字 | 使用示例 | 等同于的ES查询 |
|---|---|---|
| And | findByNameAndPrice | {“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
| Or | findByNameOrPrice | {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
| Is | findByName | {“bool” : {“must” : {“field” : {“name” : “?”}}}} |
| Not | findByNameNot | {“bool” : {“must_not” : {“field” : {“name” : “?”}}}} |
| Between | findByPriceBetween | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
| LessThanEqual | findByPriceLessThan | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
| GreaterThanEqual | findByPriceGreaterThan | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}} |
| Before | findByPriceBefore | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
| After | findByPriceAfter | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}} |
| Like | findByNameLike | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true}}}}} |
| StartingWith | findByNameStartingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true}}}}} |
| EndingWith | findByNameEndingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true}}}}} |
| Contains/Containing | findByNameContaining | {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true}}}}} |
| In | findByNameIn(Collectionnames) | {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}} |
| NotIn | findByNameNotIn(Collectionnames) | {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}} |
| True | findByAvailableTrue | {“bool” : {“must” : {“field” : {“available” : true}}}} |
| False | findByAvailableFalse | {“bool” : {“must” : {“field” : {“available” : false}}}} |
| OrderBy | findByAvailableTrueOrderByNameDesc | {“sort” : [{ “name” : {“order” : “desc”} }],“bool” : {“must” : {“field” : {“available” : true}}}} |