SpringBoot使⽤注解的⽅式构建Elasticsearch查询语句,实现多条件的复杂查询
背景&痛点
通过ES进⾏查询,如果需要新增查询条件,则每次都需要进⾏硬编码,然后实现对应的查询功能。这样不仅开发⼯作量⼤,⽽且如果有多个不同的索引对象需要进⾏同样的查询,则需要开发多次,代码复⽤性不⾼。
想要解决这个问题,那么就需要⼀种能够模块化、配置化的解决⽅案。
解决⽅案
思路⼀:配置参数
通过配置参数的⽅式来配置参数映射、查询⽅式等,代码读取配置⽂件,根据配置⽂件构建查询语句。
优点:可配置化,新增查询字段基本不需要改动代码,除⾮增加新的查询⽅式。
缺点:配置⽂件太多、太复杂,配置⽂件配置错误将会导致整个查询不可⽤。
思路⼆:注解⽅式
和⽅案⼀类似,通过注解的⽅式来配置参数映射等,然后读取注解,根据注解构建查询语句。
优点:可配置化,代码清晰、明确,可读性⾼。
缺点:每次新增查询字段都需要改动代码(在指定字段增加注解)
⽬前只有这两种可以说⼤同⼩异的解决思路,不过不喜欢配置⽂件太多,所以我就选择了第⼆种思路。
代码实现(Elasticsearch版本6.7.2)
⾸先需要创建⼀个查询⽅式的枚举类,来区分有哪些查询⽅式,⽬前只实现了⼀些常⽤的查询类型。
源码如下:
package com.ums;
/**
* @author 李锋镝
* @date Create at 19:17 2019/8/27
*/
public enum QueryTypeEnum {
/**
* 等于
*/
EQUAL,
/**
* 忽略⼤⼩写相等
*/
EQUAL_IGNORE_CASE,
/**
* 范围
*/
RANGE,
/**
* in
*/
IN,
IGNORE,
/**
* 搜索
*/
FULLTEXT,
/**
* 匹配和q搜索区分开
*/
MATCH,
/**
* 模糊查询
*/
FUZZY,
/**
* and
*/
AND,
/**
* 多个查询字段匹配上⼀个即符合条件
*/
SHOULD,
/**
* 前缀查询
*/
PREFIX,
;
}
然后开始⾃定义注解,通过注解来定义字段的查询⽅式、映射字段、嵌套查询的path以及其他的⼀些参数;通过@Repeatable注解来声明这是⼀个重复注解类。
源码如下:
package com.lifengdi.search.annotation;
import com.ums.QueryTypeEnum;
import java.lang.annotation.*;
/**
* 定义查询字段的查询⽅式
* @author 李锋镝
* @date Create at 19:07 2019/8/27
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@Repeatable(DefinitionQueryRepeatable.class)
public @interface DefinitionQuery {
/**
* 查询参数
*
* @return 查询字段
*/
String key() default "";
/**
* 查询类型 see{@link QueryTypeEnum}
*
* @return QueryTypeEnum
*/
QueryTypeEnum type() default QueryTypeEnum.EQUAL;
/**
* 范围查询 from后缀
*
* @return from后缀
*/
String fromSuffix() default "From";
/
**
* 范围查询 to后缀
*
* @return to后缀
*/
String toSuffix() default "To";
/**
* 多个字段分隔符
*
* @return 分隔符
*/
String separator() default ",";
/**
* 指定对象的哪个字段将应⽤于查询映射
* 例如:
* 同⼀个⽂档下有多个User对象,对象名分别为createdUser、updatedUser,该User对象的属性有name等字段,
* 如果要根据查询createdUser的name来进⾏查询,
* 则可以这样定义DefinitionQuery:queryField = cName, mapped = createdUser.name
*
* @return 映射的实体的字段路径
*/
String mapped() default "";
/**
* 嵌套查询的path
*
* @return path
*/
String nestedPath() default "";
}
同时定义@DefinitionQueryRepeatable注解,声明这是上边注解的容器注解类,源码如下:
package com.lifengdi.search.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 李锋镝
* @date Create at 19:11 2019/8/27
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface DefinitionQueryRepeatable {
DefinitionQuery[] value();
}
如何使⽤注解?
在索引⽂档中需要查询的字段、对象或者类上⾯使⽤即可。
源码如下:
package com.lifengdi.document;
import com.lifengdi.document.store.*;
import com.lifengdi.search.annotation.DefinitionQuery;
import com.ums.QueryTypeEnum;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.List;
/**
* 门店Document
*
* @author 李锋镝
* @date Create at 19:31 2019/8/22
*/
@Document(indexName = "store", type = "base")
@Data
@DefinitionQuery(key = "page", type = QueryTypeEnum.IGNORE)
@DefinitionQuery(key = "size", type = QueryTypeEnum.IGNORE)
@DefinitionQuery(key = "q", type = QueryTypeEnum.FULLTEXT)
public class StoreDocument {
@Id
@DefinitionQuery(type = QueryTypeEnum.IN)
@DefinitionQuery(key = "id", type = QueryTypeEnum.IN)
@Field(type = FieldType.Keyword)
private String id;
spring boot选择题/**
* 基础信息
*/
@Field(type = FieldType.Object)
private StoreBaseInfo baseInfo;
/**
* 标签
*/
@Field(type = FieldType.Nested)
@DefinitionQuery(key = "tagCode", mapped = "tags.key", type = QueryTypeEnum.IN)
@DefinitionQuery(key = "tagValue", mapped = "tags.value", type = QueryTypeEnum.AND) @DefinitionQuery(key = "_tagValue", mapped = "tags.value", type = QueryTypeEnum.IN) private List<StoreTags> tags;
}
package com.lifengdi.document.store;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.lifengdi.search.annotation.DefinitionQuery;
import com.ums.QueryTypeEnum;
import com.lifengdi.serializer.JodaDateTimeDeserializer;
import com.lifengdi.serializer.JodaDateTimeSerializer;
import lombok.Data;
import org.joda.time.DateTime;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* 门店基础信息
*
*/
@Data
public class StoreBaseInfo {
/**
* 门店id
*/
@Field(type = FieldType.Keyword)
private String storeId;
/**
* 门店名称
*/
@Field(type = FieldType.Text, analyzer = "ik_smart")
@DefinitionQuery(type = QueryTypeEnum.FUZZY)
@DefinitionQuery(key = "name", type = QueryTypeEnum.SHOULD) private String storeName;
/**
* 门店简称
*/
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String shortName;
/**
* 门店简介
*/
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String profile;
/**
* 门店属性
*/
@Field(type = FieldType.Integer)
private Integer property;
/**
* 门店类型
*/
@Field(type = FieldType.Integer)
private Integer type;
/**
* 详细地址
*/
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String address;
/**
* 所在城市
*/
@Field(type = FieldType.Keyword)
@DefinitionQuery(type = QueryTypeEnum.IN)
private String cityCode;
/**
* 城市名称
*/
@Field(type = FieldType.Keyword)
private String cityName;
/**
* 所在省份
*/
@Field(type = FieldType.Keyword)
private String provinceCode;
/**
* 省份名称
*/
@Field(type = FieldType.Keyword)
private String provinceName;
/**
* 所在地区
*/
@Field(type = FieldType.Keyword)
private String regionCode;
/**
* 地区名称
*/
@Field(type = FieldType.Keyword)
private String regionName;
/**
* 所属市场id
*/
@Field(type = FieldType.Long)
@DefinitionQuery(type = QueryTypeEnum.IN)
private Integer marketId;
/**
* 所属市场key
*/
@Field(type = FieldType.Keyword)
@DefinitionQuery(type = QueryTypeEnum.IN)
private String marketKey;
/**
* 所属市场名称
*/
@Field(type = FieldType.Keyword)
private String marketName;
/**
* 摊位号
*/
@Field(type = FieldType.Text)
private String marketStall;
/**
* 门店状态
*/
@Field(type = FieldType.Keyword)
@DefinitionQuery(key = "storeStatus", type = QueryTypeEnum.IN) @DefinitionQuery(key = "_storeStatus", type = QueryTypeEnum.IN) private String status;
/**
* 删除标⽰
*/
@Field(type = FieldType.Integer)
@DefinitionQuery(key = "deleted")
private Integer deleted;
/**
* 创建时间
*/
@Field(type = FieldType.Date)
@JsonDeserialize(using = JodaDateTimeDeserializer.class)
@JsonSerialize(using = JodaDateTimeSerializer.class)
@DefinitionQuery(type = QueryTypeEnum.RANGE)
public DateTime createdTime;
/**
* 创建⼈id
*/
@Field(type = FieldType.Keyword)
@DefinitionQuery
private String createdUserId;
/**
* 创建⼈名称
*/
@Field(type = FieldType.Keyword)
private String createdUserName;
/**
* 修改时间
*/
@Field(type = FieldType.Date)
@JsonDeserialize(using = JodaDateTimeDeserializer.class)
@JsonSerialize(using = JodaDateTimeSerializer.class)
private DateTime updatedTime;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论