集成Elasticsearch实现简单搜索
- 目录
- 前言
- es版本说明
- 整合es
- 线程池管理
- api说明
- 码上有戏
- 测试
- 源码地址
- 参考文章
目录
前言
Elasticsearch是一个基于Lucene的服务器。它提供了在分布式环境下多用户能力的全文搜索引擎,并且它是基于Restful-web接口进行操作。而它的社区地址为中文社区,相应官网地址为客户端,而本次使用的服务器版本为7.6.0
es版本说明
es的版本迭代是非常快的,而其中大的变化如下所示
5.x 一个index->多个type
6.x 一个index->一个type
7.x 移除type,直接操作index
笔者以前工作中使用的是6.x版本,主要基于TransportClient基于连接,采用门面模式去集成es和封装,如下
而此次将集成7.x版本,并采用同样的策略,集成es和使用门面模式,是调用者不需要关心实现细节
整合es
最终实现的功能为创建索引,同步与异步批量操作,删除索引以及高亮查询和最基本的CRUD操作
线程池管理
I 增加核心依赖
<properties>
<es.version>7.8.0</es.version>
</properties>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${es.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${es.version}</version>
<scope>compile</scope>
</dependency>
II RestHighLevelClient连接类
private static RestHighLevelClient client;
/**
* 服务器地址
*/
private static String host = "your es ip";
/**
* 端口
*/
private static int port = 9200;
/**
* 用户名
*/
private static String user = "";
/**
* 密码
*/
private static String password = "";
public EsModel() {
try {
RestClientBuilder clientBuilder = RestClient
.builder(new HttpHost(host, port, "http"));
if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password)) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, password));
clientBuilder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
httpAsyncClientBuilder.setKeepAliveStrategy((resp, context) -> 30);
return httpAsyncClientBuilder;
});
} else {
clientBuilder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
httpAsyncClientBuilder.setKeepAliveStrategy((resp, context) -> 30);
return httpAsyncClientBuilder;
});
}
client = new RestHighLevelClient(clientBuilder);
} catch (Exception e) {
e.printStackTrace();
}
}
有了连接之后,就可以实现各种底层api,供上层直接调用
III 创建工厂EsFactory
public class EsFactory extends BasePooledObjectFactory<EsModel> {
@Override
public EsModel create() throws Exception {
return new EsModel();
}
@Override
public PooledObject<EsModel> wrap(EsModel arg0) {
return new DefaultPooledObject<EsModel>(arg0);
}
@Override
public void destroyObject(PooledObject<EsModel> p) throws Exception {
p.getObject().close();
super.destroyObject(p);
}
}
IV 门面工具类EsUtil
最后的效果通过线程池管理连接,该工具类对外提供封装方法,如下图
api说明
1 自定义注解
通过自定义注解从而实现配置实体类型,分词规则等
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ES {
/**
* @string text ,keyword
* @Number long, integer, short, byte, double, float, half_float, scaled_float
* @date date
* @date_nanos date_nanos
* @Range integer_range, float_range, long_range, double_range, date_range
* @binary binary
* @Nested nested
*/
String type() default "string";
/**
* 分词器选择 0. not_analyzed 1. ik_smart 2. ik_max_word
*/
int participle() default 0;
}
其它如EsMapping用来存放解析后的实体对象,EsMappingUtils用来解析实体上的注解信息
2 封装查询条件
将可能用到的参数封装到EsParamDto
@Data
public class EsParamDto implements Serializable {
/**
* 数据总条数
*/
private Long total;
/**
* 当前页
*/
private int page = 1;
/**
* 每页大小
*/
private int size = 1;
/**
* 是否分页
*/
private Boolean isPage = true;
/**
* 区间查询参数
*/
private Map<String, RangConditionDTO> rangConditionMap = new HashMap<>();
/**
* 时间区间查询参数
*/
private Map<String, RangConditionsToTimeModelDTO> rangConditionsToTimeModelMap = new HashMap<>();
/**
* 模糊相等查询条件,多个查询条件","进行切割
*/
private Map<String, Object> likeSearchCondition = new HashMap<>();
/**
* or条件查询
*/
private Map<String, Object> orSearchCondition = new HashMap<>();
/**
* or条件查询
*/
private Map<String, Object> orLikeSearchCondition = new HashMap<>();
/**
* or条件查询集合类操作
*/
private List<Map<String, Object>> orSearchConditionList = new ArrayList<>();
/**
* 相等查询条件,多个查询条件","进行切割
*/
private Map<String, Object> equalsSearchCondition = new HashMap<>();
/**
* in 查询
*/
private Map<String, List> inSearchCondition = new HashMap<>();
/**
* 模糊不相等的条件,多个查询条件","进行切割
*/
private Map<String, Object> noLikeSearchConditioin = new HashMap<>();
/**
* 不相等的条件,多个查询条件","进行切割
*/
private Map<String, Object> noEqualsSearchConditioin = new HashMap<>();
/**
* 为空过滤
*/
private List<String> isNullConditioin = new ArrayList<>();
/**
* 不为空过滤
*/
private List<String> isNotNullConditioin = new ArrayList<>();
/**
* 排序字段,关键字asc,desc
*/
private Map<String, String> sortFileds = new LinkedHashMap<>();
/**
* 排序字段集合,方便对排序顺序的控制 关键字asc,desc
*/
private List<Map<String, String>> sortFiledsList = new ArrayList<>();
/**
* 高亮字段
*/
private List<String> hightFieldList = new ArrayList<>();
/**
* 去重字段
*/
private String collapseField;
/**
* 指定查询结果包含的字段
*/
private String[] fetchSourceIncludes;
/**
* 指定查询结果不包含的字段
*/
private String[] fetchSourceExcludes;
/**
* 分词字段
*/
private Map<String, Object> analyzersField = new HashMap<>();
public String getSortFileds(String key) {
return sortFileds.get(key);
}
public RangConditionDTO getRangConditionMap(String key) {
return rangConditionMap.get(key);
}
public RangConditionsToTimeModelDTO getRangConditionsToTimeModelMap(String key) {
return rangConditionsToTimeModelMap.get(key);
}
}
其中RangConditionDTO用来区间查询,而RangConditionsToTimeModelDTO用来时间区间查询
3 封装返回数据
@Data
public class PageResult<T> {
/**
* 数据总条数
*/
private Long total;
/**
* 当前页
*/
private int page;
/**
* 每页大小
*/
private int size;
/**
* 数据
*/
private List<T> rows;
}
而底层api实现细节可参考EsModel的方法实现细节
码上有戏
测试
新建EsTestModel作为mapping的类
@Data
public class EsTestModel implements Serializable {
private String id;
private String userName;
private String password;
@ES(type = "integer")
private int age;
}
可通过注解@ES去建立映射关系
1 测试批量新增
浏览器输入http://localhost:8080/init
@RequestMapping("/init")
public boolean init() {
String index = "test";
List<EsTestModel> list = new ArrayList<>();
EsTestModel po = null;
for (int i = 0; i < 10; i++) {
po = new EsTestModel();
po.setId(i + "");
po.setUserName("admin" + i);
po.setAge(i);
list.add(po);
}
String paramListJson = JSON.toJSONString(list);
boolean b = EsUtil.addBatch(index, "id", paramListJson, EsTestModel.class);
return b;
}
执行上述代码,打开kabana里的dev模式,进行数据查看
输入GET test/_search?pretty
结果
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "0",
"_score" : 1.0,
"_source" : {
"id" : "0",
"userName" : "admin0",
"age" : 0
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"id" : "1",
"userName" : "admin1",
"age" : 1
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"id" : "2",
"userName" : "admin2",
"age" : 2
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"id" : "3",
"userName" : "admin3",
"age" : 3
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"id" : "4",
"userName" : "admin4",
"age" : 4
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"id" : "5",
"userName" : "admin5",
"age" : 5
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "6",
"_score" : 1.0,
"_source" : {
"id" : "6",
"userName" : "admin6",
"age" : 6
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "7",
"_score" : 1.0,
"_source" : {
"id" : "7",
"userName" : "admin7",
"age" : 7
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "8",
"_score" : 1.0,
"_source" : {
"id" : "8",
"userName" : "admin8",
"age" : 8
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "9",
"_score" : 1.0,
"_source" : {
"id" : "9",
"userName" : "admin9",
"age" : 9
}
}
]
}
}
2 测试更新操作
浏览器输入http://localhost:8080/update
@RequestMapping("/update")
public boolean update() {
String index = "test";
String id = "0";
EsTestModel po = new EsTestModel();
po.setAge(100);
po.setUserName("slycmiaoxi");
String paramJson = JSON.toJSONString(po);
boolean b = EsUtil.update(index, id, paramJson);
return b;
}
客户端输入图示请求,结果如下所示
3 测试高亮显示
浏览器输入http://localhost:8080/query
@RequestMapping("/query")
public String query() {
EsParamDto dto = new EsParamDto();
dto.setIsPage(true);
dto.setTotal(3L);
Map<String, Object> equalsSearchCondition = new HashMap<>();
equalsSearchCondition.put("id", 1);
dto.setEqualsSearchCondition(equalsSearchCondition);
List<String> hightFieldList = new ArrayList<>();
hightFieldList.add("age");
dto.setHightFieldList(hightFieldList);
PageResult result = EsUtil.query(dto, "test");
return result.toString();
}
返回结果
PageResult(total=1, page=0, size=1, rows=[{item=1, id=1, userName=admin1, age=1}])