Elasticsearch通用查询方案
《通用查询方案(一):开篇与基础概念》
《通用查询方案(二):QueryConditionBuilders 工具类》
《通用查询方案(三):QueryBuilderFactory 组件》
《通用查询方案(四):QueryCondition 使用指南与实战》
前言
在通用接口查询的复杂机械中,QueryBuilderFactory 宛如一台精密且强劲的核心引擎,驱动着零散的查询条件有序组合、变形,最终输出为能够精准检索数据的强大指令,为系统在数据海洋中导航。
一、QueryBuilderFactory:查询世界的 “组装大师”
QueryBuilderFactory 身负重任,其核心使命便是将一系列形态各异、语义丰富的 QueryCondition 对象,巧妙转化为适配 Elasticsearch 的 BoolQueryBuilder。无论是简单直白的单条件筛选,比如找出价格等于某个数值的商品;还是复杂到如同迷宫般多层嵌套的逻辑组合查询,像是筛选出特定时间段内发布、热门且满足多个附加条件的内容,它都能有条不紊地处理,成为连接抽象查询构思与具体数据获取的关键纽带,为业务决策提供坚如磐石的数据支持。
二、代码深度剖析
(一)buildBoolQuery 方法:指挥全局的 “调度员”
代码如下(示例):
@Component
public class QueryBuilderFactory {
/**
* 此方法作为构建流程的核心调度者,接收一组 QueryCondition 列表。
* 它遍历列表,凭借条件对象的 isComposite 属性区分逻辑组合条件与普通条件,
* 分别交予对应的处理方法,最终整合出完整的 BoolQueryBuilder,用于后续数据源查询。
*
* @param queryConditions 待处理的查询条件列表,涵盖各种类型的查询需求描述。
* @return 组装完毕的 BoolQueryBuilder,可无缝对接 Elasticsearch 执行查询。
*/
public BoolQueryBuilder buildBoolQuery(List<QueryCondition> queryConditions) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
for (QueryCondition condition : queryConditions) {
if (condition.isComposite()) {
boolQueryBuilder = handleCompositeCondition(boolQueryBuilder, condition);
} else {
boolQueryBuilder = processSingleCondition(boolQueryBuilder, condition);
}
}
return boolQueryBuilder;
}
这个方法站在构建流程的顶端,犹如一位调度经验丰富的指挥官。当传入一组 QueryCondition 列表时,它迅速开启逐一审视之旅。一旦识别出某个条件是复合条件(通过 isComposite 精准判断,像那些操作符为 MUST 或 SHOULD 且包裹着多个子条件的复杂家伙),便即刻指挥handleCompositeCondition 方法进行深度整合;若是普通的单条件,就转交给 processSingleCondition 方法细致雕琢,直至所有条件完美融入 BoolQueryBuilder 框架,输出一个完整、可用的查询构建器,随时准备奔赴数据战场。
(二)processSingleCondition 方法:雕琢细节的 “工匠”
/**
* 作为处理单个查询条件的专业工匠,该方法依据条件的操作符类型,
* 运用 switch 语句打造专属的 Elasticsearch 查询构建器片段,
* 并精准嵌入 BoolQueryBuilder 的对应位置,确保每个条件都准确表达查询语义。
* 若遇操作符使用不当情况,果断抛出异常维护逻辑严谨性。
*
* @param boolQueryBuilder 已有基础的 BoolQueryBuilder,等待条件片段嵌入。
* @param condition 单个 QueryCondition 对象,承载具体查询细节。
* @return 更新后的 BoolQueryBuilder,融入了新的条件构建成果。
*/
private BoolQueryBuilder processSingleCondition(BoolQueryBuilder boolQueryBuilder, QueryCondition condition) {
switch (condition.getOperator()) {
case EQ:
Object eqOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.termQuery(condition.getField(), eqOperand));
break;
case GT:
Object gtOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.rangeQuery(condition.getField()).gt(gtOperand));
break;
case GTE:
Object gteOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.rangeQuery(condition.getField()).gte(gteOperand));
break;
case LT:
Object ltOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.rangeQuery(condition.getField()).lt(ltOperand));
break;
case LTE:
Object lteOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.rangeQuery(condition.getField()).lte(lteOperand));
break;
case NOT_IN:
List<?> notInValues = (List<?>) condition.getOperand();
boolQueryBuilder.mustNot(QueryBuilders.termsQuery(condition.getField(), notInValues));
break;
case IN:
List<?> values = (List<?>) condition.getOperand();
boolQueryBuilder.must(QueryBuilders.termsQuery(condition.getField(), values));
break;
case RANGE:
List<Object[]> rangeValues = (List<Object[]>) condition.getOperand();
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(condition.getField());
for (Object[] rangeValue : rangeValues) {
if ((boolean) rangeValue[1]) {
rangeQueryBuilder.gte(rangeValue[0]);
} else {
rangeQueryBuilder.gt(rangeValue[0]);
}
if ((boolean) rangeValue[3]) {
rangeQueryBuilder.lte(rangeValue[2]);
} else {
rangeQueryBuilder.lt(rangeValue[2]);
}
}
boolQueryBuilder.must(rangeQueryBuilder);
break;
case WILDCARD:
String wildcardOperand = (String) condition.getOperand();
boolQueryBuilder.must(QueryBuilders.wildcardQuery(condition.getField(), wildcardOperand));
break;
case CONTAINS:
Object containsOperand = condition.getOperand();
boolQueryBuilder.must(QueryBuilders.matchQuery(condition.getField(), containsOperand));
break;
case EXISTS:
boolQueryBuilder.must(QueryBuilders.existsQuery(condition.getField()));
break;
case NESTED_EQ:
// 处理嵌套对象的精确匹配条件
Object[] nestedEqOperand = (Object[]) condition.getOperand();
String nestedPath = (String) nestedEqOperand[0];
String nestedField = (String) nestedEqOperand[1];
Object nestedValue = nestedEqOperand[2];
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(nestedPath,
QueryBuilders.termQuery(nestedField, nestedValue));
boolQueryBuilder.must(nestedQueryBuilder);
break;
case NE:
Object neOperand = condition.getOperand();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(condition.getField(), neOperand);
boolQueryBuilder.mustNot(termQueryBuilder);
break;
case MUST:
case MUST_NOT:
case SHOULD:
if (condition.isComposite()) {
boolQueryBuilder = handleCompositeCondition(boolQueryBuilder, condition);
} else {
throw new IllegalArgumentException("当前条件标记不是复合条件,但使用了MUST或SHOULD操作符");
}
break;
default:
throw new IllegalArgumentException("不支持的操作符: " + condition.getOperator().getCode());
}
return boolQueryBuilder;
}
此方法恰似一位专注于细节的工匠,针对不同操作符类型的单个查询条件精雕细琢。当遭遇 EQ,迅速锻造出 TermQueryBuilder,精准匹配字段值嵌入 must 子句;面对数值范围操作符,灵活构建 RangeQueryBuilder,严谨界定区间;处理集合相关的 NOT_IN 和 IN,巧妙调度 termsQuery;对于 RANGE,更是细致拆解操作数数组,精心构建起止条件完备的范围查询。一旦发现非复合条件误用 MUST 或 SHOULD,立即抛出异常,如一位严格的质检员,守护代码逻辑的纯净。
(三)handleCompositeCondition 方法:编织逻辑的 “巧匠”
/**
* 身为处理逻辑组合条件的大师级巧匠,该方法先拆解复合条件获取子条件列表,
* 递归调用 processSingleCondition 雕琢每个子条件,构建子查询逻辑,
* 最后依外层操作符(MUST 或 SHOULD)将子查询精准嫁接到主 BoolQueryBuilder。
*
* @param boolQueryBuilder 已有的主 BoolQueryBuilder,准备接纳子查询逻辑。
* @param condition 表示逻辑组合条件的 QueryCondition,蕴含多层子条件。
* @return 融入复杂逻辑组合的 BoolQueryBuilder,功能更强大完备。
*/
private BoolQueryBuilder handleCompositeCondition(BoolQueryBuilder boolQueryBuilder, QueryCondition condition) {
List<QueryCondition> subConditions = (List<QueryCondition>) condition.getOperand();
BoolQueryBuilder subBoolQueryBuilder = QueryBuilders.boolQuery();
for (QueryCondition subCondition : subConditions) {
subBoolQueryBuilder = processSingleCondition(subBoolQueryBuilder, subCondition);
}
if (condition.getOperator() == OperatorEnum.MUST) {
boolQueryBuilder.must(subBoolQueryBuilder);
} else if (condition.getOperator() == OperatorEnum.SHOULD){
boolQueryBuilder.should(subBoolQueryBuilder);
}else if (condition.getOperator() == OperatorEnum.MUST_NOT){
boolQueryBuilder.mustNot(subBoolQueryBuilder);
}
return boolQueryBuilder;
}
当 buildBoolQuery 转来复合条件时,这位 “编织大师” 登场。它先是从复合条件对象中巧妙提取子条件列表,接着创建一个全新的 BoolQueryBuilder(subBoolQueryBuilder)作为临时工作间。随后,通过循环递归调用 processSingleCondition,深入剖析处理每个子条件,哪怕子条件中再嵌套复合结构,也能层层拆解、丝丝入扣地构建。最终,依据外层操作符是 MUST 还是 SHOULD,将精心编织的子查询逻辑无缝接入主 BoolQueryBuilder 对应子句,完成复杂查询逻辑的华丽嵌套,恰似搭建一座精巧复杂的逻辑积木城堡。
三、实战场景:复杂查询构建实战秀
想象构建一个社交媒体数据分析的查询,目标是找出特定网红在过去一周内发布、点赞数超 1000 且评论含热门话题标签或者提及特定合作品牌的动态。
起始,借助配套工具(假设已有 QueryConditionBuilders 等得力助手)搭建基础条件:
// 网红作者条件
QueryCondition influencerCondition = QueryConditionBuilders.create(OperatorEnum.EQ, "author", "TopInfluencer");
// 时间范围条件
QueryCondition timeCondition = QueryConditionBuilders.create(OperatorEnum.RANGE, "post_time", new Object[]{lastWeekStart, true, lastWeekEnd, true});
// 点赞数条件
QueryCondition likeCondition = QueryConditionBuilders.create(OperatorEnum.GT, "likes", 1000);
List<QueryCondition> innerShouldConditions = new ArrayList<>();
// 话题标签条件
QueryCondition hashtagCondition = QueryConditionBuilders.create(OperatorEnum.CONTAINS, "comments", "#TrendingTopic");
innerShouldConditions.add(hashtagCondition);
// 品牌提及条件
QueryCondition brandCondition = QueryConditionBuilders.create(OperatorEnum.CONTAINS, "content", "PartnerBrand");
innerShouldConditions.add(brandCondition);
QueryCondition innerShould = QueryConditionBuilders.create(OperatorEnum.SHOULD, null, innerShouldConditions);
List<QueryCondition> outerMustConditions = new ArrayList<>();
outerMustConditions.add(influencerCondition);
outerMustConditions.add(timeCondition);
outerMustConditions.add(likeCondition);
outerMustConditions.add(innerShould);
而后,QueryBuilderFactory 施展魔法:
BoolQueryBuilder finalQuery = QueryBuilderFactory.buildBoolQuery(outerMustConditions);
在这段代码旅程中,先是精心构建各个基础条件,其中 innerShould 作为复合条件(SHOULD 逻辑)巧妙包裹话题和品牌条件,与其他 must 条件共同组成 outerMustConditions 列表。QueryBuilderFactory 的 buildBoolQuery 方法接手后,循环识别 innerShould 为复合条件,交予 handleCompositeCondition 处理子条件构建,其余非复合条件由 processSingleCondition 雕琢,最终融合成完整可执行查询,精准捞出目标动态,淋漓尽致地展现组件应对复杂需求的超凡实力。
总结
从接收 QueryConditionBuilders 产出的原始条件素材,到为后续 Elasticsearch 客户端呈上完美查询构建器,一气呵成。