mirror of
https://gitee.com/dromara/easy-es.git
synced 2025-12-06 17:18:57 +08:00
v2.0-beta1
进一步完善功能,测试用例,以及增加执行SQL功能
This commit is contained in:
parent
848ba0c7b5
commit
01a50faa8f
@ -216,4 +216,16 @@ public interface BaseEsConstants {
|
||||
* keyword后缀
|
||||
*/
|
||||
String KEYWORD_SUFFIX = ".keyword";
|
||||
/**
|
||||
* 执行SQL x-pack 固定地址
|
||||
*/
|
||||
String SQL_ENDPOINT = "/_xpack/sql?format=json";
|
||||
/**
|
||||
* 执行DSL 查询固定地址
|
||||
*/
|
||||
String DSL_ENDPOINT = "/_search";
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
String QUERY = "query";
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public enum EsQueryTypeEnum {
|
||||
/**
|
||||
* 与条件必须满足,但不返回得分,效率更高
|
||||
*/
|
||||
FILTER,
|
||||
AND_FILTER,
|
||||
/**
|
||||
* 或条件,相当于MYSQL中的OR 和MP中的or嵌套用法一致
|
||||
*/
|
||||
@ -110,7 +110,16 @@ public enum EsQueryTypeEnum {
|
||||
*/
|
||||
NESTED,
|
||||
/**
|
||||
* 或条件,仅影响紧跟其后的一个条件,和MP中的拼接or用法一致
|
||||
* 拼接OR,或条件,和MP中的拼接or用法一致
|
||||
*/
|
||||
OR;
|
||||
OR,
|
||||
/**
|
||||
* 拼接NOT,非条件 表示必须不满足
|
||||
*/
|
||||
NOT,
|
||||
/**
|
||||
* 拼接filter,
|
||||
*/
|
||||
FILTER;
|
||||
|
||||
}
|
||||
|
||||
@ -8,6 +8,28 @@ import java.io.Serializable;
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
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() {
|
||||
return or(true);
|
||||
}
|
||||
@ -19,4 +41,21 @@ public interface Join<Children> extends Serializable {
|
||||
* @return 泛型
|
||||
*/
|
||||
Children or(boolean condition);
|
||||
|
||||
/**
|
||||
* 拼接not
|
||||
*
|
||||
* @return 泛型
|
||||
*/
|
||||
default Children not() {
|
||||
return not(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接not
|
||||
*
|
||||
* @param condition 条件
|
||||
* @return 泛型
|
||||
*/
|
||||
Children not(boolean condition);
|
||||
}
|
||||
|
||||
@ -39,14 +39,14 @@ public class GlobalConfig {
|
||||
private boolean distributed = true;
|
||||
/**
|
||||
* 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
|
||||
* 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数 若数据量过大,重建索引数据迁移时间超过60*(180/60)=180分钟时,可调大此参数值 此参数值决定多久重试一次 单位:秒
|
||||
* 分布式环境下,平滑模式,当前客户端激活最新索引重试时间间隔 若您期望最终一致性的时效性更高,可调小此值,但会牺牲一些性能
|
||||
*/
|
||||
private int activeReleaseIndexFixedDelay = 180;
|
||||
private int activeReleaseIndexFixedDelay = 60;
|
||||
/**
|
||||
* es db config 数据库配置
|
||||
*/
|
||||
@ -61,7 +61,7 @@ public class GlobalConfig {
|
||||
/**
|
||||
* index prefix eg:daily_, 索引前缀 可缺省
|
||||
*/
|
||||
private String tablePrefix = EMPTY_STR;
|
||||
private String indexPrefix = EMPTY_STR;
|
||||
/**
|
||||
* enable underscore to camel case default false 是否开启下划线自动转驼峰 默认关闭
|
||||
*/
|
||||
|
||||
@ -571,7 +571,19 @@ public abstract class AbstractChainWrapper<T, R, Children extends AbstractChainW
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -158,7 +158,17 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -667,8 +677,8 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
|
||||
param.setBoost(boost);
|
||||
param.setLevel(level);
|
||||
|
||||
// 入队之前需要先对MP中的拼接or()特殊处理
|
||||
processOr(param);
|
||||
// 入队之前需要先对MP中的拼接类型特殊处理
|
||||
processJoin(param);
|
||||
paramQueue.add(param);
|
||||
}
|
||||
|
||||
@ -687,8 +697,15 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
|
||||
param.setLevel(level);
|
||||
param.setPrevQueryType(queryTypeEnum);
|
||||
|
||||
// 入队之前需要先对MP中的拼接or()特殊处理
|
||||
processOr(param);
|
||||
// 入队之前需要先对MP中的拼接类型特殊处理
|
||||
processJoin(param);
|
||||
|
||||
// 这两种情况需要重置其prevType
|
||||
if (MUST_NOT.equals(queryTypeEnum)) {
|
||||
prevQueryType = MUST_NOT;
|
||||
} else if (FILTER.equals(queryTypeEnum)) {
|
||||
prevQueryType = FILTER;
|
||||
}
|
||||
|
||||
paramQueue.add(param);
|
||||
this.parentId = param.getId();
|
||||
@ -708,16 +725,22 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
|
||||
|
||||
|
||||
/**
|
||||
* 特殊处理拼接or
|
||||
* 特殊处理拼接
|
||||
*
|
||||
* @param param 参数
|
||||
*/
|
||||
private void processOr(Param param) {
|
||||
private void processJoin(Param param) {
|
||||
if (!paramQueue.isEmpty()) {
|
||||
Param prev = paramQueue.peekLast();
|
||||
if (OR.equals(prev.getQueryTypeEnum())) {
|
||||
// 上一节点是拼接or() 需要重置其prevQueryType类型,让其走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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package cn.easyes.core.core;
|
||||
|
||||
import cn.easyes.common.enums.MethodEnum;
|
||||
import cn.easyes.core.biz.EsPageInfo;
|
||||
import cn.easyes.core.biz.SAPageInfo;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
@ -100,24 +99,30 @@ public interface BaseEsMapper<T> {
|
||||
*/
|
||||
Boolean refresh(String... indexNames);
|
||||
|
||||
/**
|
||||
* 执行SQL语句
|
||||
*
|
||||
* @param sql 被执行的sql语句
|
||||
* @return 执行结果 jsonString
|
||||
*/
|
||||
String executeSQL(String sql);
|
||||
|
||||
/**
|
||||
* 执行静态dsl语句 不传索引名,默认为当前mapper对应索引
|
||||
*
|
||||
* @param dsl dsl语句
|
||||
* @param method 请求类型 比如查询类GET 更新PUT...
|
||||
* @return 执行结果 jsonString
|
||||
*/
|
||||
String executeDSL(String dsl, MethodEnum method);
|
||||
String executeDSL(String dsl);
|
||||
|
||||
/**
|
||||
* 执行静态dsl语句 可指定作用的索引
|
||||
*
|
||||
* @param dsl dsl语句
|
||||
* @param method 请求类型 比如查询类GET 更新PUT...
|
||||
* @param indexName 作用的索引名
|
||||
* @return 执行结果 jsonString
|
||||
*/
|
||||
String executeDSL(String dsl, MethodEnum method, String indexName);
|
||||
String executeDSL(String dsl, String indexName);
|
||||
|
||||
/**
|
||||
* 标准查询
|
||||
|
||||
@ -167,18 +167,26 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String executeDSL(String dsl, MethodEnum method) {
|
||||
String indexName = EntityInfoHelper.getEntityInfo(entityClass).getIndexName();
|
||||
Request request = new Request(method.toString(), indexName);
|
||||
public String executeSQL(String sql) {
|
||||
Request request = new Request(MethodEnum.POST.name(), SQL_ENDPOINT);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put(QUERY, sql);
|
||||
request.setJsonEntity(jsonObject.toJSONString());
|
||||
Response response = client.getLowLevelClient().performRequest(request);
|
||||
return EntityUtils.toString(response.getEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
@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");
|
||||
Request request = new Request(method.toString(), indexName);
|
||||
Request request = new Request(MethodEnum.GET.name(), indexName + DSL_ENDPOINT);
|
||||
Response response = client.getLowLevelClient().performRequest(request);
|
||||
return EntityUtils.toString(response.getEntity());
|
||||
}
|
||||
|
||||
@ -98,7 +98,9 @@ public class WrapperProcessor {
|
||||
String realField;
|
||||
switch (param.getQueryTypeEnum()) {
|
||||
case OR:
|
||||
// 渣男行为,*完就不认人了,因为拼接OR已处理过了 直接跳过
|
||||
case NOT:
|
||||
case FILTER:
|
||||
// 渣男行为,*完就不认人了,因为拼接类型在AbstractWrapper中已处理过了 直接跳过
|
||||
break;
|
||||
case TERM:
|
||||
realField = getRealFieldAndSuffix(param.getColumn(), fieldTypeMap, mappingColumnMap);
|
||||
@ -233,7 +235,7 @@ public class WrapperProcessor {
|
||||
break;
|
||||
// 下面五种嵌套类型 需要对孩子节点递归处理
|
||||
case AND_MUST:
|
||||
case FILTER:
|
||||
case AND_FILTER:
|
||||
case MUST_NOT:
|
||||
case OR_SHOULD:
|
||||
queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, null);
|
||||
@ -264,12 +266,12 @@ public class WrapperProcessor {
|
||||
bool.must(queryBuilder);
|
||||
} else if (OR_SHOULD.equals(parentType)) {
|
||||
bool.should(queryBuilder);
|
||||
} else if (FILTER.equals(parentType)) {
|
||||
} else if (AND_FILTER.equals(parentType)) {
|
||||
bool.filter(queryBuilder);
|
||||
} else if (MUST_NOT.equals(parentType)) {
|
||||
bool.mustNot(queryBuilder);
|
||||
} else {
|
||||
// just ignore,almost never happen
|
||||
// by default
|
||||
bool.must(queryBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,7 +581,7 @@ public class EntityInfoHelper {
|
||||
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
|
||||
IndexName table = clazz.getAnnotation(IndexName.class);
|
||||
String tableName = clazz.getSimpleName().toLowerCase(Locale.ROOT);
|
||||
String tablePrefix = dbConfig.getTablePrefix();
|
||||
String tablePrefix = dbConfig.getIndexPrefix();
|
||||
|
||||
boolean tablePrefixEffect = true;
|
||||
String indexName;
|
||||
|
||||
@ -14,7 +14,7 @@ import java.util.List;
|
||||
**/
|
||||
@Data
|
||||
@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 {
|
||||
/**
|
||||
* es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等.
|
||||
|
||||
@ -13,6 +13,7 @@ import cn.easyes.test.TestEasyEsApplication;
|
||||
import cn.easyes.test.entity.Document;
|
||||
import cn.easyes.test.mapper.DocumentMapper;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.geo.GeoDistance;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
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.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.junit.jupiter.api.*;
|
||||
@ -161,6 +163,26 @@ public class AllTest {
|
||||
}
|
||||
|
||||
// 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
|
||||
@Order(6)
|
||||
public void testSelectOne() {
|
||||
@ -491,6 +513,26 @@ public class AllTest {
|
||||
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
|
||||
@Order(6)
|
||||
@ -760,12 +802,12 @@ public class AllTest {
|
||||
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
|
||||
GeoPoint geoPoint = new GeoPoint(41.0, 116.0);
|
||||
wrapper.geoDistance(Document::getLocation, 168.8, DistanceUnit.KILOMETERS, geoPoint);
|
||||
// GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort(FieldUtils.val(Document::getLocation), geoPoint)
|
||||
// .unit(DistanceUnit.KILOMETERS)
|
||||
// .geoDistance(GeoDistance.ARC)
|
||||
// .order(SortOrder.DESC);
|
||||
//
|
||||
// wrapper.sort(geoDistanceSortBuilder);
|
||||
GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort(FieldUtils.val(Document::getLocation), geoPoint)
|
||||
.unit(DistanceUnit.KILOMETERS)
|
||||
.geoDistance(GeoDistance.ARC)
|
||||
.order(SortOrder.DESC);
|
||||
|
||||
wrapper.sort(geoDistanceSortBuilder);
|
||||
List<Document> documents = documentMapper.selectList(wrapper);
|
||||
Assertions.assertEquals(4, documents.size());
|
||||
}
|
||||
@ -832,7 +874,7 @@ public class AllTest {
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
public void testDSL() {
|
||||
public void testComplex() {
|
||||
// SQL写法
|
||||
// where business_type = 1 and (state = 9 or (state = 8 and bidding_sign = 1)) or business_type = 2 and state in (2,3)
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -126,4 +128,10 @@ public class IndexTest {
|
||||
boolean isOk = documentMapper.createIndex(wrapper);
|
||||
Assertions.assertTrue(isOk);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveIndex(){
|
||||
String indexName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
documentMapper.setCurrentActiveIndex(indexName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,12 @@ public class UpdateTest {
|
||||
document2.setCustomField("乌拉巴拉中魔仙");
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user