v2.0-beta1

进一步完善功能,测试用例,以及增加执行SQL功能
This commit is contained in:
xpc1024 2023-03-11 16:49:20 +08:00
parent 848ba0c7b5
commit 01a50faa8f
14 changed files with 207 additions and 42 deletions

View File

@ -216,4 +216,16 @@ public interface BaseEsConstants {
* keyword后缀 * keyword后缀
*/ */
String KEYWORD_SUFFIX = ".keyword"; String KEYWORD_SUFFIX = ".keyword";
/**
* 执行SQL x-pack 固定地址
*/
String SQL_ENDPOINT = "/_xpack/sql?format=json";
/**
* 执行DSL 查询固定地址
*/
String DSL_ENDPOINT = "/_search";
/**
* 查询
*/
String QUERY = "query";
} }

View File

@ -100,7 +100,7 @@ public enum EsQueryTypeEnum {
/** /**
* 与条件必须满足但不返回得分效率更高 * 与条件必须满足但不返回得分效率更高
*/ */
FILTER, AND_FILTER,
/** /**
* 或条件相当于MYSQL中的OR 和MP中的or嵌套用法一致 * 或条件相当于MYSQL中的OR 和MP中的or嵌套用法一致
*/ */
@ -110,7 +110,16 @@ public enum EsQueryTypeEnum {
*/ */
NESTED, NESTED,
/** /**
* 或条件仅影响紧跟其后的一个条件和MP中的拼接or用法一致 * 拼接OR,条件和MP中的拼接or用法一致
*/ */
OR; OR,
/**
* 拼接NOT,非条件 表示必须不满足
*/
NOT,
/**
* 拼接filter,
*/
FILTER;
} }

View File

