66package org .opensearch .sql .opensearch .storage .scan ;
77
88import static org .junit .jupiter .api .Assertions .assertEquals ;
9+ import static org .junit .jupiter .api .Assertions .assertThrows ;
910import static org .junit .jupiter .api .Assertions .assertTrue ;
1011import static org .mockito .Mockito .mock ;
1112import static org .opensearch .sql .data .type .ExprCoreType .STRING ;
1213
1314import java .util .Collections ;
15+ import java .util .Map ;
1416import org .junit .jupiter .api .Test ;
1517import org .opensearch .index .query .BoolQueryBuilder ;
1618import org .opensearch .index .query .QueryBuilder ;
1719import org .opensearch .index .query .WrapperQueryBuilder ;
1820import org .opensearch .sql .common .setting .Settings ;
21+ import org .opensearch .sql .exception .ExpressionEvaluationException ;
1922import org .opensearch .sql .expression .DSL ;
2023import org .opensearch .sql .expression .ReferenceExpression ;
2124import org .opensearch .sql .opensearch .data .value .OpenSearchExprValueFactory ;
2225import org .opensearch .sql .opensearch .request .OpenSearchRequestBuilder ;
2326import org .opensearch .sql .planner .logical .LogicalFilter ;
27+ import org .opensearch .sql .planner .logical .LogicalLimit ;
2428import org .opensearch .sql .planner .logical .LogicalValues ;
2529
2630class VectorSearchQueryBuilderTest {
@@ -30,7 +34,7 @@ void knnQuerySetAsScoringQuery() {
3034 var requestBuilder = createRequestBuilder ();
3135 var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
3236
33- new VectorSearchQueryBuilder (requestBuilder , knnQuery );
37+ new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map . of ( "k" , "5" ) );
3438
3539 QueryBuilder query = requestBuilder .getSourceBuilder ().query ();
3640 assertTrue (
@@ -42,7 +46,7 @@ void knnQuerySetAsScoringQuery() {
4246 void pushDownFilterKeepsKnnInScoringContext () {
4347 var requestBuilder = createRequestBuilder ();
4448 var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
45- var builder = new VectorSearchQueryBuilder (requestBuilder , knnQuery );
49+ var builder = new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map . of ( "k" , "5" ) );
4650
4751 // Simulate WHERE name = 'John'
4852 var condition = DSL .equal (new ReferenceExpression ("name" , STRING ), DSL .literal ("John" ));
@@ -62,6 +66,60 @@ void pushDownFilterKeepsKnnInScoringContext() {
6266 "must clause should contain the original knn WrapperQueryBuilder" );
6367 }
6468
69+ @ Test
70+ void pushDownLimitWithinKSucceeds () {
71+ var requestBuilder = createRequestBuilder ();
72+ var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
73+ var builder = new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map .of ("k" , "5" ));
74+
75+ var dummyChild = new LogicalValues (Collections .emptyList ());
76+ var limit = new LogicalLimit (dummyChild , 3 , 0 );
77+
78+ boolean pushed = builder .pushDownLimit (limit );
79+ assertTrue (pushed , "LIMIT within k should succeed" );
80+ }
81+
82+ @ Test
83+ void pushDownLimitExceedingKThrows () {
84+ var requestBuilder = createRequestBuilder ();
85+ var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
86+ var builder = new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map .of ("k" , "5" ));
87+
88+ var dummyChild = new LogicalValues (Collections .emptyList ());
89+ var limit = new LogicalLimit (dummyChild , 10 , 0 );
90+
91+ ExpressionEvaluationException ex =
92+ assertThrows (ExpressionEvaluationException .class , () -> builder .pushDownLimit (limit ));
93+ assertTrue (ex .getMessage ().contains ("LIMIT 10 exceeds k=5" ));
94+ }
95+
96+ @ Test
97+ void pushDownLimitEqualToKSucceeds () {
98+ var requestBuilder = createRequestBuilder ();
99+ var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
100+ var builder = new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map .of ("k" , "5" ));
101+
102+ var dummyChild = new LogicalValues (Collections .emptyList ());
103+ var limit = new LogicalLimit (dummyChild , 5 , 0 );
104+
105+ boolean pushed = builder .pushDownLimit (limit );
106+ assertTrue (pushed , "LIMIT equal to k should succeed" );
107+ }
108+
109+ @ Test
110+ void pushDownLimitRadialModeNoRestriction () {
111+ var requestBuilder = createRequestBuilder ();
112+ var knnQuery = new WrapperQueryBuilder ("{\" knn\" :{}}" );
113+ var builder =
114+ new VectorSearchQueryBuilder (requestBuilder , knnQuery , Map .of ("max_distance" , "10.0" ));
115+
116+ var dummyChild = new LogicalValues (Collections .emptyList ());
117+ var limit = new LogicalLimit (dummyChild , 100 , 0 );
118+
119+ boolean pushed = builder .pushDownLimit (limit );
120+ assertTrue (pushed , "Radial mode should not restrict LIMIT" );
121+ }
122+
65123 private OpenSearchRequestBuilder createRequestBuilder () {
66124 return new OpenSearchRequestBuilder (
67125 mock (OpenSearchExprValueFactory .class ), 10000 , mock (Settings .class ));
0 commit comments