Solon v3.3.0

SnEL 表达式的转换

</> markdown

SnEL 在解析表达式后,会形成一个抽象语法树(AST)。基于 AST 可以中转解析为新的语法,或者处理代码,或代码结构。

比如,转换为 Sql,Redis,ElasticSearch filter..

1、中转打印

String expression = "(((age > 18 AND salary < 5000) OR (NOT isMarried)) AND label IN ['aa','bb'] AND title NOT IN ['cc','dd']) OR vip=='l3'";

//解析出表达式
Expression root = SnEL.parse(expression);

//打印表达式树
PrintUtil.printTree(root);

2、Transformer<S,T> 接口中

为了转换更具规范性,我们定义了转换的专用接口

public interface Transformer<S,T> {
    /**
     * 转换
     */
    T transform(Expression<S> source);
}

参考:打印语法树工具 PrintUtil

public class PrintUtil {
    public static void printTree(Expression node) {
        printTreeDo(node, 0);
    }

    static void printTreeDo(Expression node, int level) {
        if (node instanceof VariableNode) {
            System.out.println(prefix(level) + "Field: " + ((VariableNode) node).getName());
        } else if (node instanceof ConstantNode) {
            Object value = ((ConstantNode) node).getValue();
            if (value instanceof String) {
                System.out.println(prefix(level) + "Value: '" + value + "'");
            } else {
                System.out.println(prefix(level) + "Value: " + value);
            }

        } else if (node instanceof ComparisonNode) {
            ComparisonNode compNode = (ComparisonNode) node;
            System.out.println(prefix(level) + "Comparison: " + compNode.getOperator());

            printTreeDo(compNode.getLeft(), level + 1);
            printTreeDo(compNode.getRight(), level + 1);
        } else if (node instanceof LogicalNode) {
            LogicalNode opNode = (LogicalNode) node;
            System.out.println(prefix(level) + "Logical: " + opNode.getOperator());

            printTreeDo(opNode.getLeft(), level + 1);
            printTreeDo(opNode.getRight(), level + 1);
        }
    }

    static String prefix(int n) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append("  ");
        }

        return sb.toString();
    }
}

参考:转为 milvus 过滤表达式

String filter = FilterTransformer.getInstance(root);
public class FilterTransformer implements Transformer<Boolean, String> {
    private static FilterTransformer instance = new FilterTransformer();

    public static FilterTransformer getInstance() {
        return instance;
    }

    @Override
    public String transform(Expression<Boolean> filterExpression) {
        StringBuilder buf = new StringBuilder();
        parseFilterExpression(filterExpression, buf);
        return buf.toString();
    }

    private void parseFilterExpression(Expression<Boolean> filterExpression, StringBuilder buf) {
        if (filterExpression instanceof VariableNode) {
            buf.append("metadata[\"").append(((VariableNode) filterExpression).getName()).append("\"]");
        } else if (filterExpression instanceof ConstantNode) {
            Object value = ((ConstantNode) filterExpression).getValue();
            // 判断是否为Collection类型
            if (((ConstantNode) filterExpression).isCollection()) {
                buf.append("[");
                for (Object item : (Iterable<?>) value) {
                    if (item instanceof String) {
                        buf.append("\"").append(item).append("\"");
                    } else {
                        buf.append(item);
                    }
                    buf.append(", ");
                }
                if (buf.length() > 1) {
                    buf.setLength(buf.length() - 1);
                }
                buf.append("]");
            } else if (value instanceof String) {
                buf.append("\"").append(value).append("\"");
            } else {
                buf.append(value);
            }
        } else if (filterExpression instanceof ComparisonNode) {
            ComparisonNode compNode = (ComparisonNode) filterExpression;
            buf.append("(");
            parseFilterExpression(compNode.getLeft(), buf);
            buf.append(" ").append(compNode.getOperator().getCode().toLowerCase()).append(" ");
            parseFilterExpression(compNode.getRight(), buf);
            buf.append(")");
        } else if (filterExpression instanceof LogicalNode) {
            LogicalNode opNode = (LogicalNode) filterExpression;
            buf.append("(");
            if (opNode.getRight() != null) {
                parseFilterExpression(opNode.getLeft(), buf);
                buf.append(" ").append(opNode.getOperator().getCode().toLowerCase()).append(" ");
                parseFilterExpression(opNode.getRight(), buf);
            } else {
                buf.append(opNode.getOperator().getCode()).append(" ");
                parseFilterExpression(opNode.getLeft(), buf);
            }
            buf.append(")");
        }
    }
}

