将字符串 “()“ ““ “|“ 条件组成的复杂表达式转换为ES查询语句

应用场景

"()" "&" "|"  这几个条件对于我们来说并不陌生, 其表达的逻辑非常明了, 又能通过很少的字符表达很复杂的嵌套关系, 在一些复杂的查询中会经常用到, 因此我最近也遇到了类似的问题,一开始觉得这类的工具应该挺常见的, 结果搜了半天没有找到合适的,因此决定自己写一个

简介

此工具的复杂之处在于我们并不确定操作系统的人员会输入怎样的表达式,格式并不是固定的因此可能会书写出较为复杂的逻辑. 也有可能只嵌套一层就结束了,所以我们的代码一定要考虑的通用

此处我简单说一下它的原理, 主要是用到了一个java中栈的概念: 这个工具通过解析输入的逻辑查询字符串,使用栈来管理运算符和操作数,构建出对应的查询树,然后将其转换为Elasticsearch的多字段(如标题、摘要、正文)的搜索查询,实现复杂的逻辑查询条件的自动解析和执行。

以下代码全部都加了注释, 应该是不难理解的 

代码

package com.sinosoft.springbootplus.lft.business.dispatch.publicopinion.util;import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;import java.util.Stack;/*** 构建ES复杂查询条件,包含括号、逻辑运算符和操作符** @author zzt* @date 2024-05-28*/
public class ESQueryParserUtil {/*** 解析输入字符串并将其转换为Elasticsearch的QueryBuilder** @param query 输入的查询字符串* @return Elasticsearch的QueryBuilder*/public static SearchSourceBuilder parseQuery(String query) {// 存储运算符的栈Stack<Character> operators = new Stack<>();// 存储操作数的栈Stack<QueryBuilder> 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();QueryBuilder right = operands.pop();QueryBuilder 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(QueryBuilders.multiMatchQuery(sb.toString().trim(), "title", "sysAbstract", "content"));//此处是我的ES中要模糊搜索的三个字段, 这里请自行更改}}// 处理剩余的运算符while (!operators.isEmpty()) {char operator = operators.pop();QueryBuilder right = operands.pop();QueryBuilder left = operands.pop();operands.push(applyOperator(left, right, operator));}return new SearchSourceBuilder().query(operands.pop());}/*** 根据运算符将两个操作数组合成一个QueryBuilder** @param left     左操作数* @param right    右操作数* @param operator 运算符* @return 组合后的QueryBuilder*/private static QueryBuilder applyOperator(QueryBuilder left, QueryBuilder right, char operator) {BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();if (operator == '&') {boolQuery.must(left).must(right);} else if (operator == '|') {boolQuery.should(left).should(right);}return boolQuery;}public static void main(String[] args) {String query = "((北京|天津|(河北&石家庄))&(打架|辱骂|违法))&(中国)";SearchSourceBuilder searchSourceBuilder = parseQuery(query);System.out.println(searchSourceBuilder);}
}

