Skip to content

Commit bf1f9e2

Browse files
committed
Handle non-pushdownable WHERE with explicit filter_type
pushDownFilter() did not catch ScriptQueryUnSupportedException, so non-pushdownable filters (e.g. struct-type fields) would propagate a raw internal exception instead of a clean SQL-layer error. With explicit filter_type: throw a clear error explaining the WHERE clause cannot be pushed down for the requested filter placement. Without explicit filter_type: return false to fall back to in-memory filtering, matching the base class behavior. Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent fff5c02 commit bf1f9e2

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/VectorSearchQueryBuilder.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.opensearch.sql.opensearch.request.OpenSearchRequestBuilder;
2020
import org.opensearch.sql.opensearch.storage.FilterType;
2121
import org.opensearch.sql.opensearch.storage.script.filter.FilterQueryBuilder;
22+
import org.opensearch.sql.opensearch.storage.script.filter.FilterQueryBuilder.ScriptQueryUnSupportedException;
2223
import org.opensearch.sql.opensearch.storage.serde.DefaultExpressionSerializer;
2324
import org.opensearch.sql.planner.logical.LogicalFilter;
2425
import org.opensearch.sql.planner.logical.LogicalLimit;
@@ -73,7 +74,19 @@ public VectorSearchQueryBuilder(
7374
public boolean pushDownFilter(LogicalFilter filter) {
7475
FilterQueryBuilder queryBuilder = new FilterQueryBuilder(new DefaultExpressionSerializer());
7576
Expression queryCondition = filter.getCondition();
76-
QueryBuilder whereQuery = queryBuilder.build(queryCondition);
77+
QueryBuilder whereQuery;
78+
try {
79+
whereQuery = queryBuilder.build(queryCondition);
80+
} catch (ScriptQueryUnSupportedException e) {
81+
if (filterTypeExplicit) {
82+
throw new ExpressionEvaluationException(
83+
"filter_type requires a pushdownable WHERE clause, but the given condition"
84+
+ " cannot be pushed down: "
85+
+ e.getMessage());
86+
}
87+
// Default mode: fall back to in-memory filtering (matches base class behavior)
88+
return false;
89+
}
7790
filterPushed = true;
7891

7992
if (filterType == FilterType.EFFICIENT) {

opensearch/src/test/java/org/opensearch/sql/opensearch/storage/scan/VectorSearchQueryBuilderTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.opensearch.sql.opensearch.storage.scan;
77

88
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertFalse;
910
import static org.junit.jupiter.api.Assertions.assertNotNull;
1011
import static org.junit.jupiter.api.Assertions.assertThrows;
1112
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -536,6 +537,48 @@ void pushDownSortNonScoreRejectedUnderEfficientMode() {
536537
assertTrue(ex.getMessage().contains("unsupported sort expression"));
537538
}
538539

540+
// ── Non-pushdownable filter handling ──────────────────────────────────
541+
542+
@Test
543+
void pushDownFilterNonPushdownableWithExplicitFilterTypeThrows() {
544+
var requestBuilder = createRequestBuilder();
545+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
546+
var builder =
547+
new VectorSearchQueryBuilder(
548+
requestBuilder, knnQuery, Map.of("k", "5"), FilterType.POST, true, null);
549+
550+
// STRUCT = STRUCT triggers ScriptQueryUnSupportedException in FilterQueryBuilder
551+
var condition =
552+
DSL.equal(
553+
new ReferenceExpression("nested_field", ExprCoreType.STRUCT),
554+
new ReferenceExpression("other_field", ExprCoreType.STRUCT));
555+
var dummyChild = new LogicalValues(Collections.emptyList());
556+
var filter = new LogicalFilter(dummyChild, condition);
557+
558+
ExpressionEvaluationException ex =
559+
assertThrows(ExpressionEvaluationException.class, () -> builder.pushDownFilter(filter));
560+
assertTrue(ex.getMessage().contains("filter_type requires a pushdownable WHERE clause"));
561+
assertTrue(ex.getMessage().contains("cannot be pushed down"));
562+
}
563+
564+
@Test
565+
void pushDownFilterNonPushdownableWithoutExplicitFilterTypeFallsBack() {
566+
var requestBuilder = createRequestBuilder();
567+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
568+
var builder = new VectorSearchQueryBuilder(requestBuilder, knnQuery, Map.of("k", "5"));
569+
570+
// STRUCT = STRUCT triggers ScriptQueryUnSupportedException in FilterQueryBuilder
571+
var condition =
572+
DSL.equal(
573+
new ReferenceExpression("nested_field", ExprCoreType.STRUCT),
574+
new ReferenceExpression("other_field", ExprCoreType.STRUCT));
575+
var dummyChild = new LogicalValues(Collections.emptyList());
576+
var filter = new LogicalFilter(dummyChild, condition);
577+
578+
boolean pushed = builder.pushDownFilter(filter);
579+
assertFalse(pushed, "Non-pushdownable filter should return false for in-memory fallback");
580+
}
581+
539582
@Test
540583
void buildSucceedsRadialWithSortEmbeddedLimit() {
541584
var requestBuilder = createRequestBuilder();

0 commit comments

Comments
 (0)