参考:转为 redis 过滤表达式

String filter = FilterTransformer.getInstance(root);
public class FilterTransformer implements Transformer<Boolean, String> {
    private static FilterTransformer instance = new FilterTransformer();

    public static FilterTransformer getInstance() {
        return instance;
    }

    @Override
    public String transform(Expression<Boolean> filterExpression) {
        if (filterExpression == null) {
            return "*";
        }

        try {
            StringBuilder buf = new StringBuilder();
            parseFilterExpression(filterExpression, buf);

            if (buf.length() == 0) {
                return "*";
            }

            return buf.toString();
        } catch (Exception e) {
            System.err.println("Error processing filter expression: " + e.getMessage());
            return "*";
        }
    }

    /**
     * 解析QueryCondition中的filterExpression,转换为Redis Search语法
     *
     * @param filterExpression
     * @param buf
     */
    private void parseFilterExpression(Expression<Boolean> filterExpression, StringBuilder buf) {
        if (filterExpression == null) {
            return;
        }

        if (filterExpression instanceof VariableNode) {
            // 变量节点,获取字段名 - 为Redis添加@前缀
            String name = ((VariableNode) filterExpression).getName();
            buf.append("@").append(name);
        } else if (filterExpression instanceof ConstantNode) {
            ConstantNode node = (ConstantNode) filterExpression;
            // 常量节点,获取值
            Object value = node.getValue();

            if (node.isCollection()) {
                // 集合使用Redis的OR语法 {val1|val2|val3}
                buf.append("{");
                boolean first = true;
                for (Object item : (Collection<?>) value) {
                    if (!first) {
                        buf.append("|"); // Redis 使用 | 分隔OR条件
                    }
                    buf.append(item);
                    first = false;
                }
                buf.append("}");
            } else if (value instanceof String) {
                // 字符串值使用大括号
                buf.append("{").append(value).append("}");
            } else {
                buf.append(value);
            }
        } else if (filterExpression instanceof ComparisonNode) {
            ComparisonNode node = (ComparisonNode) filterExpression;
            ComparisonOp operator = node.getOperator();
            Expression left = node.getLeft();
            Expression right = node.getRight();

            // 比较节点
            switch (operator) {
                case eq:
                    parseFilterExpression(left, buf);
                    buf.append(":");
                    parseFilterExpression(right, buf);
                    break;
                case neq:
                    buf.append("-");
                    parseFilterExpression(left, buf);
                    buf.append(":");
                    parseFilterExpression(right, buf);
                    break;
                case gt:
                    parseFilterExpression(left, buf);
                    buf.append(":[");
                    parseFilterExpression(right, buf);
                    buf.append(" +inf]");
                    break;
                case gte:
                    parseFilterExpression(left, buf);
                    buf.append(":[");
                    parseFilterExpression(right, buf);
                    buf.append(" +inf]");
                    break;
                case lt:
                    parseFilterExpression(left, buf);
                    buf.append(":[-inf ");
                    parseFilterExpression(right, buf);
                    buf.append("]");
                    break;
                case lte:
                    parseFilterExpression(left, buf);
                    buf.append(":[-inf ");
                    parseFilterExpression(right, buf);
                    buf.append("]");
                    break;
                case in:
                    parseFilterExpression(left, buf);
                    buf.append(":");
                    parseFilterExpression(right, buf);
                    break;
                case nin:
                    buf.append("-");
                    parseFilterExpression(left, buf);
                    buf.append(":");
                    parseFilterExpression(right, buf);
                    break;
                default:
                    parseFilterExpression(left, buf);
                    buf.append(":");
                    parseFilterExpression(right, buf);
                    break;
            }
        } else if (filterExpression instanceof LogicalNode) {
            LogicalNode node = (LogicalNode) filterExpression;
            LogicalOp operator = node.getOperator();
            Expression left = node.getLeft();
            Expression right = node.getRight();

            buf.append("(");

            if (right != null) {
                // 二元操作符 (AND, OR)
                parseFilterExpression(left, buf);

                switch (operator) {
                    case AND:
                        buf.append(" "); // Redis Search 使用空格表示 AND
                        break;
                    case OR:
                        buf.append(" | "); // Redis Search 使用 | 表示 OR
                        break;
                    default:
                        // 其他操作符,默认用空格
                        buf.append(" ");
                        break;
                }

                parseFilterExpression(right, buf);
            } else {
                // 一元操作符 (NOT)
                switch (operator) {
                    case NOT:
                        buf.append("-"); // Redis Search 使用 - 表示 NOT
                        break;
                    default:
                        // 其他一元操作符,不添加前缀
                        break;
                }
                parseFilterExpression(left, buf);
            }

            buf.append(")");
        }
    }
}

