From 9ab2811d9e77af4efb5b5754e060ef295091bd4f Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Mon, 28 Jul 2025 11:48:52 -0400 Subject: [PATCH 1/9] update intercept framework to handle per field boost --- .../SemanticKnnVectorQueryRewriteInterceptor.java | 4 ++-- .../queries/SemanticMatchQueryRewriteInterceptor.java | 6 ++++-- .../queries/SemanticQueryRewriteInterceptor.java | 11 +++++++---- .../SemanticSparseVectorQueryRewriteInterceptor.java | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java index b1f5c240371f8..6c3ec2add4bc8 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java @@ -33,10 +33,10 @@ public class SemanticKnnVectorQueryRewriteInterceptor extends SemanticQueryRewri public SemanticKnnVectorQueryRewriteInterceptor() {} @Override - protected String getFieldName(QueryBuilder queryBuilder) { + protected Map getFieldNamesWithBoosts(QueryBuilder queryBuilder) { assert (queryBuilder instanceof KnnVectorQueryBuilder); KnnVectorQueryBuilder knnVectorQueryBuilder = (KnnVectorQueryBuilder) queryBuilder; - return knnVectorQueryBuilder.getFieldName(); + return Map.of(knnVectorQueryBuilder.getFieldName(), 1.0f); } @Override diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java index a6599afc66c3f..ee18834749635 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java @@ -12,6 +12,8 @@ import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; +import java.util.Map; + public class SemanticMatchQueryRewriteInterceptor extends SemanticQueryRewriteInterceptor { public static final NodeFeature SEMANTIC_MATCH_QUERY_REWRITE_INTERCEPTION_SUPPORTED = new NodeFeature( @@ -21,10 +23,10 @@ public class SemanticMatchQueryRewriteInterceptor extends SemanticQueryRewriteIn public SemanticMatchQueryRewriteInterceptor() {} @Override - protected String getFieldName(QueryBuilder queryBuilder) { + protected Map getFieldNamesWithBoosts(QueryBuilder queryBuilder) { assert (queryBuilder instanceof MatchQueryBuilder); MatchQueryBuilder matchQueryBuilder = (MatchQueryBuilder) queryBuilder; - return matchQueryBuilder.fieldName(); + return Map.of(matchQueryBuilder.fieldName(), 1.0f); } @Override diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index bb76ef0be24e9..ff1b20e96e75c 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -33,7 +33,7 @@ public SemanticQueryRewriteInterceptor() {} @Override public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilder queryBuilder) { - String fieldName = getFieldName(queryBuilder); + Map fieldsWithBoosts = getFieldNamesWithBoosts(queryBuilder); ResolvedIndices resolvedIndices = context.getResolvedIndices(); if (resolvedIndices == null) { @@ -41,6 +41,7 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde return queryBuilder; } + String fieldName = fieldsWithBoosts.keySet().stream().findFirst().get(); InferenceIndexInformationForField indexInformation = resolveIndicesForField(fieldName, resolvedIndices); if (indexInformation.getInferenceIndices().isEmpty()) { // No inference fields were identified, so return the original query. @@ -58,10 +59,12 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde } /** - * @param queryBuilder {@link QueryBuilder} - * @return The singular field name requested by the provided query builder. + * Extracts field names and their associated boost values from the query builder. + * + * @param queryBuilder the query builder to extract field information from + * @return a map where keys are field names and values are their boost multipliers */ - protected abstract String getFieldName(QueryBuilder queryBuilder); + protected abstract Map getFieldNamesWithBoosts(QueryBuilder queryBuilder); /** * @param queryBuilder {@link QueryBuilder} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java index c85a21f10301d..b735c223d75fe 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java @@ -27,10 +27,10 @@ public class SemanticSparseVectorQueryRewriteInterceptor extends SemanticQueryRe public SemanticSparseVectorQueryRewriteInterceptor() {} @Override - protected String getFieldName(QueryBuilder queryBuilder) { + protected Map getFieldNamesWithBoosts(QueryBuilder queryBuilder) { assert (queryBuilder instanceof SparseVectorQueryBuilder); SparseVectorQueryBuilder sparseVectorQueryBuilder = (SparseVectorQueryBuilder) queryBuilder; - return sparseVectorQueryBuilder.getFieldName(); + return Map.of(sparseVectorQueryBuilder.getFieldName(), 1.0f); } @Override From 6af62fea9383266e454995538875be9050bb79a8 Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Mon, 28 Jul 2025 12:06:28 -0400 Subject: [PATCH 2/9] supply boost into existing intercepter builders --- .../SemanticKnnVectorQueryRewriteInterceptor.java | 9 +++++---- .../SemanticMatchQueryRewriteInterceptor.java | 9 +++++---- .../queries/SemanticQueryRewriteInterceptor.java | 15 ++++++++++----- ...manticSparseVectorQueryRewriteInterceptor.java | 9 +++++---- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java index 6c3ec2add4bc8..c34f686befa0f 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java @@ -48,7 +48,7 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation) { + protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { assert (queryBuilder instanceof KnnVectorQueryBuilder); KnnVectorQueryBuilder knnVectorQueryBuilder = (KnnVectorQueryBuilder) queryBuilder; Map> inferenceIdsIndices = indexInformation.getInferenceIdsIndices(); @@ -63,7 +63,7 @@ protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceI // Multiple inference IDs, construct a boolean query finalQueryBuilder = buildInferenceQueryWithMultipleInferenceIds(knnVectorQueryBuilder, inferenceIdsIndices); } - finalQueryBuilder.boost(queryBuilder.boost()); + finalQueryBuilder.boost(queryBuilder.boost() * fieldBoost); finalQueryBuilder.queryName(queryBuilder.queryName()); return finalQueryBuilder; } @@ -87,7 +87,8 @@ private QueryBuilder buildInferenceQueryWithMultipleInferenceIds( @Override protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( QueryBuilder queryBuilder, - InferenceIndexInformationForField indexInformation + InferenceIndexInformationForField indexInformation, + Float fieldBoost ) { assert (queryBuilder instanceof KnnVectorQueryBuilder); KnnVectorQueryBuilder knnVectorQueryBuilder = (KnnVectorQueryBuilder) queryBuilder; @@ -106,7 +107,7 @@ protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( ) ); } - boolQueryBuilder.boost(queryBuilder.boost()); + boolQueryBuilder.boost(queryBuilder.boost() * fieldBoost); boolQueryBuilder.queryName(queryBuilder.queryName()); return boolQueryBuilder; } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java index ee18834749635..7cac383705e22 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java @@ -37,9 +37,9 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation) { + protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { SemanticQueryBuilder semanticQueryBuilder = new SemanticQueryBuilder(indexInformation.fieldName(), getQuery(queryBuilder), false); - semanticQueryBuilder.boost(queryBuilder.boost()); + semanticQueryBuilder.boost(queryBuilder.boost() * fieldBoost); semanticQueryBuilder.queryName(queryBuilder.queryName()); return semanticQueryBuilder; } @@ -47,7 +47,8 @@ protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceI @Override protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( QueryBuilder queryBuilder, - InferenceIndexInformationForField indexInformation + InferenceIndexInformationForField indexInformation, + Float fieldBoost ) { assert (queryBuilder instanceof MatchQueryBuilder); MatchQueryBuilder originalMatchQueryBuilder = (MatchQueryBuilder) queryBuilder; @@ -63,7 +64,7 @@ protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( ) ); boolQueryBuilder.should(createSubQueryForIndices(indexInformation.nonInferenceIndices(), matchQueryBuilder)); - boolQueryBuilder.boost(queryBuilder.boost()); + boolQueryBuilder.boost(queryBuilder.boost() * fieldBoost); boolQueryBuilder.queryName(queryBuilder.queryName()); return boolQueryBuilder; } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index ff1b20e96e75c..7b6b346b5b142 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -41,7 +41,9 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde return queryBuilder; } - String fieldName = fieldsWithBoosts.keySet().stream().findFirst().get(); + String fieldName = fieldsWithBoosts.keySet().iterator().next(); + Float fieldBoost = fieldsWithBoosts.get(fieldName); + InferenceIndexInformationForField indexInformation = resolveIndicesForField(fieldName, resolvedIndices); if (indexInformation.getInferenceIndices().isEmpty()) { // No inference fields were identified, so return the original query. @@ -50,11 +52,11 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde // Combined case where the field name requested by this query contains both // semantic_text and non-inference fields, so we have to combine queries per index // containing each field type. - return buildCombinedInferenceAndNonInferenceQuery(queryBuilder, indexInformation); + return buildCombinedInferenceAndNonInferenceQuery(queryBuilder, indexInformation, fieldBoost); } else { // The only fields we've identified are inference fields (e.g. semantic_text), // so rewrite the entire query to work on a semantic_text field. - return buildInferenceQuery(queryBuilder, indexInformation); + return buildInferenceQuery(queryBuilder, indexInformation, fieldBoost); } } @@ -77,20 +79,23 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde * * @param queryBuilder {@link QueryBuilder} * @param indexInformation {@link InferenceIndexInformationForField} + * @param fieldBoost per field boost value * @return {@link QueryBuilder} */ - protected abstract QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation); + protected abstract QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost); /** * Builds a combined inference and non-inference query, * which separates the different queries into appropriate indices based on field type. * @param queryBuilder {@link QueryBuilder} * @param indexInformation {@link InferenceIndexInformationForField} + * @param fieldBoost per field boost value * @return {@link QueryBuilder} */ protected abstract QueryBuilder buildCombinedInferenceAndNonInferenceQuery( QueryBuilder queryBuilder, - InferenceIndexInformationForField indexInformation + InferenceIndexInformationForField indexInformation, + Float fieldBoost ); private InferenceIndexInformationForField resolveIndicesForField(String fieldName, ResolvedIndices resolvedIndices) { diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java index b735c223d75fe..ee80f233c3f32 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java @@ -41,7 +41,7 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation) { + protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { Map> inferenceIdsIndices = indexInformation.getInferenceIdsIndices(); QueryBuilder finalQueryBuilder; if (inferenceIdsIndices.size() == 1) { @@ -53,7 +53,7 @@ protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceI finalQueryBuilder = buildInferenceQueryWithMultipleInferenceIds(queryBuilder, inferenceIdsIndices); } finalQueryBuilder.queryName(queryBuilder.queryName()); - finalQueryBuilder.boost(queryBuilder.boost()); + finalQueryBuilder.boost(queryBuilder.boost() * fieldBoost); return finalQueryBuilder; } @@ -76,7 +76,8 @@ private QueryBuilder buildInferenceQueryWithMultipleInferenceIds( @Override protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( QueryBuilder queryBuilder, - InferenceIndexInformationForField indexInformation + InferenceIndexInformationForField indexInformation, + Float fieldBoost ) { assert (queryBuilder instanceof SparseVectorQueryBuilder); SparseVectorQueryBuilder sparseVectorQueryBuilder = (SparseVectorQueryBuilder) queryBuilder; @@ -106,7 +107,7 @@ protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( ) ); } - boolQueryBuilder.boost(queryBuilder.boost()); + boolQueryBuilder.boost(queryBuilder.boost() * fieldBoost); boolQueryBuilder.queryName(queryBuilder.queryName()); return boolQueryBuilder; } From 34ef4efd8cdef7629c705ae74ef7c03266f2ed4a Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Mon, 28 Jul 2025 12:06:58 -0400 Subject: [PATCH 3/9] linting --- .../queries/SemanticKnnVectorQueryRewriteInterceptor.java | 6 +++++- .../queries/SemanticMatchQueryRewriteInterceptor.java | 6 +++++- .../inference/queries/SemanticQueryRewriteInterceptor.java | 6 +++++- .../SemanticSparseVectorQueryRewriteInterceptor.java | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java index c34f686befa0f..05821effa2f3a 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticKnnVectorQueryRewriteInterceptor.java @@ -48,7 +48,11 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { + protected QueryBuilder buildInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ) { assert (queryBuilder instanceof KnnVectorQueryBuilder); KnnVectorQueryBuilder knnVectorQueryBuilder = (KnnVectorQueryBuilder) queryBuilder; Map> inferenceIdsIndices = indexInformation.getInferenceIdsIndices(); diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java index 7cac383705e22..915234f4a5a66 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMatchQueryRewriteInterceptor.java @@ -37,7 +37,11 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { + protected QueryBuilder buildInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ) { SemanticQueryBuilder semanticQueryBuilder = new SemanticQueryBuilder(indexInformation.fieldName(), getQuery(queryBuilder), false); semanticQueryBuilder.boost(queryBuilder.boost() * fieldBoost); semanticQueryBuilder.queryName(queryBuilder.queryName()); diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index 7b6b346b5b142..8bc4806b6eeae 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -82,7 +82,11 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde * @param fieldBoost per field boost value * @return {@link QueryBuilder} */ - protected abstract QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost); + protected abstract QueryBuilder buildInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ); /** * Builds a combined inference and non-inference query, diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java index ee80f233c3f32..228a07769c520 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticSparseVectorQueryRewriteInterceptor.java @@ -41,7 +41,11 @@ protected String getQuery(QueryBuilder queryBuilder) { } @Override - protected QueryBuilder buildInferenceQuery(QueryBuilder queryBuilder, InferenceIndexInformationForField indexInformation, Float fieldBoost) { + protected QueryBuilder buildInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ) { Map> inferenceIdsIndices = indexInformation.getInferenceIdsIndices(); QueryBuilder finalQueryBuilder; if (inferenceIdsIndices.size() == 1) { From 550eca672d6dc552614d4f1a5a85a7babfc5e5db Mon Sep 17 00:00:00 2001 From: Samiul Monir <150824886+Samiul-TheSoccerFan@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:12:20 -0400 Subject: [PATCH 4/9] Update docs/changelog/132026.yaml --- docs/changelog/132026.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/changelog/132026.yaml diff --git a/docs/changelog/132026.yaml b/docs/changelog/132026.yaml new file mode 100644 index 0000000000000..c4c51e928671b --- /dev/null +++ b/docs/changelog/132026.yaml @@ -0,0 +1,5 @@ +pr: 132026 +summary: "[Multi Match] Refactor `QueryIntercepter` Rewrite" +area: Relevance +type: enhancement +issues: [] From 0005033ed935aff80985f17c7369abc1ad1409c4 Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Mon, 28 Jul 2025 12:21:08 -0400 Subject: [PATCH 5/9] update changelog --- docs/changelog/132026.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog/132026.yaml b/docs/changelog/132026.yaml index c4c51e928671b..086a73bd0fffb 100644 --- a/docs/changelog/132026.yaml +++ b/docs/changelog/132026.yaml @@ -1,5 +1,5 @@ pr: 132026 -summary: "[Multi Match] Refactor `QueryIntercepter` Rewrite" +summary: "Refactor semantic query rewrite interceptors to support multi-field queries with boost handling" area: Relevance type: enhancement issues: [] From c1046c80cd3487e0b89b01d6ebe7f9b318f457e8 Mon Sep 17 00:00:00 2001 From: Samiul Monir <150824886+Samiul-TheSoccerFan@users.noreply.github.com> Date: Wed, 30 Jul 2025 15:05:50 -0400 Subject: [PATCH 6/9] Delete docs/changelog/132026.yaml --- docs/changelog/132026.yaml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 docs/changelog/132026.yaml diff --git a/docs/changelog/132026.yaml b/docs/changelog/132026.yaml deleted file mode 100644 index 086a73bd0fffb..0000000000000 --- a/docs/changelog/132026.yaml +++ /dev/null @@ -1,5 +0,0 @@ -pr: 132026 -summary: "Refactor semantic query rewrite interceptors to support multi-field queries with boost handling" -area: Relevance -type: enhancement -issues: [] From a1d3ef3005a09a3e5a1471ec0f9e904f714046bb Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Wed, 30 Jul 2025 15:46:34 -0400 Subject: [PATCH 7/9] adding multimatch intercepter class --- .../xpack/inference/InferencePlugin.java | 4 +- ...nticMultiMatchQueryRewriteInterceptor.java | 52 +++++++++++++++++++ .../SemanticQueryRewriteInterceptor.java | 14 +++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java index 374770ad25eb1..56fb57a1446fc 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java @@ -95,6 +95,7 @@ import org.elasticsearch.xpack.inference.mapper.SemanticTextFieldMapper; import org.elasticsearch.xpack.inference.queries.SemanticKnnVectorQueryRewriteInterceptor; import org.elasticsearch.xpack.inference.queries.SemanticMatchQueryRewriteInterceptor; +import org.elasticsearch.xpack.inference.queries.SemanticMultiMatchQueryRewriteInterceptor; import org.elasticsearch.xpack.inference.queries.SemanticQueryBuilder; import org.elasticsearch.xpack.inference.queries.SemanticSparseVectorQueryRewriteInterceptor; import org.elasticsearch.xpack.inference.rank.random.RandomRankBuilder; @@ -569,7 +570,8 @@ public List getQueryRewriteInterceptors() { return List.of( new SemanticKnnVectorQueryRewriteInterceptor(), new SemanticMatchQueryRewriteInterceptor(), - new SemanticSparseVectorQueryRewriteInterceptor() + new SemanticSparseVectorQueryRewriteInterceptor(), + new SemanticMultiMatchQueryRewriteInterceptor() ); } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java new file mode 100644 index 0000000000000..36006aee0451b --- /dev/null +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.inference.queries; + +import org.elasticsearch.index.query.MultiMatchQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; + +import java.util.Map; + +public class SemanticMultiMatchQueryRewriteInterceptor extends SemanticQueryRewriteInterceptor { + @Override + protected Map getFieldNamesWithBoosts(QueryBuilder queryBuilder) { + assert (queryBuilder instanceof MultiMatchQueryBuilder); + MultiMatchQueryBuilder multiMatchQueryBuilder = (MultiMatchQueryBuilder) queryBuilder; + return multiMatchQueryBuilder.fields(); + } + + @Override + protected String getQuery(QueryBuilder queryBuilder) { + assert (queryBuilder instanceof MultiMatchQueryBuilder); + MultiMatchQueryBuilder multiMatchQueryBuilder = (MultiMatchQueryBuilder) queryBuilder; + return (String) multiMatchQueryBuilder.value(); + } + + @Override + protected QueryBuilder buildInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ) { + return queryBuilder; + } + + @Override + protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( + QueryBuilder queryBuilder, + InferenceIndexInformationForField indexInformation, + Float fieldBoost + ) { + return queryBuilder; + } + + @Override + public String getQueryName() { + return MultiMatchQueryBuilder.NAME; + } +} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index 8bc4806b6eeae..7bf0983894891 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -12,6 +12,7 @@ import org.elasticsearch.cluster.metadata.InferenceFieldMetadata; import org.elasticsearch.index.mapper.IndexFieldMapper; import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.TermsQueryBuilder; @@ -41,6 +42,12 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde return queryBuilder; } + if (queryBuilder instanceof MultiMatchQueryBuilder) { + // This is a placeholder for the actual multi-field handling logic. + // Going forward, we will also send fieldNamesWithWeights and resolvedIndices + return handleMultiFieldQuery(queryBuilder); + } + String fieldName = fieldsWithBoosts.keySet().iterator().next(); Float fieldBoost = fieldsWithBoosts.get(fieldName); @@ -60,6 +67,13 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde } } + /** + * Handle multi-field queries + */ + private QueryBuilder handleMultiFieldQuery(QueryBuilder queryBuilder) { + return queryBuilder; + } + /** * Extracts field names and their associated boost values from the query builder. * From 053ab13a0b0fbc7051483215f6767cb0bf1ad23b Mon Sep 17 00:00:00 2001 From: Samiul Monir Date: Thu, 31 Jul 2025 10:53:23 -0400 Subject: [PATCH 8/9] working commit --- ...nticMultiMatchQueryRewriteInterceptor.java | 65 +++++++++++++- .../SemanticQueryRewriteInterceptor.java | 87 +++++++++++++++++-- 2 files changed, 144 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java index 36006aee0451b..af879f788d391 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java @@ -7,8 +7,10 @@ package org.elasticsearch.xpack.inference.queries; +import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import java.util.Map; @@ -33,7 +35,14 @@ protected QueryBuilder buildInferenceQuery( InferenceIndexInformationForField indexInformation, Float fieldBoost ) { - return queryBuilder; + SemanticQueryBuilder semanticQueryBuilder = new SemanticQueryBuilder( + indexInformation.fieldName(), + getQuery(queryBuilder), + false + ); + semanticQueryBuilder.boost(queryBuilder.boost() * fieldBoost); + semanticQueryBuilder.queryName(queryBuilder.queryName()); + return semanticQueryBuilder; } @Override @@ -42,11 +51,63 @@ protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( InferenceIndexInformationForField indexInformation, Float fieldBoost ) { - return queryBuilder; + BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); + + // Add the semantic part for inference indices + boolQueryBuilder.should( + createSemanticSubQuery( + indexInformation.getInferenceIndices(), + indexInformation.fieldName(), + getQuery(queryBuilder) + ) + ); + + // Add the non-semantic part for non-inference indices + boolQueryBuilder.should( + createSubQueryForIndices( + indexInformation.nonInferenceIndices(), + QueryBuilders.matchQuery(indexInformation.fieldName(), getQuery(queryBuilder)) + ) + ); + + // Apply the field boost + boolQueryBuilder.boost(queryBuilder.boost() * fieldBoost); + boolQueryBuilder.queryName(queryBuilder.queryName()); + + return boolQueryBuilder; } @Override public String getQueryName() { return MultiMatchQueryBuilder.NAME; } + + + public static void copyMultiMatchConfiguration(MultiMatchQueryBuilder source, MultiMatchQueryBuilder target) { + target.type(source.type()); + target.operator(source.operator()); + if (source.analyzer() != null) { + target.analyzer(source.analyzer()); + } + target.slop(source.slop()); + if (source.fuzziness() != null) { + target.fuzziness(source.fuzziness()); + } + target.prefixLength(source.prefixLength()); + target.maxExpansions(source.maxExpansions()); + if (source.minimumShouldMatch() != null) { + target.minimumShouldMatch(source.minimumShouldMatch()); + } + if (source.fuzzyRewrite() != null) { + target.fuzzyRewrite(source.fuzzyRewrite()); + } + if (source.tieBreaker() != null) { + target.tieBreaker(source.tieBreaker()); + } + target.lenient(source.lenient()); + target.zeroTermsQuery(source.zeroTermsQuery()); + target.autoGenerateSynonymsPhraseQuery(source.autoGenerateSynonymsPhraseQuery()); + target.fuzzyTranspositions(source.fuzzyTranspositions()); + } + } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index 7bf0983894891..f7b40f39d73de 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -14,6 +14,7 @@ import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.plugins.internal.rewriter.QueryRewriteInterceptor; @@ -43,9 +44,7 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde } if (queryBuilder instanceof MultiMatchQueryBuilder) { - // This is a placeholder for the actual multi-field handling logic. - // Going forward, we will also send fieldNamesWithWeights and resolvedIndices - return handleMultiFieldQuery(queryBuilder); + return handleMultiFieldQuery(queryBuilder, fieldsWithBoosts, resolvedIndices); } String fieldName = fieldsWithBoosts.keySet().iterator().next(); @@ -68,10 +67,86 @@ public QueryBuilder interceptAndRewrite(QueryRewriteContext context, QueryBuilde } /** - * Handle multi-field queries + * Handle multi-field queries by analyzing each field for semantic_text fields + * and creating appropriate queries */ - private QueryBuilder handleMultiFieldQuery(QueryBuilder queryBuilder) { - return queryBuilder; + private QueryBuilder handleMultiFieldQuery( + QueryBuilder queryBuilder, + Map fieldsWithBoosts, + ResolvedIndices resolvedIndices + ) { + assert (queryBuilder instanceof MultiMatchQueryBuilder); + MultiMatchQueryBuilder multiMatchQueryBuilder = (MultiMatchQueryBuilder) queryBuilder; + String queryText = getQuery(queryBuilder); + + boolean hasSemanticField = false; + Map fieldInfoMap = new HashMap<>(); + + // Analyze each field to determine if it's semantic or not + for (Map.Entry fieldEntry : fieldsWithBoosts.entrySet()) { + String fieldName = fieldEntry.getKey(); + InferenceIndexInformationForField indexInfo = resolveIndicesForField(fieldName, resolvedIndices); + fieldInfoMap.put(fieldName, indexInfo); + + if (indexInfo.getInferenceIndices().isEmpty() ==false) { + hasSemanticField = true; + } + } + + // If no semantic fields were found, return the original query + if (hasSemanticField == false) { + return queryBuilder; + } + + // Create a combined query + BoolQueryBuilder combinedQuery = new BoolQueryBuilder(); + + // Apply the MultiMatch type and tie-breaker + MultiMatchQueryBuilder.Type type = multiMatchQueryBuilder.type(); + Float tieBreaker = multiMatchQueryBuilder.tieBreaker(); + boolean shouldUseTieBreaker = (tieBreaker != null); + + + Map nonSemanticFields = new HashMap<>(); + for (Map.Entry fieldEntry : fieldsWithBoosts.entrySet()) { + String fieldName = fieldEntry.getKey(); + Float fieldBoost = fieldEntry.getValue(); + InferenceIndexInformationForField indexInfo = fieldInfoMap.get(fieldName); + + if (indexInfo.getInferenceIndices().isEmpty()) { + nonSemanticFields.put(fieldName, fieldBoost); + } else if (indexInfo.nonInferenceIndices().isEmpty()) { + QueryBuilder semanticQuery = buildInferenceQuery(queryBuilder, indexInfo, fieldBoost); + if (shouldUseTieBreaker) { + semanticQuery.boost(semanticQuery.boost() * (type == MultiMatchQueryBuilder.Type.MOST_FIELDS ? 1.0f : tieBreaker)); + } + combinedQuery.should(semanticQuery); + } else { + QueryBuilder mixedQuery = buildCombinedInferenceAndNonInferenceQuery(queryBuilder, indexInfo, fieldBoost); + if (shouldUseTieBreaker) { + mixedQuery.boost(mixedQuery.boost() * (type == MultiMatchQueryBuilder.Type.MOST_FIELDS ? 1.0f : tieBreaker)); + } + combinedQuery.should(mixedQuery); + } + } + + if (nonSemanticFields.isEmpty() == false) { + MultiMatchQueryBuilder nonSemanticQuery = QueryBuilders.multiMatchQuery(queryText); + nonSemanticQuery.fields(nonSemanticFields); + + SemanticMultiMatchQueryRewriteInterceptor.copyMultiMatchConfiguration(multiMatchQueryBuilder, nonSemanticQuery); + + if (shouldUseTieBreaker) { + nonSemanticQuery.boost(nonSemanticQuery.boost() * (type == MultiMatchQueryBuilder.Type.MOST_FIELDS ? 1.0f : tieBreaker)); + } + + combinedQuery.should(nonSemanticQuery); + } + + combinedQuery.boost(queryBuilder.boost()); + combinedQuery.queryName(queryBuilder.queryName()); + + return combinedQuery; } /** From 75d04200340aa9f444e29a8f5b3ff3ef58476d7a Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Thu, 31 Jul 2025 15:02:57 +0000 Subject: [PATCH 9/9] [CI] Auto commit changes from spotless --- .../SemanticMultiMatchQueryRewriteInterceptor.java | 13 ++----------- .../queries/SemanticQueryRewriteInterceptor.java | 3 +-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java index af879f788d391..095e61b25dca8 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticMultiMatchQueryRewriteInterceptor.java @@ -35,11 +35,7 @@ protected QueryBuilder buildInferenceQuery( InferenceIndexInformationForField indexInformation, Float fieldBoost ) { - SemanticQueryBuilder semanticQueryBuilder = new SemanticQueryBuilder( - indexInformation.fieldName(), - getQuery(queryBuilder), - false - ); + SemanticQueryBuilder semanticQueryBuilder = new SemanticQueryBuilder(indexInformation.fieldName(), getQuery(queryBuilder), false); semanticQueryBuilder.boost(queryBuilder.boost() * fieldBoost); semanticQueryBuilder.queryName(queryBuilder.queryName()); return semanticQueryBuilder; @@ -55,11 +51,7 @@ protected QueryBuilder buildCombinedInferenceAndNonInferenceQuery( // Add the semantic part for inference indices boolQueryBuilder.should( - createSemanticSubQuery( - indexInformation.getInferenceIndices(), - indexInformation.fieldName(), - getQuery(queryBuilder) - ) + createSemanticSubQuery(indexInformation.getInferenceIndices(), indexInformation.fieldName(), getQuery(queryBuilder)) ); // Add the non-semantic part for non-inference indices @@ -82,7 +74,6 @@ public String getQueryName() { return MultiMatchQueryBuilder.NAME; } - public static void copyMultiMatchConfiguration(MultiMatchQueryBuilder source, MultiMatchQueryBuilder target) { target.type(source.type()); target.operator(source.operator()); diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java index f7b40f39d73de..d0c2a5b8d1632 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/queries/SemanticQueryRewriteInterceptor.java @@ -88,7 +88,7 @@ private QueryBuilder handleMultiFieldQuery( InferenceIndexInformationForField indexInfo = resolveIndicesForField(fieldName, resolvedIndices); fieldInfoMap.put(fieldName, indexInfo); - if (indexInfo.getInferenceIndices().isEmpty() ==false) { + if (indexInfo.getInferenceIndices().isEmpty() == false) { hasSemanticField = true; } } @@ -106,7 +106,6 @@ private QueryBuilder handleMultiFieldQuery( Float tieBreaker = multiMatchQueryBuilder.tieBreaker(); boolean shouldUseTieBreaker = (tieBreaker != null); - Map nonSemanticFields = new HashMap<>(); for (Map.Entry fieldEntry : fieldsWithBoosts.entrySet()) { String fieldName = fieldEntry.getKey();