Skip to content

Commit 5b0373c

Browse files
committed
Add integration tests for filter_type=post|efficient and spotless formatting
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent af6e2ba commit 5b0373c

5 files changed

Lines changed: 167 additions & 31 deletions

File tree

integ-test/src/test/java/org/opensearch/sql/sql/VectorSearchIT.java

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,102 @@ public void testExplainLimitWithinKSucceeds() throws IOException {
315315

316316
assertTrue("Explain should succeed with LIMIT <= k:\n" + explain, explain.contains("wrapper"));
317317
}
318+
319+
// ── filter_type validation and explain ─────────────────────────────
320+
321+
@Test
322+
public void testExplainFilterTypePostProducesBoolQuery() throws IOException {
323+
String explain =
324+
explainQuery(
325+
"SELECT v._id, v._score "
326+
+ "FROM vectorSearch(table='"
327+
+ TEST_INDEX
328+
+ "', field='embedding', "
329+
+ "vector='[1.0, 2.0, 3.0]', option='k=10,filter_type=post') AS v "
330+
+ "WHERE v.state = 'TX' "
331+
+ "LIMIT 10");
332+
333+
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
334+
assertTrue("Explain should contain must:\n" + explain, explain.contains("must"));
335+
assertTrue("Explain should contain filter:\n" + explain, explain.contains("filter"));
336+
}
337+
338+
@Test
339+
public void testExplainFilterTypeEfficientProducesKnnWithFilter() throws IOException {
340+
String explain =
341+
explainQuery(
342+
"SELECT v._id, v._score "
343+
+ "FROM vectorSearch(table='"
344+
+ TEST_INDEX
345+
+ "', field='embedding', "
346+
+ "vector='[1.0, 2.0]', option='k=5,filter_type=efficient') AS v "
347+
+ "WHERE v.state = 'TX' "
348+
+ "LIMIT 5");
349+
350+
// Efficient mode: knn rebuilt with filter inside, wrapped in WrapperQueryBuilder
351+
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
352+
}
353+
354+
@Test
355+
public void testFilterTypeEfficientWithoutWhereRejects() throws IOException {
356+
ResponseException ex =
357+
expectThrows(
358+
ResponseException.class,
359+
() ->
360+
executeQuery(
361+
"SELECT v._id FROM vectorSearch(table='"
362+
+ TEST_INDEX
363+
+ "', field='embedding', "
364+
+ "vector='[1.0, 2.0]', option='k=5,filter_type=efficient') AS v "
365+
+ "LIMIT 5"));
366+
367+
assertThat(ex.getMessage(), containsString("filter_type requires a pushdownable WHERE clause"));
368+
}
369+
370+
@Test
371+
public void testFilterTypePostWithoutWhereRejects() throws IOException {
372+
ResponseException ex =
373+
expectThrows(
374+
ResponseException.class,
375+
() ->
376+
executeQuery(
377+
"SELECT v._id FROM vectorSearch(table='"
378+
+ TEST_INDEX
379+
+ "', field='embedding', "
380+
+ "vector='[1.0, 2.0]', option='k=5,filter_type=post') AS v "
381+
+ "LIMIT 5"));
382+
383+
assertThat(ex.getMessage(), containsString("filter_type requires a pushdownable WHERE clause"));
384+
}
385+
386+
@Test
387+
public void testInvalidFilterTypeRejects() throws IOException {
388+
ResponseException ex =
389+
expectThrows(
390+
ResponseException.class,
391+
() ->
392+
executeQuery(
393+
"SELECT v._id FROM vectorSearch(table='t', field='f', "
394+
+ "vector='[1.0]', option='k=5,filter_type=bogus') AS v"));
395+
396+
assertThat(ex.getMessage(), containsString("filter_type must be one of"));
397+
}
398+
399+
@Test
400+
public void testEfficientFilterWithOrderByScoreDescSucceeds() throws IOException {
401+
String explain =
402+
explainQuery(
403+
"SELECT v._id, v._score "
404+
+ "FROM vectorSearch(table='"
405+
+ TEST_INDEX
406+
+ "', field='embedding', "
407+
+ "vector='[1.0, 2.0]', option='k=5,filter_type=efficient') AS v "
408+
+ "WHERE v.state = 'TX' "
409+
+ "ORDER BY v._score DESC "
410+
+ "LIMIT 5");
411+
412+
assertTrue(
413+
"Explain should succeed with efficient + ORDER BY _score DESC:\n" + explain,
414+
explain.contains("wrapper"));
415+
}
318416
}

