构建可扩展Python布尔搜索解析器:生成抽象语法树及展开查询
哇,你已经完成了布尔搜索解析器里最硬核的AST构建部分,这可是绕不开的关键一步!接下来要实现复杂表达式的展开和更多运算符支持,咱们可以分模块来推进,思路清晰又不容易踩坑:
一、搞定AND分组的笛卡尔积展开
你要的把(A OR B) AND (C OR D) AND (E OR F)拆成所有AND组合的需求,本质是计算多个OR组的笛卡尔积。具体步骤可以这么做:
- 遍历AST的顶层AND节点,把每个子项转换成表达式列表:如果是OR节点,提取它的所有子表达式;如果是单个表达式(比如纯关键词、NOT语句、邻近运算符),就当成只有一个元素的列表。
- 用
itertools.product计算这些列表的笛卡尔积,每个乘积结果拼接成一行简化的AND表达式。
举个实现片段:
import itertools def expand_and_groups(processed_children): # 先把每个AND子项整理成可迭代的表达式列表 groups = [] for expr_list in processed_children: # 这里expr_list是子节点处理后得到的表达式列表(比如OR组会拆成多个元素) groups.append(expr_list) # 生成所有笛卡尔积组合,拼接成AND表达式 for combo in itertools.product(*groups): yield " AND ".join(combo)
二、NOT运算符的特殊处理(含德摩根定律)
NOT的处理要分场景,尤其是嵌套在括号里的情况:
- 如果是
NOT (A OR B)这种形式,要应用德摩根定律转换成NOT A AND NOT B;如果是NOT (A AND B),则转换成NOT A OR NOT B。 - 如果是
(A OR NOT B)这种局部否定,直接把NOT B当成一个独立元素参与笛卡尔积即可。
可以写个专门处理德摩根转换的函数:
def apply_de_morgan(not_node): child = not_node.children[0] if child.type == "OR": # NOT (A OR B) → NOT A AND NOT B return Node(type="AND", children=[Node(type="NOT", children=[c]) for c in child.children]) elif child.type == "AND": # NOT (A AND B) → NOT A OR NOT B return Node(type="OR", children=[Node(type="NOT", children=[c]) for c in child.children]) else: # 单个表达式的否定,直接返回原节点 return not_node
三、邻近运算符的独立处理
邻近运算符(比如NEAR/n、ADJ)是位置约束,不属于传统布尔逻辑,所以要把它们当成不可拆分的整体:
- 在AST构建阶段,把邻近运算符识别为独立的节点类型(比如
PROXIMITY),存储左右表达式和距离参数。 - 序列化时输出成标准格式,比如
X NEAR/5 Y。 - 在展开AND组时,邻近节点直接作为单元素参与笛卡尔积,不要拆分它的左右部分。
四、递归处理任意层级括号
因为AST是树形结构,所有的展开和转换都要递归遍历每个子节点,先处理内层括号,再处理外层:
def process_ast(node): if node.type == "AND": # 递归处理每个AND子节点 processed_children = [process_ast(child) for child in node.children] # 展开所有AND组合 return list(expand_and_groups(processed_children)) elif node.type == "OR": # OR节点的子节点结果直接扁平化合并 processed_children = [process_ast(child) for child in node.children] return [item for sublist in processed_children for item in sublist] elif node.type == "NOT": # 先处理被否定的子节点,再应用德摩根定律 processed_child = process_ast(node.children[0]) # 这里需要把processed_child重新包装成AST节点再传入apply_de_morgan demorgan_node = apply_de_morgan(Node(type="NOT", children=[processed_child])) return process_ast(demorgan_node) elif node.type == "PROXIMITY": # 邻近节点直接返回序列化字符串的列表 return [f"{node.left.value} NEAR/{node.distance} {node.right.value}"] else: # 基础关键词节点,返回自身字符串的列表 return [node.value]
五、测试边界情况
别忘测试一些容易踩坑的场景:
- 多层嵌套括号:比如
((A OR B) AND NOT (C OR D)) AND (E NEAR/2 F),确保展开后生成4行正确的表达式。 - 混合运算符:比如
A AND (B OR NOT C) AND (D NEAR/1 E OR F),验证NOT和邻近运算符都正确保留。 - 空表达式或无效输入:在展开阶段加个判断,避免生成
AND AND这种无效字符串。
内容的提问来源于stack exchange,提问作者nrflaw
相关产品推荐
相关产品推荐

