Skip to content

Commit 4ab27ae

Browse files
committed
Collapse buildKnnQueryJson to accept optional filter clause
Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent bfaabb4 commit 4ab27ae

2 files changed

Lines changed: 60 additions & 2 deletions

File tree

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ private QueryBuilder buildKnnQuery() {
9090

9191
// Package-private for testing
9292
String buildKnnQueryJson() {
93+
return buildKnnQueryJson(null);
94+
}
95+
96+
/**
97+
* Builds knn query JSON, optionally embedding a filter clause for efficient filtering.
98+
*
99+
* @param filterJson serialized filter JSON to embed in knn.field.filter, or null for no filter
100+
*/
101+
String buildKnnQueryJson(String filterJson) {
93102
StringBuilder vectorJson = new StringBuilder("[");
94103
for (int i = 0; i < vector.length; i++) {
95104
if (i > 0) vectorJson.append(",");
@@ -110,9 +119,14 @@ String buildKnnQueryJson() {
110119
}
111120
}
112121

122+
String filterClause = "";
123+
if (filterJson != null) {
124+
filterClause = String.format(",\"filter\":%s", filterJson);
125+
}
126+
113127
return String.format(
114-
"{\"knn\":{\"%s\":{\"vector\":%s%s}}}",
115-
field, vectorJson.toString(), optionsJson.toString());
128+
"{\"knn\":{\"%s\":{\"vector\":%s%s%s}}}",
129+
field, vectorJson.toString(), optionsJson.toString(), filterClause);
116130
}
117131

118132
private static boolean isNumeric(String str) {

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,50 @@ void buildKnnQueryJsonNonNumericOptionRenderedQuoted() {
140140
assertTrue(json.contains("\"k\":5"), "Numeric option should be unquoted");
141141
}
142142

143+
@Test
144+
void buildKnnQueryJsonWithFilterEmbeds() {
145+
VectorSearchIndex index =
146+
new VectorSearchIndex(
147+
client, settings, "test-index", "embedding",
148+
new float[] {1.0f, 2.0f}, Map.of("k", "5"), FilterType.EFFICIENT);
149+
150+
String filterJson = "{\"term\":{\"city\":{\"value\":\"Miami\"}}}";
151+
String json = index.buildKnnQueryJson(filterJson);
152+
153+
assertTrue(json.contains("\"filter\""), "Should contain filter field");
154+
assertTrue(json.contains("\"term\""), "Should contain the filter content");
155+
assertTrue(json.contains("\"k\":5"), "Should still contain k");
156+
assertTrue(json.contains("\"vector\":[1.0,2.0]"), "Should contain vector");
157+
}
158+
159+
@Test
160+
void buildKnnQueryJsonWithFilterRadial() {
161+
VectorSearchIndex index =
162+
new VectorSearchIndex(
163+
client, settings, "test-index", "embedding",
164+
new float[] {1.0f}, Map.of("max_distance", "10.5"), FilterType.EFFICIENT);
165+
166+
String filterJson = "{\"range\":{\"rating\":{\"gte\":4.0}}}";
167+
String json = index.buildKnnQueryJson(filterJson);
168+
169+
assertTrue(json.contains("\"max_distance\":10.5"), "Should contain max_distance");
170+
assertTrue(json.contains("\"filter\""), "Should contain filter");
171+
}
172+
173+
@Test
174+
void buildKnnQueryJsonNullFilterProducesBaseJson() {
175+
VectorSearchIndex index =
176+
new VectorSearchIndex(
177+
client, settings, "test-index", "embedding",
178+
new float[] {1.0f}, Map.of("k", "5"), null);
179+
180+
String json = index.buildKnnQueryJson(null);
181+
String baseJson = index.buildKnnQueryJson();
182+
183+
assertEquals(baseJson, json, "null filter should produce same JSON as no-arg version");
184+
assertFalse(json.contains("\"filter\""), "Should not contain filter field");
185+
}
186+
143187
@Test
144188
void buildKnnQueryJsonExcludesFilterType() {
145189
LinkedHashMap<String, String> options = new LinkedHashMap<>();

0 commit comments

Comments
 (0)