 生成的查询条件

由于我写的这个算是稍微复杂一点的嵌套,生成的查询条件还是挺长的, 感兴趣的可以试一下

{"query": {"bool": {"must": [{"bool": {"must": [{"bool": {"should": [{"multi_match": {"query": "北京","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}},{"bool": {"should": [{"multi_match": {"query": "天津","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}},{"bool": {"must": [{"multi_match": {"query": "河北","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}},{"multi_match": {"query": "石家庄","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}},{"bool": {"should": [{"multi_match": {"query": "打架","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}},{"bool": {"should": [{"multi_match": {"query": "辱骂","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}},{"multi_match": {"query": "违法","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}},{"multi_match": {"query": "中国","fields": ["content^1.0","sysAbstract^1.0","title^1.0"],"type": "best_fields","operator": "OR","slop": 0,"prefix_length": 0,"max_expansions": 50,"zero_terms_query": "NONE","auto_generate_synonyms_phrase_query": true,"fuzzy_transpositions": true,"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21161.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM垃圾收集器和内存分配策略

概述 Java内存运行时数据区的程序计数器、虚拟机栈、本地方法栈3个区域会随着线程而产生&#xff0c;随线程而消失。这几个区域分配多少内存时在类结构确定下来即已知的&#xff0c;在这几个区域内就不需要过多考虑如何回收内存的问题&#xff0c;当方法结束或者线程结束时&am…

【spring】第一篇 IOC和DI入门案例

Spring到底是如何来实现IOC和DI的&#xff0c;那接下来就通过一些简单的入门案例&#xff0c;来演示下具体实现过程。 目录 前期准备 一、IOC入门案例 思路分析 代码实现 二、DI入门案例 思路分析 代码实现 总结 前期准备 使用IDEA创建Maven项目&#xff0c;首先需要配…

JAVAEE1

Web前端&#xff1a; 1.建立web开发的息维模式写代码不仅仅是为了实现某个功能&#xff0c;更是学习解决问题的思维方式 2.先使用&#xff0c;再理解&#xff0c;会导致刚开始比较懵&#xff0c;不知其所以然.切忌不可深陷其中&#xff0c; 3.涉及简单的软件工程的设计思想&…

Springboot整合kafka简单使用

kafka 一&#xff0c;介绍 Kafka 是一个开源的分布式流处理平台&#xff0c;最初由 LinkedIn 开发并贡献给 Apache 软件基金会。它设计用于构建高性能、持久性、可伸缩和容错的实时数据管道和流处理应用程序。 以下是 Kafka 的一些关键特点和概念&#xff1a; 发布-订阅模型…

SPWM载波调制方式-三电平杂记1

方法一&#xff1a; P2 O1 N0 方法二&#xff1a;双载波直接发波 方法三&#xff1a;负轴载波和调制波往上抬升1&#xff0c;得到使用同一个载波 在正半周在P和O切换&#xff0c;在下半轴式O和N切换

自动评论自动私信引流系统,自动化时代的挑战与机遇

随着科技的飞速发展&#xff0c;自动化技术已经渗透到我们生活的方方面面。从工业生产线上的机械臂到家庭中的智能助手&#xff0c;自动化不仅改变了我们的工作方式&#xff0c;也在重塑着社会的面貌。然而&#xff0c;在享受自动化带来的便利和效率的同时&#xff0c;我们也必…

961题库 北航计算机 MIPS基础选择题 附答案 选择题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 MIPS处理器五级流水线中&#xff0c;涉及DRAM的是 A. 取指阶段 B. 译码阶段 C. 执行阶段 D. 访存阶段 MIPS处理器五级流水线中&#xff0c;R型指令保存结果的阶段是 A.…

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法 如下图 将model saving history 修改为None即可 关于AutoCAD 2022 丢失模板库的问题 从新从以下地址打开即可&#xff1a; D:\Program Files\Autodesk\AutoCAD 2022\UserDataCache\zh-cn\Template

Visual Studio Installer 点击闪退

Visual Studio Installer 点击闪退问题 1. 问题描述2. 错误类型3. 解决方法4. 结果5. 说明6. 参考 1. 问题描述 重装了系统后&#xff08;系统版本&#xff1a;如下图所示&#xff09;&#xff0c;我从官方网站&#xff08;https://visualstudio.microsoft.com/ ) 下载了安装程…

Leetcode:正则表达式匹配

目录 普通版本&#xff08;动态规划&#xff09; 状态表示 状态转移方程 优化③①情况 数学化简分析 结合实际情况画图化简分析 总结 最终代码 题目链接&#xff1a;10. 正则表达式匹配 - 力扣&#xff08;LeetCode&#xff09; 好像是leetcode前100道里面最难的一道&a…

方法引用与构造方法引用

目录 方法引用 什么是方法引用 构造方法引用 构造方法引用&#xff08;也可以称作构造器引用&#xff09; 数组构造方法引用 方法引用 什么是方法引用 当要传递给 Lambda 体的操作&#xff0c;已经有实现的方法了&#xff0c;可以使用方法引用。 方法引用可以看做是 La…

PHAR反序列化

PHAR PHAR&#xff08;PHP Archive&#xff09;文件是一种归档文件格式&#xff0c;phar文件本质上是一种压缩文件&#xff0c;会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时&#xff0c;会自动反序列化meta-data内的内容,这里就是我们反序…

头歌页面置换算法第3关:计算LRU算法缺页率

2 任务:LRU算法 2.1 任务描述 设计LRU页面置换算法模拟程序:从键盘输入访问串。计算LRU算法在不同内存页框数时的缺页数和缺页率。要求程序模拟驻留集变化过程,即能模拟页框装入与释放过程。 2.2任务要求 输入串长度作为总页框数目,补充程序完成LRU算法。 2.3算法思路 LRU算…

jmeter常用的断言

包括&#xff08;Contains&#xff09;&#xff1a;响应内容包括需要匹配的内容即代表响应成功&#xff0c;支持正则表达式 匹配&#xff08;Matches&#xff09;&#xff1a;响应内容要完全匹配需要匹配的内容即代表响应成功&#xff0c;大小写不敏感&#xff0c;支持正则表达…

vue html2canvas生成base64图片和图片高度

vue html2canvas生成图片 exportAsBase64() {const ele document.getElementById(content);html2canvas(ele, {dpi: 96, // 分辨率 scale: 2, // 设置缩放 useCORS: true, // 允许canvas画布内跨域请求外部链接图片 bgcolor: #ffffff, // 背景颜色 logging: false, // 不…

rust之cargo install cargo-binstall 是什么

cargo-binstall 是什么 官方&#xff1a;https://lib.rs/crates/cargo-binstall Binstall 提供了一种低复杂性的机制来安装 Rust 二进制文件&#xff0c;作为从源代码&#xff08;通过 cargo install &#xff09;构建或手动下载软件包的替代方案。这旨在与现有的 CI 工件和基…

Windows安装ElasticSearch版本7.17.0

在Windows系统上本地安装Elasticsearch的详细步骤如下&#xff1a; 1. 下载Elasticsearch 访问 Elasticsearch下载页面。选择适用于Windows的版本7.17.0&#xff0c;并下载ZIP文件。 2. 解压文件 下载完成后&#xff0c;找到ZIP文件&#xff08;例如 elasticsearch-7.17.0.…

【算法篇】冒泡排序算法JavaScript版

冒泡排序算法&#xff1a;原理与实现 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它重复地遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换&…

spoon基础使用-第一个转换文件

新建一个转换&#xff0c;文件->新建->转换&#xff0c;也可以直接ctralN新建。 从右边主对象树拖拽一个输入->表输入&#xff1b;输出->文本文档输出&#xff1b;也可以直接在搜索框搜素表输入、文本文档输出。 双击表输入新建一个数据库连接 确定后就可以在S…

【人工智能】第二部分:ChatGPT的架构设计和训练过程

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…