opensearch/src/main/java/org/opensearch/sql/opensearch/storage/VectorSearchIndex.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,12 @@ public TableScanBuilder createScanBuilder() {
7070

7171
var queryBuilder =
7272
new VectorSearchQueryBuilder(
73-
requestBuilder, buildKnnQuery(), options,
74-
effectiveFilterType, filterTypeExplicit, rebuildWithFilter);
73+
requestBuilder,
74+
buildKnnQuery(),
75+
options,
76+
effectiveFilterType,
77+
filterTypeExplicit,
78+
rebuildWithFilter);
7579
requestBuilder.pushDownTrackedScore(true);
7680

7781
// Default size policy: LIMIT pushdown will further reduce if present.

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ public VectorSearchQueryBuilder(
6464

6565
/** Backward-compatible constructor — defaults to POST, not explicit. */
6666
public VectorSearchQueryBuilder(
67-
OpenSearchRequestBuilder requestBuilder,
68-
QueryBuilder knnQuery,
69-
Map<String, String> options) {
67+
OpenSearchRequestBuilder requestBuilder, QueryBuilder knnQuery, Map<String, String> options) {
7068
this(requestBuilder, knnQuery, options, FilterType.POST, false, null);
7169
}
7270

@@ -131,8 +129,7 @@ public boolean pushDownSort(LogicalSort sort) {
131129
@Override
132130
public OpenSearchRequestBuilder build() {
133131
if (filterTypeExplicit && !filterPushed) {
134-
throw new ExpressionEvaluationException(
135-
"filter_type requires a pushdownable WHERE clause");
132+
throw new ExpressionEvaluationException("filter_type requires a pushdownable WHERE clause");
136133
}
137134
return super.build();
138135
}

opensearch/src/test/java/org/opensearch/sql/opensearch/storage/VectorSearchIndexTest.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,13 @@ void buildKnnQueryJsonNonNumericOptionRenderedQuoted() {
144144
void buildKnnQueryJsonWithFilterEmbeds() {
145145
VectorSearchIndex index =
146146
new VectorSearchIndex(
147-
client, settings, "test-index", "embedding",
148-
new float[] {1.0f, 2.0f}, Map.of("k", "5"), FilterType.EFFICIENT);
147+
client,
148+
settings,
149+
"test-index",
150+
"embedding",
151+
new float[] {1.0f, 2.0f},
152+
Map.of("k", "5"),
153+
FilterType.EFFICIENT);
149154

150155
String filterJson = "{\"term\":{\"city\":{\"value\":\"Miami\"}}}";
151156
String json = index.buildKnnQueryJson(filterJson);
@@ -160,8 +165,13 @@ void buildKnnQueryJsonWithFilterEmbeds() {
160165
void buildKnnQueryJsonWithFilterRadial() {
161166
VectorSearchIndex index =
162167
new VectorSearchIndex(
163-
client, settings, "test-index", "embedding",
164-
new float[] {1.0f}, Map.of("max_distance", "10.5"), FilterType.EFFICIENT);
168+
client,
169+
settings,
170+
"test-index",
171+
"embedding",
172+
new float[] {1.0f},
173+
Map.of("max_distance", "10.5"),
174+
FilterType.EFFICIENT);
165175

166176
String filterJson = "{\"range\":{\"rating\":{\"gte\":4.0}}}";
167177
String json = index.buildKnnQueryJson(filterJson);
@@ -174,8 +184,13 @@ void buildKnnQueryJsonWithFilterRadial() {
174184
void buildKnnQueryJsonNullFilterProducesBaseJson() {
175185
VectorSearchIndex index =
176186
new VectorSearchIndex(
177-
client, settings, "test-index", "embedding",
178-
new float[] {1.0f}, Map.of("k", "5"), null);
187+
client,
188+
settings,
189+
"test-index",
190+
"embedding",
191+
new float[] {1.0f},
192+
Map.of("k", "5"),
193+
null);
179194

180195
String json = index.buildKnnQueryJson(null);
181196
String baseJson = index.buildKnnQueryJson();
@@ -191,8 +206,13 @@ void buildKnnQueryJsonExcludesFilterType() {
191206

192207
VectorSearchIndex index =
193208
new VectorSearchIndex(
194-
client, settings, "test-index", "embedding",
195-
new float[] {1.0f}, options, FilterType.EFFICIENT);
209+
client,
210+
settings,
211+
"test-index",
212+
"embedding",
213+
new float[] {1.0f},
214+
options,
215+
FilterType.EFFICIENT);
196216

197217
String json = index.buildKnnQueryJson();
198218
assertFalse(json.contains("filter_type"), "filter_type should not appear in knn JSON");

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

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,12 @@ void pushDownFilterEfficientPlacesInsideKnn() {
279279
whereQuery -> new WrapperQueryBuilder("{\"knn\":{\"filter\":\"embedded\"}}");
280280
var builder =
281281
new VectorSearchQueryBuilder(
282-
requestBuilder, knnQuery, Map.of("k", "5"),
283-
FilterType.EFFICIENT, true, rebuildWithFilter);
282+
requestBuilder,
283+
knnQuery,
284+
Map.of("k", "5"),
285+
FilterType.EFFICIENT,
286+
true,
287+
rebuildWithFilter);
284288

285289
var condition = DSL.equal(new ReferenceExpression("city", STRING), DSL.literal("Miami"));
286290
var dummyChild = new LogicalValues(Collections.emptyList());
@@ -301,8 +305,7 @@ void pushDownFilterExplicitPostProducesBool() {
301305
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
302306
var builder =
303307
new VectorSearchQueryBuilder(
304-
requestBuilder, knnQuery, Map.of("k", "5"),
305-
FilterType.POST, true, null);
308+
requestBuilder, knnQuery, Map.of("k", "5"), FilterType.POST, true, null);
306309

307310
var condition = DSL.equal(new ReferenceExpression("name", STRING), DSL.literal("John"));
308311
var dummyChild = new LogicalValues(Collections.emptyList());
@@ -326,8 +329,7 @@ void buildRejectsExplicitFilterTypePostWithoutWhere() {
326329
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
327330
var builder =
328331
new VectorSearchQueryBuilder(
329-
requestBuilder, knnQuery, Map.of("k", "5"),
330-
FilterType.POST, true, null);
332+
requestBuilder, knnQuery, Map.of("k", "5"), FilterType.POST, true, null);
331333

332334
ExpressionEvaluationException ex =
333335
assertThrows(ExpressionEvaluationException.class, builder::build);
@@ -342,8 +344,12 @@ void buildRejectsExplicitFilterTypeEfficientWithoutWhere() {
342344
whereQuery -> new WrapperQueryBuilder("{\"knn\":{\"filter\":\"embedded\"}}");
343345
var builder =
344346
new VectorSearchQueryBuilder(
345-
requestBuilder, knnQuery, Map.of("k", "5"),
346-
FilterType.EFFICIENT, true, rebuildWithFilter);
347+
requestBuilder,
348+
knnQuery,
349+
Map.of("k", "5"),
350+
FilterType.EFFICIENT,
351+
true,
352+
rebuildWithFilter);
347353

348354
ExpressionEvaluationException ex =
349355
assertThrows(ExpressionEvaluationException.class, builder::build);
@@ -366,8 +372,7 @@ void buildSucceedsWithFilterTypeAndPushedWhere() {
366372
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
367373
var builder =
368374
new VectorSearchQueryBuilder(
369-
requestBuilder, knnQuery, Map.of("k", "5"),
370-
FilterType.POST, true, null);
375+
requestBuilder, knnQuery, Map.of("k", "5"), FilterType.POST, true, null);
371376

372377
var condition = DSL.equal(new ReferenceExpression("name", STRING), DSL.literal("John"));
373378
var dummyChild = new LogicalValues(Collections.emptyList());
@@ -387,8 +392,12 @@ void pushDownLimitExceedingKThrowsUnderEfficientMode() {
387392
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
388393
var builder =
389394
new VectorSearchQueryBuilder(
390-
requestBuilder, knnQuery, Map.of("k", "5"),
391-
FilterType.EFFICIENT, true, rebuildWithFilter);
395+
requestBuilder,
396+
knnQuery,
397+
Map.of("k", "5"),
398+
FilterType.EFFICIENT,
399+
true,
400+
rebuildWithFilter);
392401

393402
var dummyChild = new LogicalValues(Collections.emptyList());
394403
var limit = new LogicalLimit(dummyChild, 10, 0);
@@ -406,8 +415,12 @@ void pushDownSortScoreDescAcceptedUnderEfficientMode() {
406415
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
407416
var builder =
408417
new VectorSearchQueryBuilder(
409-
requestBuilder, knnQuery, Map.of("k", "5"),
410-
FilterType.EFFICIENT, true, rebuildWithFilter);
418+
requestBuilder,
419+
knnQuery,
420+
Map.of("k", "5"),
421+
FilterType.EFFICIENT,
422+
true,
423+
rebuildWithFilter);
411424

412425
var dummyChild = new LogicalValues(Collections.emptyList());
413426
var sort =
@@ -430,8 +443,12 @@ void pushDownSortNonScoreRejectedUnderEfficientMode() {
430443
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
431444
var builder =
432445
new VectorSearchQueryBuilder(
433-
requestBuilder, knnQuery, Map.of("k", "5"),
434-
FilterType.EFFICIENT, true, rebuildWithFilter);
446+
requestBuilder,
447+
knnQuery,
448+
Map.of("k", "5"),
449+
FilterType.EFFICIENT,
450+
true,
451+
rebuildWithFilter);
435452

436453
var dummyChild = new LogicalValues(Collections.emptyList());
437454
var sort =

0 commit comments

Comments
 (0)