1.简介
(1).DSL查询
为了便于统一设计,所有的查询语句最后都被封装成BoolQueryBuilder,查询条件最终会被封装成为一个有层级关系的对象。查询操作统一由@Search注解来标记,支持分页查询。
(2).基本条件定义
查询条件统一定义成一个POJO对象,对象的属性名即为mapping字段名称,如果属性名称和mapping字段名称不一致,通过@Field注解来映射。属性分为基本类型和对象类型,对象类型会再次递归定义查询条件。
(3).查询语句类型
语句类型通过属性注解来表示,如果属性不加注解,默认是@Must条件,语句支持嵌套,属性详细如下所示。
序号 | 注解 | 说明 |
---|---|---|
1 | @Must | 必须满足的条件 |
2 | @MustNot | 必须排除的条件 |
3 | @Should | 可选条件 |
4 | @Filter | 过滤条件 |
5 | @Ignore | 忽略的条件,不参与语句拼装 |
6 | @Exists | 字段是否存在 |
(4).@Query属性说明
序号 | 属性名 | 默认值 | 说明 |
---|---|---|---|
1 | queryType | QueryType.BOOL | 默认Bool查询,可选类型:BOOL、FUNCTION_SCORE、CONSTANT_SCORE |
2 | searchType | SearchType.QUERY_THEN_FETCH | 默认即可 |
(5).路由说明
入参实体类实现需RoutingProvider。
2.Bool查询
(1).condition开发
在项目目录“/src/main/java/com/example/es/condition”下新建EmployeeCondition查询条件类,EmployeeCondition类需要实现RoutingProvider接口,具体代码如下。
@Data
public class EmployeeCondition extends SampleEmployeeCondition implements RoutingProvider {
/**
* 单范围查询
*/
@Must
@Field("age")
private Range<Integer> ageRange;
@Override
public String getRouting() {
return null;
}
}
(2).mapper开发
在项目目录“/src/main/java/com/example/es”的EmployeeMapper类中新增分页查询员工信息接口,具体代码如下。
@EasyMapper(indices = "employee", clusterRouter = "sampleCluster")
public interface EmployeeMapper {
/**
* 分页查询员工
*
* @param pageable 分页信息
* @param employeeCondition 查询条件
* @return 员工分页
*/
@Search(queryType = QueryType.BOOL)
Page<EmployeeEntity> queryEmployeePage(Pageable pageable, EmployeeCondition employeeCondition);
}
(3).controller开发
在项目目录“/src/main/java/com/example/es”下的EmployeeController类中新增分页查询员工信息接口,具体代码如下。
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Resource
private EmployeeMapper employeeMapper;
@ResponseBody
@RequestMapping(value = "/queryEmployeePage", method = RequestMethod.GET)
public Page<EmployeeEntity> queryEmployeePage() {
EmployeeCondition employeeCondition = new EmployeeCondition();
//岗位
employeeCondition.setJob("Java engineer");
//年龄范围
employeeCondition.setAgeRange(Range.of(27, 32).closeLeft());
//分页
int page = 0; //页码,从0开始
int size = 5; //分页大小
Pageable pageAble = Pageable.of(page, size);
return employeeMapper.queryEmployeePage(pageAble, employeeCondition);
}
}
(4).测试
启动项目,然后在postman中请求“http://localhost:8080/employee/queryEmployeePage”,成功后返回对应的信息。
{
"total": 2,
"content": [
{
"id": "10000001",
"employeeId": "10000001",
"name": "James Harden",
"age": 31,
"birthday": "1991-01-01",
"job": "Java engineer",
"salary": 30000.0
},
{
"id": "10000002",
"employeeId": "10000002",
"name": "Stephen Curry",
"age": 27,
"birthday": "1995-08-06",
"job": "Java engineer",
"salary": 20000.0
}
],
"pageable": {
"page": 0,
"size": 5,
"offset": 0,
"from": 0
},
"totalPage": 1,
"empty": false,
"totalElement": 2
}
(5).DSL语句打印
在项目目录resources下新建ebatis.properties配置文件并添加如下配置。
ebatis.debugEnabled=true
ebatis.clusterRouter=localhost
在application.properties文件中添加如下配置。
logging.level.io.manbang.ebatis = debug
上述查询条件最终会被映射为如下DSL查询语句。
{
"from" : 0,
"size" : 2,
"query" : {
"bool" : {
"must" : [ {
"term" : {
"job" : {
"value" : "Java engineer",
"boost" : 1.0
}
}
}, {
"range" : {
"age" : {
"from" : 27,
"to" : 32,
"include_lower" : true,
"include_upper" : false,
"boost" : 1.0
}
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
"_source" : {
"includes" : [ "id", "employeeId", "name", "age", "birthday", "job", "salary" ],
"excludes" : [ ]
}
}
3.Constant Score查询
(1).文档准备
打开kibana Dev Tools,分别添加索引和文档记录。
PUT /employee
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
PUT /employee/_mapping
{
"properties": {
"employeeId": {
"type": "integer"
},
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd"
},
"job": {
"type": "keyword"
},
"salary": {
"type": "float"
},
"hobby": {
"type": "text"
}
}
}
POST /employee/_doc
{
"id":"10000001",
"employeeId":10000001,
"name": "James Harden",
"job": "Java engineer",
"age": 31,
"salary": 30000.00,
"birthday": "1991-01-01",
"hobby":["swimming","running","basketball","football"]
}
{
"id":"10000002",
"employeeId":10000002,
"name": "Stephen Curry",
"job": "Java engineer",
"age": 27,
"salary": 20000.00,
"birthday": "1995-08-06",
"hobby":["tennis","football"]
}
{
"id":"10000003",
"employeeId":10000003,
"name": "LeBron James",
"job": "Technical director",
"age": 35,
"salary": 50000.00,
"birthday": "1987-12-25",
"hobby":["boxing","climbing","football"]
}
{
"id":"10000004",
"employeeId":10000004,
"name": "Damian Lillard",
"job": "Vue engineer",
"age": 25,
"salary": 18000.00,
"birthday": "1996-10-01",
"hobby":["shooting","basketball","football"]
}
(2).entity开发
在项目目录“/src/main/java/com/example/es”的EmployeeEntity类中新增hobby字段,具体代码如下。
@Data
public class EmployeeEntity implements IdProvider{
@JsonProperty("hobby")
private List<String> hobby;
}
(3).condition开发
在项目目录“/src/main/java/com/example/es/condition”的EmployeeCondition类中新增hobbyList和hobbyArray字段,EmployeeCondition类需要实现ScoreFunctionProvider接口,具体代码如下。
@Data
public class EmployeeCondition extends SampleEmployeeCondition implements RoutingProvider, ScoreFunctionProvider {
/**
* 基本类型集合
*/
@Field("hobby")
private List<String> hobbyList;
/**
* 基本类型集合
*/
@Field("hobby")
private String[] hobbyArray;
@Override
public ScoreFunction getFunction() {
return null;
}
@Override
public ScoreFunctionMode getFunctionMode() {
return null;
}
}
(4).mapper开发
在项目目录“/src/main/java/com/example/es”的EmployeeMapper类中新增根据兴趣查询员工信息接口,具体代码如下。
@EasyMapper(indices = "employee", clusterRouter = "sampleCluster")
public interface EmployeeMapper {
/**
* 根据兴趣查询员工
*
* @param employeeCondition 查询条件
* @return 员工列表
*/
@Search(queryType = QueryType.FUNCTION_SCORE)
List<EmployeeEntity> queryEmployeeList(EmployeeCondition employeeCondition);
}
(5).controller开发
在项目目录“/src/main/java/com/example/es”下的EmployeeController类中新增根据兴趣查询员工信息接口,具体代码如下。
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Resource
private EmployeeMapper employeeMapper;
@ResponseBody
@RequestMapping(value = "/queryEmployeeList", method = RequestMethod.GET)
public List<EmployeeEntity> queryEmployeeList() {
EmployeeCondition employeeCondition = new EmployeeCondition();
employeeCondition.setHobbyList(Lists.newArrayList("basketball","football"));
return employeeMapper.queryEmployeeList(employeeCondition);
}
}
(6).测试
启动项目,然后在postman中请求“http://localhost:8080/employee/queryEmployeePage”,成功后返回对应的信息。
[
{
"id": "10000004",
"employeeId": "10000004",
"name": "Damian Lillard",
"age": 25,
"birthday": "1996-10-01",
"job": "Vue engineer",
"salary": 18000.0,
"hobby": [
"shooting",
"basketball",
"football"
]
},
{
"id": "10000001",
"employeeId": "10000001",
"name": "James Harden",
"age": 31,
"birthday": "1991-01-01",
"job": "Java engineer",
"salary": 30000.0,
"hobby": [
"swimming",
"running",
"basketball",
"football"
]
}
]
4.Function Score查询
(1).简介
函数打分查询,允许修改文档的相关度分值,通过提供一个或多个函数来计算查询出来的文档新分值。因此,Function Score查询条件必须要提供打分函数,ebatis要求,条件必须实现ScoreFunctionProvider接口。此接口,有两个接口方法,默认实现getFunction方法即可。
(2).文档准备
打开kibana Dev Tools,分别添加索引和文档记录。
PUT /employee
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
PUT /employee/_mapping
{
"properties": {
"employeeId": {
"type": "integer"
},
"hobby": {
"type": "text"
},
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd"
},
"job": {
"type": "keyword"
},
"salary": {
"type": "float"
},
"profile": {
"properties": {
"nickName": {
"type": "text"
},
"avatar": {
"type": "keyword"
},
"grade": {
"type": "keyword"
}
}
},
"relative": {
"properties": {
"employeeId": {
"type": "integer"
}
}
}
}
}
POST /employee/_doc
{
"id": "10000001",
"employeeId": 10000001,
"name": "James Harden",
"job": "Java engineer",
"age": 31,
"salary": 30000.00,
"birthday": "1991-01-01",
"hobby": [
"swimming",
"running",
"basketball",
"football"
],
"profile": {
"nickName": "squirrel",
"avatar": "https://www.avatar.com/10000001.png",
"grade": "diamonds"
},
"relative": [
{
"employeeId": "10000002"
},
{
"employeeId": "10000004"
}
]
}
{
"id": "10000002",
"employeeId": 10000002,
"name": "Stephen Curry",
"job": "Java engineer",
"age": 27,
"salary": 20000.00,
"birthday": "1995-08-06",
"hobby": [
"tennis",
"football"
],
"profile": {
"nickName": "cupid",
"avatar": "https://www.avatar.com/10000002.png",
"grade": "golden"
},
"relative": [
{
"employeeId": "10000001"
},
{
"employeeId": "10000003"
}
]
}
{
"id": "10000003",
"employeeId": 10000003,
"name": "LeBron James",
"job": "Technical director",
"age": 35,
"salary": 50000.00,
"birthday": "1987-12-25",
"hobby": [
"boxing",
"climbing",
"football"
],
"profile": {
"nickName": "mountain",
"avatar": "https://www.avatar.com/10000003.png",
"grade": "diamonds"
},
"relative": [
{
"employeeId": "10000002"
}
]
}
{
"id": "10000004",
"employeeId": 10000004,
"name": "Damian Lillard",
"job": "Vue engineer",
"age": 25,
"salary": 18000.00,
"birthday": "1996-10-01",
"hobby": [
"shooting",
"basketball",
"football"
],
"profile": {
"nickName": "moon",
"avatar": "https://www.avatar.com/10000004.png",
"grade": "silver"
},
"relative": [
{
"employeeId": "10000001"
}
]
}
(3).entity开发
在项目目录“/src/main/java/com/example/es/entity”下新建Profile和Relative类,具体代码如下。
@Data
public class Profile {
private String nickName;
private String avatar;
private String grade;
}
@Data
public class Relative {
private String employeeId;
}
在项目目录“/src/main/java/com/example/es”的EmployeeEntity类中新增profile和relative字段,具体代码如下。
@Data
public class EmployeeEntity implements IdProvider{
@JsonProperty("profile")
private Profile profile;
@JsonProperty("relative")
private List<Relative> relativeList;
}
(4).condition开发
在项目目录“/src/main/java/com/example/es/condition”的EmployeeCondition类中新增profileNickName、profileAvatar、profileGrade和relativeEmployeeId字段,getFunction和getFunctionMode方法填充实现逻辑,具体代码如下。
@Data
public class EmployeeCondition extends SampleEmployeeCondition implements RoutingProvider, ScoreFunctionProvider {
/**
* 嵌套条件
*/
@Must
@Field("profile.nickName")
private String profileNickName;
@Must
@Field("profile.avatar")
private String profileAvatar;
@Must
@Field("profile.grade")
private String profileGrade;
/**
* 非基本类型集合
*/
@Must
@Field("relative.employeeId")
private String relativeEmployeeId;
@Override
public ScoreFunction getFunction() {
return ScoreFunction.fieldValueFactor("age", 10, 10, FieldValueFactorFunction.Modifier.LN);
}
@Override
public ScoreFunctionMode getFunctionMode() {
return ScoreFunctionMode.of(FunctionScoreQuery.ScoreMode.MAX, CombineFunction.MAX, 1.0f, 1.0f);
}
}
(5).controller开发
在项目目录“/src/main/java/com/example/es”下的EmployeeController类中修改根据兴趣查询员工信息接口,具体代码如下。
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Resource
private EmployeeMapper employeeMapper;
@ResponseBody
@RequestMapping(value = "/queryEmployeeList", method = RequestMethod.GET)
public List<EmployeeEntity> queryEmployeeList() {
EmployeeCondition employeeCondition = new EmployeeCondition();
employeeCondition.setProfileGrade("diamonds");
employeeCondition.setRelativeEmployeeId("10000004");
return employeeMapper.queryEmployeeList(employeeCondition);
}
}
(6).测试
启动项目,然后在postman中请求“http://localhost:8080/employee/queryEmployeePage”,成功后返回对应的信息。
[
{
"id": "10000001",
"employeeId": "10000001",
"name": "James Harden",
"age": 31,
"birthday": "1991-01-01",
"job": "Java engineer",
"salary": 30000.0,
"hobby": [
"swimming",
"running",
"basketball",
"football"
],
"profile": {
"nickName": "squirrel",
"avatar": "https://www.avatar.com/10000001.png",
"grade": "diamonds"
},
"relative": [
{
"employeeId": "10000002"
},
{
"employeeId": "10000004"
}
]
}
]