@ -8,6 +8,28 @@ import java.io.Serializable;
* Copyright © 2021 xpc1024 All Rights Reserved * Copyright © 2021 xpc1024 All Rights Reserved
**/ **/
public interface Join<Children> extends Serializable { public interface Join<Children> extends Serializable {
/**
* 拼接filter
*
* @return 泛型
*/
default Children filter() {
return filter(true);
}
/**
* 拼接filter
*
* @param condition 条件
* @return 泛型
*/
Children filter(boolean condition);
/**
* 拼接or
*
* @return 泛型
*/
default Children or() { default Children or() {
return or(true); return or(true);
} }
@ -19,4 +41,21 @@ public interface Join<Children> extends Serializable {
* @return 泛型 * @return 泛型
*/ */
Children or(boolean condition); Children or(boolean condition);
/**
* 拼接not
*
* @return 泛型
*/
default Children not() {
return not(true);
}
/**
* 拼接not
*
* @param condition 条件
* @return 泛型
*/
Children not(boolean condition);
} }

View File

@ -39,14 +39,14 @@ public class GlobalConfig {
private boolean distributed = true; private boolean distributed = true;
/** /**
* Activate the current client's release index maximum number of retries * Activate the current client's release index maximum number of retries
* 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数若数据量过大,重建索引数据迁移时间超过60*(180/60)=180分钟时,可调大此参数值,此参数值决定最大重试次数,超出此次数后仍未成功,则终止重试并记录异常日志 * 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数,若数据量过大,重建索引数据迁移时间超过4320/60=72H,可调大此参数值,此参数值决定最大重试次数,超出此次数后仍未成功,则终止重试并记录异常日志
*/ */
private int activeReleaseIndexMaxRetry = 60; private int activeReleaseIndexMaxRetry = 4320;
/** /**
* Activate the current client's release index retry delay for a fixed time uint:second * Activate the current client's release index retry delay for a fixed time uint:second
* 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数 若数据量过大,重建索引数据迁移时间超过60*(180/60)=180分钟时,可调大此参数值 此参数值决定多久重试一次 单位: * 分布式环境下,平滑模式,当前客户端激活最新索引重试时间间隔 若您期望最终一致性的时效性更高,可调小此值,但会牺牲一些性能
*/ */
private int activeReleaseIndexFixedDelay = 180; private int activeReleaseIndexFixedDelay = 60;
/** /**
* es db config 数据库配置 * es db config 数据库配置
*/ */
@ -61,7 +61,7 @@ public class GlobalConfig {
/** /**
* index prefix eg:daily_, 索引前缀 可缺省 * index prefix eg:daily_, 索引前缀 可缺省
*/ */
private String tablePrefix = EMPTY_STR; private String indexPrefix = EMPTY_STR;
/** /**
* enable underscore to camel case default false 是否开启下划线自动转驼峰 默认关闭 * enable underscore to camel case default false 是否开启下划线自动转驼峰 默认关闭
*/ */

View File

@ -571,7 +571,19 @@ public abstract class AbstractChainWrapper<T, R, Children extends AbstractChainW
@Override @Override
public Children or(boolean condition) { public Children or(boolean condition) {
getWrapper().exists(condition); getWrapper().or(condition);
return typedThis;
}
@Override
public Children not(boolean condition) {
getWrapper().not(condition);
return typedThis;
}
@Override
public Children filter(boolean condition) {
getWrapper().filter(condition);
return typedThis; return typedThis;
} }

View File

@ -158,7 +158,17 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
@Override @Override
public Children filter(boolean condition, Consumer<Children> consumer) { public Children filter(boolean condition, Consumer<Children> consumer) {
return addNested(condition, FILTER, consumer); return addNested(condition, AND_FILTER, consumer);
}
@Override
public Children filter(boolean condition) {
return addParam(condition, FILTER, null, null, null);
}
@Override
public Children not(boolean condition) {
return addParam(condition, NOT, null, null, null);
} }
@Override @Override
@ -667,8 +677,8 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
param.setBoost(boost); param.setBoost(boost);
param.setLevel(level); param.setLevel(level);
// 入队之前需要先对MP中的拼接or()特殊处理 // 入队之前需要先对MP中的拼接类型特殊处理
processOr(param); processJoin(param);
paramQueue.add(param); paramQueue.add(param);
} }
@ -687,8 +697,15 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
param.setLevel(level); param.setLevel(level);
param.setPrevQueryType(queryTypeEnum); param.setPrevQueryType(queryTypeEnum);
// 入队之前需要先对MP中的拼接or()特殊处理 // 入队之前需要先对MP中的拼接类型特殊处理
processOr(param); processJoin(param);
// 这两种情况需要重置其prevType
if (MUST_NOT.equals(queryTypeEnum)) {
prevQueryType = MUST_NOT;
} else if (FILTER.equals(queryTypeEnum)) {
prevQueryType = FILTER;
}
paramQueue.add(param); paramQueue.add(param);
this.parentId = param.getId(); this.parentId = param.getId();
@ -708,16 +725,22 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
/** /**
* 特殊处理拼接or * 特殊处理拼接
* *
* @param param 参数 * @param param 参数
*/ */
private void processOr(Param param) { private void processJoin(Param param) {
if (!paramQueue.isEmpty()) { if (!paramQueue.isEmpty()) {
Param prev = paramQueue.peekLast(); Param prev = paramQueue.peekLast();
if (OR.equals(prev.getQueryTypeEnum())) { if (OR.equals(prev.getQueryTypeEnum())) {
// 上一节点是拼接or() 需要重置其prevQueryType类型,让其走should查询 // 上一节点是拼接or() 需要重置其prevQueryType类型,让其走should查询
param.setPrevQueryType(OR_SHOULD); param.setPrevQueryType(OR_SHOULD);
} else if (NOT.equals(prev.getQueryTypeEnum())) {
// 上一节点是拼接not() 需要重置其prevQueryType类型,让其走must_not查询
param.setPrevQueryType(MUST_NOT);
} else if (FILTER.equals(prev.getPrevQueryType())) {
// 上一节点是拼接filter() 需要重置其prevQueryType类型,让其走filter查询
param.setPrevQueryType(AND_FILTER);
} }
} }
} }

View File

@ -1,6 +1,5 @@
package cn.easyes.core.core; package cn.easyes.core.core;
import cn.easyes.common.enums.MethodEnum;
import cn.easyes.core.biz.EsPageInfo; import cn.easyes.core.biz.EsPageInfo;
import cn.easyes.core.biz.SAPageInfo; import cn.easyes.core.biz.SAPageInfo;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
@ -101,23 +100,29 @@ public interface BaseEsMapper<T> {
Boolean refresh(String... indexNames); Boolean refresh(String... indexNames);
/** /**
* 执行静态dsl语句 不传索引名,默认为当前mapper对应索引 * 执行SQL语句
* *
* @param dsl dsl语句 * @param sql 被执行的sql语句
* @param method 请求类型 比如查询类GET 更新PUT...
* @return 执行结果 jsonString * @return 执行结果 jsonString
*/ */
String executeDSL(String dsl, MethodEnum method); String executeSQL(String sql);
/**
* 执行静态dsl语句 不传索引名,默认为当前mapper对应索引
*
* @param dsl dsl语句
* @return 执行结果 jsonString
*/
String executeDSL(String dsl);
/** /**
* 执行静态dsl语句 可指定作用的索引 * 执行静态dsl语句 可指定作用的索引
* *
* @param dsl dsl语句 * @param dsl dsl语句
* @param method 请求类型 比如查询类GET 更新PUT...
* @param indexName 作用的索引名 * @param indexName 作用的索引名
* @return 执行结果 jsonString * @return 执行结果 jsonString
*/ */
String executeDSL(String dsl, MethodEnum method, String indexName); String executeDSL(String dsl, String indexName);
/** /**
* 标准查询 * 标准查询

View File

@ -167,18 +167,26 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
@Override @Override
@SneakyThrows @SneakyThrows
public String executeDSL(String dsl, MethodEnum method) { public String executeSQL(String sql) {
String indexName = EntityInfoHelper.getEntityInfo(entityClass).getIndexName(); Request request = new Request(MethodEnum.POST.name(), SQL_ENDPOINT);
Request request = new Request(method.toString(), indexName); JSONObject jsonObject = new JSONObject();
jsonObject.put(QUERY, sql);
request.setJsonEntity(jsonObject.toJSONString());
Response response = client.getLowLevelClient().performRequest(request); Response response = client.getLowLevelClient().performRequest(request);
return EntityUtils.toString(response.getEntity()); return EntityUtils.toString(response.getEntity());
} }
@Override @Override
@SneakyThrows @SneakyThrows
public String executeDSL(String dsl, MethodEnum method, String indexName) { public String executeDSL(String dsl) {
return executeDSL(dsl, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
}
@Override
@SneakyThrows
public String executeDSL(String dsl, String indexName) {
Assert.notNull(indexName, "indexName must not null"); Assert.notNull(indexName, "indexName must not null");
Request request = new Request(method.toString(), indexName); Request request = new Request(MethodEnum.GET.name(), indexName + DSL_ENDPOINT);
Response response = client.getLowLevelClient().performRequest(request); Response response = client.getLowLevelClient().performRequest(request);
return EntityUtils.toString(response.getEntity()); return EntityUtils.toString(response.getEntity());
} }

View File

@ -98,7 +98,9 @@ public class WrapperProcessor {
String realField; String realField;
switch (param.getQueryTypeEnum()) { switch (param.getQueryTypeEnum()) {
case OR: case OR:
// 渣男行为,*完就不认人了,因为拼接OR已处理过了 直接跳过 case NOT:
case FILTER:
// 渣男行为,*完就不认人了,因为拼接类型在AbstractWrapper中已处理过了 直接跳过
break; break;
case TERM: case TERM:
realField = getRealFieldAndSuffix(param.getColumn(), fieldTypeMap, mappingColumnMap); realField = getRealFieldAndSuffix(param.getColumn(), fieldTypeMap, mappingColumnMap);
@ -233,7 +235,7 @@ public class WrapperProcessor {
break; break;
// 下面五种嵌套类型 需要对孩子节点递归处理 // 下面五种嵌套类型 需要对孩子节点递归处理
case AND_MUST: case AND_MUST:
case FILTER: case AND_FILTER:
case MUST_NOT: case MUST_NOT:
case OR_SHOULD: case OR_SHOULD:
queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, null); queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, null);
@ -264,12 +266,12 @@ public class WrapperProcessor {
bool.must(queryBuilder); bool.must(queryBuilder);
} else if (OR_SHOULD.equals(parentType)) { } else if (OR_SHOULD.equals(parentType)) {
bool.should(queryBuilder); bool.should(queryBuilder);
} else if (FILTER.equals(parentType)) { } else if (AND_FILTER.equals(parentType)) {
bool.filter(queryBuilder); bool.filter(queryBuilder);
} else if (MUST_NOT.equals(parentType)) { } else if (MUST_NOT.equals(parentType)) {
bool.mustNot(queryBuilder); bool.mustNot(queryBuilder);
} else { } else {
// just ignore,almost never happen // by default
bool.must(queryBuilder); bool.must(queryBuilder);
} }
} }

View File

@ -581,7 +581,7 @@ public class EntityInfoHelper {
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig(); GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
IndexName table = clazz.getAnnotation(IndexName.class); IndexName table = clazz.getAnnotation(IndexName.class);
String tableName = clazz.getSimpleName().toLowerCase(Locale.ROOT); String tableName = clazz.getSimpleName().toLowerCase(Locale.ROOT);
String tablePrefix = dbConfig.getTablePrefix(); String tablePrefix = dbConfig.getIndexPrefix();
boolean tablePrefixEffect = true; boolean tablePrefixEffect = true;
String indexName; String indexName;

View File

@ -14,7 +14,7 @@ import java.util.List;
**/ **/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@IndexName(value = "easyes_document", shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true, childClass = Comment.class,routing = "666") @IndexName(value = "easyes_document", shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true, childClass = Comment.class,routing = "testRouting")
public class Document { public class Document {
/** /**
* es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等. * es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等.

View File

@ -13,6 +13,7 @@ import cn.easyes.test.TestEasyEsApplication;
import cn.easyes.test.entity.Document; import cn.easyes.test.entity.Document;
import cn.easyes.test.mapper.DocumentMapper; import cn.easyes.test.mapper.DocumentMapper;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
@ -29,6 +30,7 @@ import org.elasticsearch.search.aggregations.metrics.ParsedMin;
import org.elasticsearch.search.aggregations.metrics.ParsedSum; import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
@ -161,6 +163,26 @@ public class AllTest {
} }
// 3.查询 // 3.查询
@Test
@Order(6)
public void testSQL() {
// 注意 sql中的from后面跟的是要被查询的索引名,也可以是索引别名(效果一样) 由于索引名可能会变,所以此处我采用别名ee_default_alias进行查询
String sql = "select count(*) from ee_default_alias where star_num > 0";
String jsonResult = documentMapper.executeSQL(sql);
System.out.println(jsonResult);
Assertions.assertNotNull(jsonResult);
}
@Test
@Order(6)
public void testDSL() {
String dsl = "{\"size\":10000,\"query\":{\"bool\":{\"must\":[{\"term\":{\"title.keyword\":{\"value\":\"测试文档2\",\"boost\":1.0}}}],\"adjust_pure_negative\":true,\"boost\":1.0}}\"track_total_hits\":2147483647}";
String jsonResult = documentMapper.executeDSL(dsl);
System.out.println(jsonResult);
Assertions.assertNotNull(jsonResult);
}
@Test @Test
@Order(6) @Order(6)
public void testSelectOne() { public void testSelectOne() {
@ -491,6 +513,26 @@ public class AllTest {
Assertions.assertEquals(2, documents.size()); Assertions.assertEquals(2, documents.size());
} }
@Test
@Order(6)
public void testConditionFilter() {
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getStarNum, 10)
.filter(i -> i.eq(Document::getTitle, "测试文档10"));
List<Document> documents = documentMapper.selectList(wrapper);
Assertions.assertEquals(1, documents.size());
}
@Test
@Order(6)
public void testConditionMustNot() {
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.in(Document::getStarNum, 10,11,12,13)
.mustNot(i->i.eq(Document::getTitle,"测试文档10").eq(Document::getTitle,"测试文档11"));
List<Document> documents = documentMapper.selectList(wrapper);
Assertions.assertEquals(2, documents.size());
}
@Test @Test
@Order(6) @Order(6)
@ -760,12 +802,12 @@ public class AllTest {
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
GeoPoint geoPoint = new GeoPoint(41.0, 116.0); GeoPoint geoPoint = new GeoPoint(41.0, 116.0);
wrapper.geoDistance(Document::getLocation, 168.8, DistanceUnit.KILOMETERS, geoPoint); wrapper.geoDistance(Document::getLocation, 168.8, DistanceUnit.KILOMETERS, geoPoint);
// GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort(FieldUtils.val(Document::getLocation), geoPoint) GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort(FieldUtils.val(Document::getLocation), geoPoint)
// .unit(DistanceUnit.KILOMETERS) .unit(DistanceUnit.KILOMETERS)
// .geoDistance(GeoDistance.ARC) .geoDistance(GeoDistance.ARC)
// .order(SortOrder.DESC); .order(SortOrder.DESC);
//
// wrapper.sort(geoDistanceSortBuilder); wrapper.sort(geoDistanceSortBuilder);
List<Document> documents = documentMapper.selectList(wrapper); List<Document> documents = documentMapper.selectList(wrapper);
Assertions.assertEquals(4, documents.size()); Assertions.assertEquals(4, documents.size());
} }
@ -832,7 +874,7 @@ public class AllTest {
@Test @Test
@Order(9) @Order(9)
public void testDSL() { public void testComplex() {
// SQL写法 // SQL写法
// where business_type = 1 and (state = 9 or (state = 8 and bidding_sign = 1)) or business_type = 2 and state in (2,3) // where business_type = 1 and (state = 9 or (state = 8 and bidding_sign = 1)) or business_type = 2 and state in (2,3)

View File

@ -14,6 +14,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -126,4 +128,10 @@ public class IndexTest {
boolean isOk = documentMapper.createIndex(wrapper); boolean isOk = documentMapper.createIndex(wrapper);
Assertions.assertTrue(isOk); Assertions.assertTrue(isOk);
} }
@Test
public void testActiveIndex(){
String indexName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
documentMapper.setCurrentActiveIndex(indexName);
}
} }

View File

@ -51,7 +51,12 @@ public class UpdateTest {
document2.setCustomField("乌拉巴拉中魔仙"); document2.setCustomField("乌拉巴拉中魔仙");
documentMapper.update(document2, wrapper); documentMapper.update(document2, wrapper);
// 关于case2 还有另一种省略实体的简单写法,这里不演示,后面章节有介绍,语法与MP一致 // 关于case2 另一种省略实体的简单写法,语法与MP一致
LambdaEsUpdateWrapper<Document> wrapper3 = new LambdaEsUpdateWrapper<>();
wrapper3.eq(Document::getTitle, title1);
wrapper3.set(Document::getContent,"推*技术过软")
.set(Document::getCustomField,"乌拉巴拉中魔仙");
documentMapper.update(null,wrapper);
} }
@Test @Test