环境:CentOS7、Elasticsearch6.4.0
*** 本次测试使用ES与SpringData集成 ***
一、创建maven项目,向pom文件中导入配置
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
二、实体类配置
@Data
@AllArgsConstructor
@NoArgsConstructor
//以上为lombok动态实现完全封装的注解
import org.springframework.data.elasticsearch.annotations.Document;
//Document注解,用来标识实体类对应的索引名(indexName ),类型(type),分区等
@Document(indexName = "tp",type = "poetry")
public class Poetry {
@Id
//标识主键
private String id;
//为文本类型字段指定(插入,查询)分词器,指定字段类型
@Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word",type = FieldType.Text)
private String name;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Double)
private double salary;
@DateTimeFormat(style = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
@Field(type = FieldType.Date)
//指定日期类型格式
private Date birthday;
}
实体类如上配置,可直接使用JavaAPI进行插入,无需手动创建索引,类型的字段属性同注解配置
三、使用ES的JavaAPI
1. 基础的repository接口:提供基本的增删改查和根据方法名的查询(ES提供实现)
import com.baizhi.entity.User;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
/**
* 基础操作的es repository接口(定义的有通用的增删改查方法)
* ElasticsearchRepository<实体,主键类型>
* 这里只要继承ElasticsearchRepository接口就可以使用es为我们提供的方法
* 一些其他的方法可以通过给抽象方法以指定格式起名来让es默认实现
* 本类中提供了一些简单的命名
* 注:聚合查询不能使用这种方式
* @author liudy
*/
public interface UserRepository extends ElasticsearchRepository<User,String> {
/**
* 根据年龄区间查询数据 并根据年龄降序排列
*/
public List<User> findByAgeBetweenOrderByAgeDesc(int start,int end);
/**
* 查询真实姓名已“王”开头的数据
*/
public List<User> findByRealnameStartingWith(String startStr);
/**
* 通过Query注解自定义查询表达式
*/
@Query("{\"bool\" : {\"must\" : {\"fuzzy\" : {\"name\" : \"?0\"}}}}")
public List<User> findByNameLike(String name);
}
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
@SpringBootApplication
//配置es接口所在的包
@EnableElasticsearchRepositories("com.dao")
public class TpApplication {
public static void main(String[] args) {
SpringApplication.run(TpApplication.class, args);
}
}
测试
import com.baizhi.entity.User;
import com.baizhi.es.dao.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* @author gaozhy
* @date 2018/12/29.9:30
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-es.xml")
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
/**
* 查所有
*/
@Test
public void testQueryAll(){
Iterable<User> users = userRepository.findAll();
for (User user : users) {
System.out.println(user);
}
}
/**
* 查询所有 并根据年龄倒序排列
*/
@Test
public void testQueryBySort(){
Iterable<User> users = userRepository.findAll(Sort.by(Sort.Direction.DESC, "age"));
for (User user : users) {
System.out.println(user);
}
}
/**
* 根据id查询
*/
@Test
public void testQueryById(){
Optional<User> user = userRepository.findById("1");
System.out.println(user.get());
}
/**
* 新增或者修改数据
*/
@Test
public void testAdd(){
User user = userRepository.save(new User("6", "wb", "王八", 26, 10000D, new Date(), "河南
省郑州市XXX区XX街XX路XX号"));
System.out.println(user);
}
//==================================================================
/**
* 接口中声明方法查询:
* 根据年龄区间查询数据 并根据年龄降序排列
*/
@Test
public void testQueryByRange(){
List<User> users = userRepository.findByAgeBetweenOrderByAgeDesc(20, 28);
users.forEach(user -> System.out.println(user));
}
/**
* 接口中声明方法查询:
* 查询真实姓名已“王”开头的数据
*
* 响应结果:
* User{id='6', name='wb', realname='王八', age=26, salary=10000.0, birthday=Sat Dec 29
14:38:39 CST 2018, address='河南省郑州市XXX区XXX街XXX路XXX号'}
User{id='3', name='ww', realname='王五', age=25, salary=4300.0, birthday=Tue Mar 15
08:00:00 CST 2016, address='北京市XXX区XXX村XXX街X商城XXX楼XXX室'}
*/
@Test
public void testQueryByPrefix(){
List<User> users = userRepository.findByRealnameStartingWith("王");
users.forEach(user -> System.out.println(user));
}
//==================================================================
/**
* 通过Query注解自定义查询表达式
*/
@Test
public void testQueryByNameLike(){
List<User> users = userRepository.findByNameLike("zs");
users.forEach(user -> System.out.println(user));
}
}
2. 自定义Repository接口:使用 elasticsearchTemplate 实现复杂查询
自定义 CustomUserRepository 接口
import com.baizhi.entity.User;
import java.util.List;
import java.util.Map;
/**
* @author gaozhy
* @date 2019/1/1.23:10
*/
public interface CustomUserRepository {
public List<User> findByPageable(int nowPage,int pageSize);
public List<User> findByFieldDesc(String field);
public List<User> findByRealNameLikeAndHighLight(String realName);
public List<User> findByNameWithTermFilter(String ...terms);
public List<User> findByAgeWithRangeFilter(int start,int end);
public Map findByNameStartingWithAndAggregations(String prefixName);
/**
* 嵌套查询:
*
* 先按年龄直方图(桶聚合)统计
* 然后再统计区间内员工的最高工资(度量聚合)
*/
public Map aggregationsWithHistogramAndMax();
/**
* 日期直方图(桶聚合)
*/
public Map aggregationsWithDateHistogram();
}
自定义 CustomUserRepositoryImpl 实现类
import com.baizhi.entity.User;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.index.query.QueryBuilders.*;
/**
* @author gaozhy
* @date 2019/1/1.23:11
*/
public class CustomUserRepositoryImpl implements CustomUserRepository {
@Autowired
private ElasticsearchTemplate template;
/**
* ====================================
* {
* "query": {
* "match_all": {}
* },
* "from":1, //从第几条开始 (从0开始)
* "size":1 //大小
* }
* ====================================
*
* @param nowPage
* @param pageSize
* @return
*/
@Override
public List<User> findByPageable(int nowPage, int pageSize) {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(new PageRequest((nowPage - 1) * pageSize, pageSize))
.build();
return template.queryForList(query, User.class);
}
/**
* @param field
* @return
*/
@Override
public List<User> findByFieldDesc(String field) {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withSort(SortBuilders.fieldSort(field).order(SortOrder.DESC))
.build();
return template.queryForList(query, User.class);
}
/**
* 高亮
*
* @param realName
* @return
*/
@Override
public List<User> findByRealNameLikeAndHighLight(String realName) {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(matchQuery("realname", realName))
.withHighlightFields(new HighlightBuilder.Field("realname"))
.build();
AggregatedPage<User> users = template.queryForPage(query, User.class, new
SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T>
aClass, Pageable pageable) {
ArrayList<User> users = new ArrayList<>();
SearchHits searchHits = searchResponse.getHits();
for (SearchHit searchHit : searchHits) {
if (searchHits.getHits().length <= 0) {
return null;
}
User user = new User();
user.setId(searchHit.getId());
// searchHit.getSourceAsMap().forEach((k, v) -> System.out.println(k + " "
+ v));
user.setName(searchHit.getSourceAsMap().get("name").toString());
user.setAddress(searchHit.getSourceAsMap().get("address").toString());
user.setAge(Integer.parseInt(searchHit.getSourceAsMap().get("age").toString()));
user.setBirthday(new
Date(Long.parseLong(searchHit.getSourceAsMap().get("birthday").toString())));
user.setSalary(Double.parseDouble(searchHit.getSourceAsMap().get("salary").toString()));
String realname =
searchHit.getHighlightFields().get("realname").fragments()[0].toString();
user.setRealname(realname);
users.add(user);
}
return new AggregatedPageImpl<T>((List<T>) users);
}
});
return users.getContent();
}
@Override
public List<User> findByNameWithTermFilter(String... terms) {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(termsQuery("name",terms))
.build();
System.out.println(query.getFilter());
return template.queryForList(query,User.class);
}
@Override
public List<User> findByAgeWithRangeFilter(int start, int end) {
SearchQuery query = new NativeSearchQueryBuilder()
.withFilter(rangeQuery("age").gte(start).lte(end))
.build();
//System.out.println(query.getQuery());
//System.out.println(query.getFilter());
return template.queryForList(query,User.class);
}
@Override
public Map<String, Aggregation> findByNameStartingWithAndAggregations(String prefixName) {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(prefixQuery("name",prefixName))
// result为度量聚合结果的别名
.addAggregation(AggregationBuilders.avg("result").field("age"))
.build();
Aggregations aggregations = template.query(query, new ResultsExtractor<Aggregations>(){
@Override
public Aggregations extract(SearchResponse searchResponse) {
Aggregations aggregations = searchResponse.getAggregations();
return aggregations;
}
});
Map<String, Aggregation> map = aggregations.getAsMap();
return map;
}
@Override
public Map aggregationsWithHistogramAndMax() {
SearchQuery query = new NativeSearchQueryBuilder()
.addAggregation(AggregationBuilders.histogram("result").field("age").interval(5)
.subAggregation(AggregationBuilders.max("max_salary").field("salary")))
.build();
Aggregations aggregations = template.query(query, new ResultsExtractor<Aggregations>(){
@Override
public Aggregations extract(SearchResponse searchResponse) {
return searchResponse.getAggregations();
}
});
return aggregations.getAsMap();
}
@Override
public Map aggregationsWithDateHistogram() {
SearchQuery query = new NativeSearchQueryBuilder()
.addAggregation(AggregationBuilders.dateHistogram("result").field("birthday").format("yyyy-MM-
dd").dateHistogramInterval(DateHistogramInterval.YEAR))
.build();
Aggregations aggregations = template.query(query, new ResultsExtractor<Aggregations>(){
@Override
public Aggregations extract(SearchResponse searchResponse) {
return searchResponse.getAggregations();
}
});
return aggregations.getAsMap();
}
}
将实现类注入到工厂
import com.dao.CustomUserRepositoryImpl ;
import com.dao.PoetryDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ImportUserRepository {
@Bean
public CustomUserRepositoryImpl getCustomUserRepositoryImpl (){
return new CustomUserRepositoryImpl ();
}
}
此处省略测试
以上就是ES JavaAPI的一般使用
描述略少,请看代码