mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-09-03 06:35:25 +08:00
fix(crud): 修复TermExpressionParser解析逻辑,支持更多条件运算符和空格处理
This commit is contained in:
@@ -9,6 +9,7 @@ import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -82,7 +83,7 @@ public class TermExpressionParser {
|
||||
@SneakyThrows
|
||||
public static List<Term> parse(String expression) {
|
||||
try {
|
||||
expression = URLDecoder.decode(expression, "utf-8");
|
||||
expression = URLDecoder.decode(expression, StandardCharsets.UTF_8);
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
@@ -91,15 +92,13 @@ public class TermExpressionParser {
|
||||
NestConditional<?> nest = null;
|
||||
|
||||
// 字符容器
|
||||
char[] buf = new char[128];
|
||||
// 记录词项的长度, Arrays.copyOf使用
|
||||
byte len = 0;
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
// 空格数量?
|
||||
byte spaceLen = 0;
|
||||
// 当前列
|
||||
char[] currentColumn = null;
|
||||
String currentColumn = null;
|
||||
// 当前列对应的值
|
||||
char[] currentValue = null;
|
||||
String currentValue = null;
|
||||
// 当前条件类型 eq btw in ...
|
||||
String currentTermType = null;
|
||||
// 当前链接类型 and / or
|
||||
@@ -124,24 +123,24 @@ public class TermExpressionParser {
|
||||
nest = (nest == null ?
|
||||
(currentType.equals("or") ? conditional.orNest() : conditional.nest()) :
|
||||
(currentType.equals("or") ? nest.orNest() : nest.nest()));
|
||||
len = 0;
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
} else if (c == ')') {
|
||||
if (nest == null) {
|
||||
continue;
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
currentValue = buf.toString();
|
||||
nest.accept(currentColumn, convertTermType(currentTermType), currentValue);
|
||||
currentColumn = null;
|
||||
currentTermType = null;
|
||||
}
|
||||
Object end = nest.end();
|
||||
nest = end instanceof NestConditional ? ((NestConditional) end) : null;
|
||||
len = 0;
|
||||
nest = end instanceof NestConditional ? ((NestConditional<?>) end) : null;
|
||||
buf.setLength(0);
|
||||
spaceLen++;
|
||||
continue;
|
||||
} else if (c == '=' || c == '>' || c == '<') {
|
||||
} else if (c == '=' || c == '>' || c == '<' || c == '!') {
|
||||
if (currentTermType != null) {
|
||||
currentTermType += String.valueOf(c);
|
||||
//spaceLen--;
|
||||
@@ -150,44 +149,44 @@ public class TermExpressionParser {
|
||||
}
|
||||
|
||||
if (currentColumn == null) {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
currentColumn = buf.toString();
|
||||
}
|
||||
spaceLen++;
|
||||
len = 0;
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
} else if (c == ' ') {
|
||||
if (len == 0) {
|
||||
if (buf.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (quotationMarks != 0) {
|
||||
// 如果当前字符是空格,并且前面迭代时碰到过单/双引号, 不处理并且添加到buf中
|
||||
buf[len++] = c;
|
||||
buf.append(c);
|
||||
continue;
|
||||
}
|
||||
spaceLen++;
|
||||
if (currentColumn == null && (spaceLen == 1 || spaceLen % 5 == 0)) {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
currentColumn = buf.toString();
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
if (null == currentTermType) {
|
||||
currentTermType = new String(Arrays.copyOf(buf, len));
|
||||
len = 0;
|
||||
currentTermType = buf.toString();
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
}
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
currentValue = buf.toString();
|
||||
if (nest != null) {
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
nest.accept(currentColumn, convertTermType(currentTermType), currentValue);
|
||||
} else {
|
||||
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
conditional.accept(currentColumn, convertTermType(currentTermType), currentValue);
|
||||
}
|
||||
currentColumn = null;
|
||||
currentTermType = null;
|
||||
len = 0;
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
} else if (len == 2 || len == 3) {
|
||||
String type = new String(Arrays.copyOf(buf, len));
|
||||
} else if (buf.length() == 2 || buf.length() == 3) {
|
||||
String type = buf.toString();
|
||||
if (type.equalsIgnoreCase("or")) {
|
||||
currentType = "or";
|
||||
if (nest != null) {
|
||||
@@ -195,7 +194,7 @@ public class TermExpressionParser {
|
||||
} else {
|
||||
conditional.or();
|
||||
}
|
||||
len = 0;
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
} else if (type.equalsIgnoreCase("and")) {
|
||||
currentType = "and";
|
||||
@@ -204,29 +203,29 @@ public class TermExpressionParser {
|
||||
} else {
|
||||
conditional.and();
|
||||
}
|
||||
len = 0;
|
||||
buf.setLength(0);
|
||||
continue;
|
||||
} else {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
currentColumn = buf.toString();
|
||||
buf.setLength(0);
|
||||
spaceLen++;
|
||||
}
|
||||
} else {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
currentColumn = buf.toString();
|
||||
buf.setLength(0);
|
||||
spaceLen++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[len++] = c;
|
||||
buf.append(c);
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
currentValue = buf.toString();
|
||||
if (nest != null) {
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
nest.accept(currentColumn, convertTermType(currentTermType), currentValue);
|
||||
} else {
|
||||
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
conditional.accept(currentColumn, convertTermType(currentTermType), currentValue);
|
||||
}
|
||||
}
|
||||
return conditional.getParam().getTerms();
|
||||
@@ -270,6 +269,8 @@ public class TermExpressionParser {
|
||||
return TermType.gte;
|
||||
case "<=":
|
||||
return TermType.lte;
|
||||
case "!=":
|
||||
return TermType.not;
|
||||
default:
|
||||
return termType;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ public class TermExpressionParserTest {
|
||||
public void testUrl(){
|
||||
List<Term> terms = TermExpressionParser.parse("type=email%20and%20provider=test");
|
||||
|
||||
assertEquals(terms.get(0).getTermType(), TermType.eq);
|
||||
assertEquals(terms.get(0).getColumn(), "type");
|
||||
assertEquals(terms.get(0).getValue(), "email");
|
||||
assertEquals(TermType.eq, terms.get(0).getTermType());
|
||||
assertEquals("type", terms.get(0).getColumn());
|
||||
assertEquals("email", terms.get(0).getValue());
|
||||
|
||||
assertEquals(terms.get(1).getTermType(), TermType.eq);
|
||||
assertEquals(terms.get(1).getColumn(), "provider");
|
||||
assertEquals(terms.get(1).getValue(), "test");
|
||||
assertEquals(TermType.eq, terms.get(1).getTermType());
|
||||
assertEquals("provider", terms.get(1).getColumn());
|
||||
assertEquals("test", terms.get(1).getValue());
|
||||
|
||||
}
|
||||
|
||||
@@ -31,16 +31,16 @@ public class TermExpressionParserTest {
|
||||
{
|
||||
List<Term> terms = TermExpressionParser.parse("name = 我");
|
||||
|
||||
assertEquals(terms.get(0).getTermType(), TermType.eq);
|
||||
assertEquals(terms.get(0).getValue(),"我");
|
||||
assertEquals(TermType.eq, terms.get(0).getTermType());
|
||||
assertEquals("我", terms.get(0).getValue());
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
List<Term> terms = TermExpressionParser.parse("name like %我%");
|
||||
|
||||
assertEquals(terms.get(0).getTermType(), TermType.like);
|
||||
assertEquals(terms.get(0).getValue(),"%我%");
|
||||
assertEquals(TermType.like, terms.get(0).getTermType());
|
||||
assertEquals("%我%", terms.get(0).getValue());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -115,4 +115,206 @@ public class TermExpressionParserTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLessThan() {
|
||||
List<Term> terms = TermExpressionParser.parse("age < 18");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.lt, terms.get(0).getTermType());
|
||||
assertEquals("age", terms.get(0).getColumn());
|
||||
assertEquals("18", terms.get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLessThanOrEqual() {
|
||||
List<Term> terms = TermExpressionParser.parse("price <= 100");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.lte, terms.get(0).getTermType());
|
||||
assertEquals("price", terms.get(0).getColumn());
|
||||
assertEquals("100", terms.get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEqual() {
|
||||
List<Term> terms = TermExpressionParser.parse("status != active");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.not, terms.get(0).getTermType());
|
||||
assertEquals("status", terms.get(0).getColumn());
|
||||
assertEquals("active", terms.get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInOperator() {
|
||||
List<Term> terms = TermExpressionParser.parse("status in active,inactive,pending");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.in, terms.get(0).getTermType());
|
||||
assertEquals("status", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotInOperator() {
|
||||
List<Term> terms = TermExpressionParser.parse("type nin admin,root");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.nin, terms.get(0).getTermType());
|
||||
assertEquals("type", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBetweenOperator() {
|
||||
List<Term> terms = TermExpressionParser.parse("age btw 18,60");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.btw, terms.get(0).getTermType());
|
||||
assertEquals("age", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNull() {
|
||||
List<Term> terms = TermExpressionParser.parse("deletedTime isnull 1");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.isnull, terms.get(0).getTermType());
|
||||
assertEquals("deletedTime", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotNull() {
|
||||
List<Term> terms = TermExpressionParser.parse("createTime notnull 1");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.notnull, terms.get(0).getTermType());
|
||||
assertEquals("createTime", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
List<Term> terms = TermExpressionParser.parse("description empty 1");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.empty, terms.get(0).getTermType());
|
||||
assertEquals("description", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEmpty() {
|
||||
List<Term> terms = TermExpressionParser.parse("name nempty 1");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals(TermType.nempty, terms.get(0).getTermType());
|
||||
assertEquals("name", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndConditions() {
|
||||
List<Term> terms = TermExpressionParser.parse("name = test and age > 18 and status = active");
|
||||
|
||||
assertEquals(3, terms.size());
|
||||
assertEquals(TermType.eq, terms.get(0).getTermType());
|
||||
assertEquals("name", terms.get(0).getColumn());
|
||||
assertEquals(TermType.gt, terms.get(1).getTermType());
|
||||
assertEquals("age", terms.get(1).getColumn());
|
||||
assertEquals(TermType.eq, terms.get(2).getTermType());
|
||||
assertEquals("status", terms.get(2).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleOrConditions() {
|
||||
List<Term> terms = TermExpressionParser.parse("status = active or status = pending or status = approved");
|
||||
|
||||
assertEquals(3, terms.size());
|
||||
assertEquals(Term.Type.or, terms.get(1).getType());
|
||||
assertEquals(Term.Type.or, terms.get(2).getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedMultipleLevels() {
|
||||
List<Term> terms = TermExpressionParser.parse("age > 18 and (name = test or (status = active and type = user))");
|
||||
|
||||
assertEquals(2, terms.size());
|
||||
assertEquals(TermType.gt, terms.get(0).getTermType());
|
||||
assertNotNull(terms.get(1).getTerms());
|
||||
assertEquals(2, terms.get(1).getTerms().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecialCharactersInValue() {
|
||||
List<Term> terms = TermExpressionParser.parse("email = user@example.com");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("email", terms.get(0).getColumn());
|
||||
assertEquals("user@example.com", terms.get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumericValues() {
|
||||
{
|
||||
List<Term> terms = TermExpressionParser.parse("price = 99.99");
|
||||
assertEquals("99.99", terms.get(0).getValue());
|
||||
}
|
||||
{
|
||||
List<Term> terms = TermExpressionParser.parse("count = -10");
|
||||
assertEquals("-10", terms.get(0).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringValue() {
|
||||
List<Term> terms = TermExpressionParser.parse("description = \"\"");
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("description", terms.get(0).getColumn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhitespaceHandling() {
|
||||
List<Term> terms = TermExpressionParser.parse(" name = test and age > 18 ");
|
||||
|
||||
assertEquals(2, terms.size());
|
||||
assertEquals("name", terms.get(0).getColumn());
|
||||
assertEquals("test", terms.get(0).getValue());
|
||||
assertEquals("age", terms.get(1).getColumn());
|
||||
assertEquals("18", terms.get(1).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithComplexNestedConditions() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("$nest", "(name = test or name = demo) and age > 18");
|
||||
|
||||
List<Term> terms = TermExpressionParser.parse(map);
|
||||
|
||||
assertEquals(1, terms.size());
|
||||
assertNotNull(terms.get(0).getTerms());
|
||||
assertTrue(terms.get(0).getTerms().size() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithMultipleTermTypes() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("name$like", "%test%");
|
||||
map.put("age$gt", "18");
|
||||
map.put("status$in", "active,pending");
|
||||
map.put("deletedTime$isnull", "");
|
||||
|
||||
List<Term> terms = TermExpressionParser.parse(map);
|
||||
|
||||
assertEquals(4, terms.size());
|
||||
assertEquals("like", terms.get(0).getTermType());
|
||||
assertEquals("gt", terms.get(1).getTermType());
|
||||
assertEquals("in", terms.get(2).getTermType());
|
||||
assertEquals("isnull", terms.get(3).getTermType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedAndOrWithoutParentheses() {
|
||||
List<Term> terms = TermExpressionParser.parse("name = test and age > 18 or status = active");
|
||||
|
||||
assertNotNull(terms);
|
||||
assertTrue(terms.size() > 0);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user