参考:转为 elasticsearch 过滤对象

Map<String, Object> filter = FilterTransformer.getInstance(root);
public class FilterTransformer implements Transformer<Boolean, Map<String, Object>> {
    private static FilterTransformer instance = new FilterTransformer();

    public static FilterTransformer getInstance() {
        return instance;
    }

    /**
     * 将过滤表达式转换为Elasticsearch查询
     *
     * @param filterExpression 过滤表达式
     * @return Elasticsearch查询对象
     */
    @Override
    public Map<String, Object> transform(Expression<Boolean> filterExpression) {
        if (filterExpression == null) {
            return null;
        }

        if (filterExpression instanceof VariableNode) {
            // 变量节点,获取字段名
            String fieldName = ((VariableNode) filterExpression).getName();
            Map<String, Object> exists = new HashMap<>();
            Map<String, Object> field = new HashMap<>();
            field.put("field", fieldName);
            exists.put("exists", field);
            return exists;
        } else if (filterExpression instanceof ConstantNode) {
            // 常量节点,根据值类型和是否为集合创建不同的查询
            ConstantNode node = (ConstantNode) filterExpression;
            Object value = node.getValue();
            Boolean isCollection = node.isCollection();

            if (Boolean.TRUE.equals(value)) {
                Map<String, Object> matchAll = new HashMap<>();
                matchAll.put("match_all", new HashMap<>());
                return matchAll;
            } else if (Boolean.FALSE.equals(value)) {
                Map<String, Object> boolQuery = new HashMap<>();
                Map<String, Object> mustNot = new HashMap<>();
                mustNot.put("match_all", new HashMap<>());
                boolQuery.put("must_not", mustNot);
                return boolQuery;
            }

            return null;
        } else if (filterExpression instanceof ComparisonNode) {
            // 比较节点,处理各种比较运算符
            ComparisonNode node = (ComparisonNode) filterExpression;
            ComparisonOp operator = node.getOperator();
            Expression left = node.getLeft();
            Expression right = node.getRight();

            // 获取字段名和值
            String fieldName = null;
            Object value = null;

            if (left instanceof VariableNode && right instanceof ConstantNode) {
                fieldName = ((VariableNode) left).getName();
                value = ((ConstantNode) right).getValue();
            } else if (right instanceof VariableNode && left instanceof ConstantNode) {
                fieldName = ((VariableNode) right).getName();
                value = ((ConstantNode) left).getValue();
                // 反转操作符
                operator = reverseOperator(operator);
            } else {
                // 不支持的比较节点结构
                return null;
            }

            // 根据操作符构建相应的查询
            switch (operator) {
                case eq:
                    return createTermQuery(fieldName, value);
                case neq:
                    return createMustNotQuery(createTermQuery(fieldName, value));
                case gt:
                    return createRangeQuery(fieldName, "gt", value);
                case gte:
                    return createRangeQuery(fieldName, "gte", value);
                case lt:
                    return createRangeQuery(fieldName, "lt", value);
                case lte:
                    return createRangeQuery(fieldName, "lte", value);
                case in:
                    if (value instanceof Collection) {
                        return createTermsQuery(fieldName, (Collection<?>) value);
                    }
                    return createTermQuery(fieldName, value);
                case nin:
                    if (value instanceof Collection) {
                        return createMustNotQuery(createTermsQuery(fieldName, (Collection<?>) value));
                    }
                    return createMustNotQuery(createTermQuery(fieldName, value));
                default:
                    return null;
            }
        } else if (filterExpression instanceof LogicalNode) {
            // 逻辑节点,处理AND, OR, NOT
            LogicalNode node = (LogicalNode) filterExpression;
            LogicalOp operator = node.getOperator();
            Expression left = node.getLeft();
            Expression right = node.getRight();

            if (right != null) {
                // 二元逻辑运算符 (AND, OR)
                Map<String, Object> leftQuery = transform(left);
                Map<String, Object> rightQuery = transform(right);

                if (leftQuery == null || rightQuery == null) {
                    return null;
                }

                Map<String, Object> boolQuery = new HashMap<>();
                List<Map<String, Object>> conditions = new ArrayList<>();
                conditions.add(leftQuery);
                conditions.add(rightQuery);

                switch (operator) {
                    case AND:
                        boolQuery.put("must", conditions);
                        break;
                    case OR:
                        boolQuery.put("should", conditions);
                        break;
                    default:
                        return null;
                }

                Map<String, Object> result = new HashMap<>();
                result.put("bool", boolQuery);
                return result;
            } else if (left != null) {
                // 一元逻辑运算符 (NOT)
                Map<String, Object> operandQuery = transform(left);

                if (operandQuery == null) {
                    return null;
                }

                if (operator == LogicalOp.NOT) {
                    return createMustNotQuery(operandQuery);
                }
            }
        }

        return null;
    }

