Skip to content

Commit 0f15462

Browse files
committed
Check if field caps uses a semantic query as a filter
1 parent 67bf0a8 commit 0f15462

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
import org.elasticsearch.common.Strings;
1919
import org.elasticsearch.common.io.stream.StreamInput;
2020
import org.elasticsearch.common.io.stream.StreamOutput;
21+
import org.elasticsearch.index.query.BoolQueryBuilder;
22+
import org.elasticsearch.index.query.BoostingQueryBuilder;
23+
import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
24+
import org.elasticsearch.index.query.DisMaxQueryBuilder;
25+
import org.elasticsearch.index.query.NestedQueryBuilder;
2126
import org.elasticsearch.index.query.QueryBuilder;
27+
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
2228
import org.elasticsearch.tasks.CancellableTask;
2329
import org.elasticsearch.tasks.Task;
2430
import org.elasticsearch.tasks.TaskId;
@@ -268,9 +274,53 @@ public ActionRequestValidationException validate() {
268274
if (fields == null || fields.length == 0) {
269275
validationException = ValidateActions.addValidationError("no fields specified", validationException);
270276
}
277+
278+
// Band-aid fix for https://github.com/elastic/elasticsearch/issues/116106.
279+
// Semantic queries are high-recall queries, making them poor filters and effectively the same as an exists query when used in that
280+
// context.
281+
if (containsSemanticQuery(indexFilter)) {
282+
validationException = ValidateActions.addValidationError(
283+
"index filter cannot contain semantic queries. Use an exists query instead.",
284+
validationException
285+
);
286+
}
287+
271288
return validationException;
272289
}
273290

291+
/**
292+
* Recursively checks if a query builder contains any semantic queries
293+
*/
294+
private static boolean containsSemanticQuery(QueryBuilder queryBuilder) {
295+
boolean containsSemanticQuery = false;
296+
297+
if (queryBuilder == null) {
298+
return containsSemanticQuery;
299+
}
300+
301+
if ("semantic".equals(queryBuilder.getWriteableName())) {
302+
containsSemanticQuery = true;
303+
} else if (queryBuilder instanceof BoolQueryBuilder boolQuery) {
304+
return boolQuery.must().stream().anyMatch(FieldCapabilitiesRequest::containsSemanticQuery)
305+
|| boolQuery.mustNot().stream().anyMatch(FieldCapabilitiesRequest::containsSemanticQuery)
306+
|| boolQuery.should().stream().anyMatch(FieldCapabilitiesRequest::containsSemanticQuery)
307+
|| boolQuery.filter().stream().anyMatch(FieldCapabilitiesRequest::containsSemanticQuery);
308+
} else if (queryBuilder instanceof DisMaxQueryBuilder disMaxQuery) {
309+
containsSemanticQuery = disMaxQuery.innerQueries().stream().anyMatch(FieldCapabilitiesRequest::containsSemanticQuery);
310+
} else if (queryBuilder instanceof NestedQueryBuilder nestedQuery) {
311+
containsSemanticQuery = containsSemanticQuery(nestedQuery.query());
312+
} else if (queryBuilder instanceof BoostingQueryBuilder boostingQuery) {
313+
containsSemanticQuery = containsSemanticQuery(boostingQuery.positiveQuery())
314+
|| containsSemanticQuery(boostingQuery.negativeQuery());
315+
} else if (queryBuilder instanceof ConstantScoreQueryBuilder constantScoreQuery) {
316+
containsSemanticQuery = containsSemanticQuery(constantScoreQuery.innerQuery());
317+
} else if (queryBuilder instanceof FunctionScoreQueryBuilder functionScoreQuery) {
318+
containsSemanticQuery = containsSemanticQuery(functionScoreQuery.query());
319+
}
320+
321+
return containsSemanticQuery;
322+
}
323+
274324
@Override
275325
public boolean equals(Object o) {
276326
if (this == o) return true;

0 commit comments

Comments
 (0)