Skip to content

Commit 9ff5d2d

Browse files
committed
Add mutual exclusivity and k range validation
- Enforce exactly one of k, max_distance, or min_score - Validate k is in [1, 10000] range - Add 6 tests: mutual exclusivity (3 combos), k too small, k too large, k boundary values (1 and 10000) Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent b54f293 commit 9ff5d2d

2 files changed

Lines changed: 70 additions & 0 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,20 @@ private void validateOptions(Map<String, String> options) {
197197
throw new ExpressionEvaluationException(
198198
"Missing required option: one of k, max_distance, or min_score");
199199
}
200+
// Mutual exclusivity: exactly one search mode allowed
201+
int modeCount = (hasK ? 1 : 0) + (hasMaxDistance ? 1 : 0) + (hasMinScore ? 1 : 0);
202+
if (modeCount > 1) {
203+
throw new ExpressionEvaluationException(
204+
"Only one of k, max_distance, or min_score may be specified");
205+
}
200206
// Parse and canonicalize numeric values — closes JSON injection via option values
201207
if (hasK) {
202208
parseIntOption(options, "k");
209+
int k = Integer.parseInt(options.get("k"));
210+
if (k < 1 || k > 10000) {
211+
throw new ExpressionEvaluationException(
212+
String.format("k must be between 1 and 10000, got %d", k));
213+
}
203214
}
204215
if (hasMaxDistance) {
205216
parseDoubleOption(options, "max_distance");

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,65 @@ void testInfiniteMinScoreThrows() {
233233
assertTrue(ex.getMessage().contains("must be a finite number"));
234234
}
235235

236+
@Test
237+
void testMutualExclusivityKAndMaxDistanceThrows() {
238+
VectorSearchTableFunctionImplementation impl =
239+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=5,max_distance=10.0");
240+
ExpressionEvaluationException ex =
241+
assertThrows(ExpressionEvaluationException.class, () -> impl.applyArguments());
242+
assertTrue(ex.getMessage().contains("Only one of"));
243+
}
244+
245+
@Test
246+
void testMutualExclusivityKAndMinScoreThrows() {
247+
VectorSearchTableFunctionImplementation impl =
248+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=5,min_score=0.5");
249+
ExpressionEvaluationException ex =
250+
assertThrows(ExpressionEvaluationException.class, () -> impl.applyArguments());
251+
assertTrue(ex.getMessage().contains("Only one of"));
252+
}
253+
254+
@Test
255+
void testMutualExclusivityAllThreeThrows() {
256+
VectorSearchTableFunctionImplementation impl =
257+
createImplWithArgs(
258+
"my-index", "embedding", "[1.0, 2.0]", "k=5,max_distance=10.0,min_score=0.5");
259+
ExpressionEvaluationException ex =
260+
assertThrows(ExpressionEvaluationException.class, () -> impl.applyArguments());
261+
assertTrue(ex.getMessage().contains("Only one of"));
262+
}
263+
264+
@Test
265+
void testKTooSmallThrows() {
266+
VectorSearchTableFunctionImplementation impl =
267+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=0");
268+
ExpressionEvaluationException ex =
269+
assertThrows(ExpressionEvaluationException.class, () -> impl.applyArguments());
270+
assertTrue(ex.getMessage().contains("k must be between 1 and 10000"));
271+
}
272+
273+
@Test
274+
void testKTooLargeThrows() {
275+
VectorSearchTableFunctionImplementation impl =
276+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=10001");
277+
ExpressionEvaluationException ex =
278+
assertThrows(ExpressionEvaluationException.class, () -> impl.applyArguments());
279+
assertTrue(ex.getMessage().contains("k must be between 1 and 10000"));
280+
}
281+
282+
@Test
283+
void testKBoundaryValuesAllowed() {
284+
// k=1 should work
285+
VectorSearchTableFunctionImplementation impl1 =
286+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=1");
287+
assertTrue(impl1.applyArguments() instanceof VectorSearchIndex);
288+
289+
// k=10000 should work
290+
VectorSearchTableFunctionImplementation impl2 =
291+
createImplWithArgs("my-index", "embedding", "[1.0, 2.0]", "k=10000");
292+
assertTrue(impl2.applyArguments() instanceof VectorSearchIndex);
293+
}
294+
236295
@Test
237296
void testNonNamedArgThrows() {
238297
FunctionName functionName = FunctionName.of("vectorsearch");

0 commit comments

Comments
 (0)