    /**
     * 反转比较运算符
     *
     * @param op 原运算符
     * @return 反转后的运算符
     */
    private ComparisonOp reverseOperator(ComparisonOp op) {
        switch (op) {
            case gt:
                return ComparisonOp.lt;
            case gte:
                return ComparisonOp.lte;
            case lt:
                return ComparisonOp.gt;
            case lte:
                return ComparisonOp.gte;
            default:
                return op;
        }
    }

    /**
     * 创建term查询
     *
     * @param field 字段名
     * @param value 值
     * @return 查询对象
     */
    private Map<String, Object> createTermQuery(String field, Object value) {
        Map<String, Object> termValue = new HashMap<>();
        termValue.put("value", value);
        Map<String, Object> term = new HashMap<>();
        term.put(field, termValue);

        Map<String, Object> result = new HashMap<>();
        result.put("term", term);
        return result;
    }

    /**
     * 创建terms查询(适用于集合)
     *
     * @param field  字段名
     * @param values 值集合
     * @return 查询对象
     */
    private Map<String, Object> createTermsQuery(String field, Collection<?> values) {
        Map<String, Object> terms = new HashMap<>();
        terms.put(field, new ArrayList<>(values));

        Map<String, Object> result = new HashMap<>();
        result.put("terms", terms);
        return result;
    }

    /**
     * 创建范围查询
     *
     * @param field    字段名
     * @param operator 操作符(gt, gte, lt, lte)
     * @param value    值
     * @return 查询对象
     */
    private Map<String, Object> createRangeQuery(String field, String operator, Object value) {
        Map<String, Object> rangeValue = new HashMap<>();
        rangeValue.put(operator, value);

        Map<String, Object> range = new HashMap<>();
        range.put(field, rangeValue);

        Map<String, Object> result = new HashMap<>();
        result.put("range", range);
        return result;
    }

    /**
     * 创建must_not查询(NOT操作)
     *
     * @param query 要否定的查询
     * @return 查询对象
     */
    private Map<String, Object> createMustNotQuery(Map<String, Object> query) {
        if (query == null) {
            return null;
        }

        Map<String, Object> boolQuery = new HashMap<>();
        List<Map<String, Object>> mustNot = new ArrayList<>();
        mustNot.add(query);
        boolQuery.put("must_not", mustNot);

        Map<String, Object> result = new HashMap<>();
        result.put("bool", boolQuery);
        return result;
    }
}