es8中解析字符串中的"与"、"或"、"括号"拼装查询DSL

37 阅读2分钟

es8中解析字符串中的"与"、"或"、"括号"拼装查询DSL

依赖

implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
implementation 'co.elastic.clients:elasticsearch-java:8.11.0'
implementation 'com.fasterxml.jackson.core:jackson-databind'

// Lombok
compileOnly("org.projectlombok:lombok:1.18.30")
annotationProcessor("org.projectlombok:lombok:1.18.30")

// 测试代码中的 Lombok
testCompileOnly("org.projectlombok:lombok:1.18.30")
testAnnotationProcessor("org.projectlombok:lombok:1.18.30")

package cn.golaxy.lqtest.es;


import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;

import java.util.Stack;

//构建ES复杂查询条件,包含括号、逻辑运算符和操作符
 
public class ESQueryParserUtil {

    /**
     * 解析输入字符串并将其转换为Elasticsearch的QueryBuilder
     *
     * @param query 输入的查询字符串
     * @return Elasticsearch的QueryBuilder
     */
    public static SearchRequest parseQuery(String query) {
        // 存储运算符的栈
        Stack<Character> operators = new Stack<>();
        // 存储操作数的栈
        Stack<Query> operands = new Stack<>();

        for (int i = 0; i < query.length(); i++) {
            char ch = query.charAt(i);

            if (ch == '(' || ch == '&' || ch == '|') {
                // 遇到左括号或者运算符时,压入运算符栈
                operators.push(ch);
            } else if (ch == ')') {
                // 遇到右括号时,弹出运算符栈中的运算符并进行计算直到遇到左括号
                while (!operators.isEmpty() && operators.peek() != '(') {
                    char operator = operators.pop();
                    Query right = operands.pop();
                    Query left = operands.pop();
                    operands.push(applyOperator(left, right, operator));
                }
                operators.pop(); // 弹出左括号
            } else if (Character.isLetterOrDigit(ch) || ch == ' ') {
                // 遇到字母、数字、空格或者“地区”时,构建查询字符串
                StringBuilder sb = new StringBuilder();
                while (i < query.length() && (Character.isLetterOrDigit(query.charAt(i)) || query.charAt(i) == ' ')) {
                    sb.append(query.charAt(i));
                    i++;
                }
                i--; // 回退一个字符,因为外层for循环会前进一个字符
                operands.push(Query.of(q -> q.multiMatch(m -> m.fields("name").query(sb.toString().trim()))));//此处是我的ES中要模糊搜索的三个字段, 这里请自行更改
            }
        }

        // 处理剩余的运算符
        while (!operators.isEmpty()) {
            char operator = operators.pop();
            Query right = operands.pop();
            Query left = operands.pop();
            operands.push(applyOperator(left, right, operator));
        }
        return SearchRequest.of(sr -> sr.query(operands.pop()));
    }

    /**
     * 根据运算符将两个操作数组合成一个Query
     *
     * @param left     左操作数
     * @param right    右操作数
     * @param operator 运算符
     * @return 组合后的QueryBuilder
     */
    private static Query applyOperator(Query left, Query right, char operator) {

        if (operator == '&') {
            return Query.of(q -> q.bool(b -> b.must(left).must(right)));
        } else if (operator == '|') {
            return Query.of(q -> q.bool(b -> b.should(left).should(right)));
        }
        return null;
    }

    public static void main(String[] args) {
//        String query = "((北京|天津|(河北&石家庄))&(打架|辱骂|违法))&(中国)";
        String query = "(河北&石家庄)|打架&新闻";
        SearchRequest searchRequest = parseQuery(query);
        System.out.println(searchRequest);
    }
}

生成查询json

{
    "query": {
        "bool": {
            "should": [
                {
                    "bool": {
                        "must": [
                            {
                                "multi_match": {
                                    "fields": [
                                        "name"
                                    ],
                                    "query": "河北"
                                }
                            },
                            {
                                "multi_match": {
                                    "fields": [
                                        "name"
                                    ],
                                    "query": "石家庄"
                                }
                            }
                        ]
                    }
                },
                {
                    "bool": {
                        "must": [
                            {
                                "multi_match": {
                                    "fields": [
                                        "name"
                                    ],
                                    "query": "打架"
                                }
                            },
                            {
                                "multi_match": {
                                    "fields": [
                                        "name"
                                    ],
                                    "query": "新闻"
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}