Skip to content

Commit af6e2ba

Browse files
committed
Add build-time validation and regression tests for LIMIT/sort under efficient mode
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent fdd1810 commit af6e2ba

1 file changed

Lines changed: 130 additions & 0 deletions

File tree

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

Lines changed: 130 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.assertNotNull;
910
import static org.junit.jupiter.api.Assertions.assertThrows;
1011
import static org.junit.jupiter.api.Assertions.assertTrue;
1112
import static org.mockito.Mockito.mock;
@@ -317,6 +318,135 @@ void pushDownFilterExplicitPostProducesBool() {
317318
assertEquals(1, boolQuery.filter().size());
318319
}
319320

321+
// ── Build-time validation ────────────────────────────────────────────
322+
323+
@Test
324+
void buildRejectsExplicitFilterTypePostWithoutWhere() {
325+
var requestBuilder = createRequestBuilder();
326+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
327+
var builder =
328+
new VectorSearchQueryBuilder(
329+
requestBuilder, knnQuery, Map.of("k", "5"),
330+
FilterType.POST, true, null);
331+
332+
ExpressionEvaluationException ex =
333+
assertThrows(ExpressionEvaluationException.class, builder::build);
334+
assertTrue(ex.getMessage().contains("filter_type requires a pushdownable WHERE clause"));
335+
}
336+
337+
@Test
338+
void buildRejectsExplicitFilterTypeEfficientWithoutWhere() {
339+
var requestBuilder = createRequestBuilder();
340+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
341+
Function<QueryBuilder, QueryBuilder> rebuildWithFilter =
342+
whereQuery -> new WrapperQueryBuilder("{\"knn\":{\"filter\":\"embedded\"}}");
343+
var builder =
344+
new VectorSearchQueryBuilder(
345+
requestBuilder, knnQuery, Map.of("k", "5"),
346+
FilterType.EFFICIENT, true, rebuildWithFilter);
347+
348+
ExpressionEvaluationException ex =
349+
assertThrows(ExpressionEvaluationException.class, builder::build);
350+
assertTrue(ex.getMessage().contains("filter_type requires a pushdownable WHERE clause"));
351+
}
352+
353+
@Test
354+
void buildSucceedsWithNoFilterTypeAndNoWhere() {
355+
var requestBuilder = createRequestBuilder();
356+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
357+
var builder = new VectorSearchQueryBuilder(requestBuilder, knnQuery, Map.of("k", "5"));
358+
359+
OpenSearchRequestBuilder result = builder.build();
360+
assertNotNull(result);
361+
}
362+
363+
@Test
364+
void buildSucceedsWithFilterTypeAndPushedWhere() {
365+
var requestBuilder = createRequestBuilder();
366+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
367+
var builder =
368+
new VectorSearchQueryBuilder(
369+
requestBuilder, knnQuery, Map.of("k", "5"),
370+
FilterType.POST, true, null);
371+
372+
var condition = DSL.equal(new ReferenceExpression("name", STRING), DSL.literal("John"));
373+
var dummyChild = new LogicalValues(Collections.emptyList());
374+
builder.pushDownFilter(new LogicalFilter(dummyChild, condition));
375+
376+
OpenSearchRequestBuilder result = builder.build();
377+
assertNotNull(result);
378+
}
379+
380+
// ── Regression: LIMIT and sort invariants under efficient mode ──────
381+
382+
@Test
383+
void pushDownLimitExceedingKThrowsUnderEfficientMode() {
384+
var requestBuilder = createRequestBuilder();
385+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
386+
Function<QueryBuilder, QueryBuilder> rebuildWithFilter =
387+
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
388+
var builder =
389+
new VectorSearchQueryBuilder(
390+
requestBuilder, knnQuery, Map.of("k", "5"),
391+
FilterType.EFFICIENT, true, rebuildWithFilter);
392+
393+
var dummyChild = new LogicalValues(Collections.emptyList());
394+
var limit = new LogicalLimit(dummyChild, 10, 0);
395+
396+
ExpressionEvaluationException ex =
397+
assertThrows(ExpressionEvaluationException.class, () -> builder.pushDownLimit(limit));
398+
assertTrue(ex.getMessage().contains("LIMIT 10 exceeds k=5"));
399+
}
400+
401+
@Test
402+
void pushDownSortScoreDescAcceptedUnderEfficientMode() {
403+
var requestBuilder = createRequestBuilder();
404+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
405+
Function<QueryBuilder, QueryBuilder> rebuildWithFilter =
406+
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
407+
var builder =
408+
new VectorSearchQueryBuilder(
409+
requestBuilder, knnQuery, Map.of("k", "5"),
410+
FilterType.EFFICIENT, true, rebuildWithFilter);
411+
412+
var dummyChild = new LogicalValues(Collections.emptyList());
413+
var sort =
414+
new org.opensearch.sql.planner.logical.LogicalSort(
415+
dummyChild,
416+
List.of(
417+
org.apache.commons.lang3.tuple.ImmutablePair.of(
418+
org.opensearch.sql.ast.tree.Sort.SortOption.DEFAULT_DESC,
419+
new ReferenceExpression("_score", ExprCoreType.FLOAT))));
420+
421+
boolean pushed = builder.pushDownSort(sort);
422+
assertTrue(pushed, "ORDER BY _score DESC should be accepted under efficient mode");
423+
}
424+
425+
@Test
426+
void pushDownSortNonScoreRejectedUnderEfficientMode() {
427+
var requestBuilder = createRequestBuilder();
428+
var knnQuery = new WrapperQueryBuilder("{\"knn\":{}}");
429+
Function<QueryBuilder, QueryBuilder> rebuildWithFilter =
430+
whereQuery -> new WrapperQueryBuilder("{\"knn\":{}}");
431+
var builder =
432+
new VectorSearchQueryBuilder(
433+
requestBuilder, knnQuery, Map.of("k", "5"),
434+
FilterType.EFFICIENT, true, rebuildWithFilter);
435+
436+
var dummyChild = new LogicalValues(Collections.emptyList());
437+
var sort =
438+
new org.opensearch.sql.planner.logical.LogicalSort(
439+
dummyChild,
440+
List.of(
441+
org.apache.commons.lang3.tuple.ImmutablePair.of(
442+
org.opensearch.sql.ast.tree.Sort.SortOption.DEFAULT_ASC,
443+
new ReferenceExpression("name", STRING))));
444+
445+
ExpressionEvaluationException ex =
446+
assertThrows(ExpressionEvaluationException.class, () -> builder.pushDownSort(sort));
447+
assertTrue(ex.getMessage().contains("unsupported sort expression"));
448+
}
449+
320450
private OpenSearchRequestBuilder createRequestBuilder() {
321451
return new OpenSearchRequestBuilder(
322452
mock(OpenSearchExprValueFactory.class), 10000, mock(Settings.class));

0 commit comments

Comments
 (0)