Skip to content

Commit 4a744d4

Browse files
committed
Split explain tests into dedicated VectorSearchExplainIT
Move all explainQuery()-based DSL shape tests into a dedicated VectorSearchExplainIT suite. VectorSearchIT now contains only validation and error-path tests. Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
1 parent 1cf35ce commit 4a744d4

2 files changed

Lines changed: 176 additions & 150 deletions

File tree

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.sql;
7+
8+
import java.io.IOException;
9+
import org.junit.Test;
10+
import org.opensearch.sql.legacy.SQLIntegTestCase;
11+
import org.opensearch.sql.legacy.TestsConstants;
12+
13+
/**
14+
* Explain-plan integration tests for vectorSearch SQL table function. These tests verify DSL
15+
* push-down shape via _explain. They do NOT require the k-NN plugin since _explain only parses and
16+
* plans the query without executing it against a knn index.
17+
*/
18+
public class VectorSearchExplainIT extends SQLIntegTestCase {
19+
20+
@Override
21+
protected void init() throws Exception {
22+
// _explain needs the index to exist for field resolution.
23+
loadIndex(Index.ACCOUNT);
24+
}
25+
26+
private static final String TEST_INDEX = TestsConstants.TEST_INDEX_ACCOUNT;
27+
28+
// ── Top-k / radial DSL shape ─────────────────────────────────────────
29+
30+
@Test
31+
public void testExplainTopKProducesKnnQuery() throws IOException {
32+
String explain =
33+
explainQuery(
34+
"SELECT v._id, v._score "
35+
+ "FROM vectorSearch(table='"
36+
+ TEST_INDEX
37+
+ "', field='embedding', "
38+
+ "vector='[1.0, 2.0, 3.0]', option='k=5') AS v "
39+
+ "LIMIT 5");
40+
41+
// WrapperQueryBuilder wraps the knn JSON — verify the wrapper is present
42+
// and track_scores is enabled for score preservation.
43+
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
44+
assertTrue(
45+
"Explain should contain track_scores:\n" + explain, explain.contains("track_scores"));
46+
}
47+
48+
@Test
49+
public void testExplainRadialMaxDistanceProducesKnnQuery() throws IOException {
50+
String explain =
51+
explainQuery(
52+
"SELECT v._id, v._score "
53+
+ "FROM vectorSearch(table='"
54+
+ TEST_INDEX
55+
+ "', field='embedding', "
56+
+ "vector='[1.0, 2.0]', option='max_distance=10.5') AS v "
57+
+ "LIMIT 100");
58+
59+
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
60+
}
61+
62+
@Test
63+
public void testExplainRadialMinScoreProducesKnnQuery() throws IOException {
64+
String explain =
65+
explainQuery(
66+
"SELECT v._id, v._score "
67+
+ "FROM vectorSearch(table='"
68+
+ TEST_INDEX
69+
+ "', field='embedding', "
70+
+ "vector='[1.0, 2.0]', option='min_score=0.8') AS v "
71+
+ "LIMIT 100");
72+
73+
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
74+
}
75+
76+
// ── Post-filter DSL shape ────────────────────────────────────────────
77+
78+
@Test
79+
public void testExplainPostFilterProducesBoolQuery() throws IOException {
80+
String explain =
81+
explainQuery(
82+
"SELECT v._id, v._score "
83+
+ "FROM vectorSearch(table='"
84+
+ TEST_INDEX
85+
+ "', field='embedding', "
86+
+ "vector='[1.0, 2.0, 3.0]', option='k=10') AS v "
87+
+ "WHERE v.state = 'TX' "
88+
+ "LIMIT 10");
89+
90+
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
91+
assertTrue(
92+
"Explain should contain must clause (knn in scoring context):\n" + explain,
93+
explain.contains("must"));
94+
assertTrue(
95+
"Explain should contain filter clause (WHERE in non-scoring context):\n" + explain,
96+
explain.contains("filter"));
97+
}
98+
99+
@Test
100+
public void testExplainCompoundPredicateProducesBoolQuery() throws IOException {
101+
String explain =
102+
explainQuery(
103+
"SELECT v._id, v._score "
104+
+ "FROM vectorSearch(table='"
105+
+ TEST_INDEX
106+
+ "', field='embedding', "
107+
+ "vector='[1.0, 2.0, 3.0]', option='k=10') AS v "
108+
+ "WHERE v.state = 'TX' AND v.age > 30 "
109+
+ "LIMIT 10");
110+
111+
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
112+
assertTrue(
113+
"Explain should contain must clause (knn in scoring context):\n" + explain,
114+
explain.contains("must"));
115+
assertTrue(
116+
"Explain should contain filter clause (compound WHERE in non-scoring context):\n" + explain,
117+
explain.contains("filter"));
118+
}
119+
120+
@Test
121+
public void testExplainRadialWithWhereProducesBoolQuery() throws IOException {
122+
String explain =
123+
explainQuery(
124+
"SELECT v._id, v._score "
125+
+ "FROM vectorSearch(table='"
126+
+ TEST_INDEX
127+
+ "', field='embedding', "
128+
+ "vector='[1.0, 2.0]', option='max_distance=10.5') AS v "
129+
+ "WHERE v.state = 'TX' "
130+
+ "LIMIT 100");
131+
132+
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
133+
assertTrue(
134+
"Explain should contain must clause (knn in scoring context):\n" + explain,
135+
explain.contains("must"));
136+
assertTrue(
137+
"Explain should contain filter clause (WHERE in non-scoring context):\n" + explain,
138+
explain.contains("filter"));
139+
}
140+
141+
// ── Sort + LIMIT explain ─────────────────────────────────────────────
142+
143+
@Test
144+
public void testOrderByScoreDescExplainSucceeds() throws IOException {
145+
String explain =
146+
explainQuery(
147+
"SELECT v._id, v._score "
148+
+ "FROM vectorSearch(table='"
149+
+ TEST_INDEX
150+
+ "', field='embedding', "
151+
+ "vector='[1.0, 2.0]', option='k=5') AS v "
152+
+ "ORDER BY v._score DESC "
153+
+ "LIMIT 5");
154+
155+
assertTrue(
156+
"Explain should succeed with ORDER BY _score DESC:\n" + explain,
157+
explain.contains("wrapper"));
158+
}
159+
160+
@Test
161+
public void testExplainLimitWithinKSucceeds() throws IOException {
162+
String explain =
163+
explainQuery(
164+
"SELECT v._id, v._score "
165+
+ "FROM vectorSearch(table='"
166+
+ TEST_INDEX
167+
+ "', field='embedding', "
168+
+ "vector='[1.0, 2.0]', option='k=10') AS v "
169+
+ "LIMIT 5");
170+
171+
assertTrue("Explain should succeed with LIMIT <= k:\n" + explain, explain.contains("wrapper"));
172+
}
173+
}

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

Lines changed: 3 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -14,89 +14,19 @@
1414
import org.opensearch.sql.legacy.TestsConstants;
1515

1616
/**
17-
* Integration tests for vectorSearch SQL table function. These tests verify DSL push-down shape via
18-
* _explain and validation error paths. They do NOT require the k-NN plugin since _explain only
19-
* parses and plans the query without executing it against a knn index.
17+
* Integration tests for vectorSearch SQL table function — validation and error paths. These tests
18+
* verify that invalid inputs are rejected with clear error messages. Explain-plan DSL shape tests
19+
* live in {@link VectorSearchExplainIT}.
2020
*/
2121
public class VectorSearchIT extends SQLIntegTestCase {
2222

2323
@Override
2424
protected void init() throws Exception {
25-
// _explain needs the index to exist for field resolution.
2625
loadIndex(Index.ACCOUNT);
2726
}
2827

2928
private static final String TEST_INDEX = TestsConstants.TEST_INDEX_ACCOUNT;
3029

31-
// ── DSL shape verification via _explain ───────────────────────────────
32-
33-
@Test
34-
public void testExplainTopKProducesKnnQuery() throws IOException {
35-
String explain =
36-
explainQuery(
37-
"SELECT v._id, v._score "
38-
+ "FROM vectorSearch(table='"
39-
+ TEST_INDEX
40-
+ "', field='embedding', "
41-
+ "vector='[1.0, 2.0, 3.0]', option='k=5') AS v "
42-
+ "LIMIT 5");
43-
44-
// WrapperQueryBuilder wraps the knn JSON — verify the wrapper is present
45-
// and track_scores is enabled for score preservation.
46-
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
47-
assertTrue(
48-
"Explain should contain track_scores:\n" + explain, explain.contains("track_scores"));
49-
}
50-
51-
@Test
52-
public void testExplainRadialMaxDistanceProducesKnnQuery() throws IOException {
53-
String explain =
54-
explainQuery(
55-
"SELECT v._id, v._score "
56-
+ "FROM vectorSearch(table='"
57-
+ TEST_INDEX
58-
+ "', field='embedding', "
59-
+ "vector='[1.0, 2.0]', option='max_distance=10.5') AS v "
60-
+ "LIMIT 100");
61-
62-
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
63-
}
64-
65-
@Test
66-
public void testExplainRadialMinScoreProducesKnnQuery() throws IOException {
67-
String explain =
68-
explainQuery(
69-
"SELECT v._id, v._score "
70-
+ "FROM vectorSearch(table='"
71-
+ TEST_INDEX
72-
+ "', field='embedding', "
73-
+ "vector='[1.0, 2.0]', option='min_score=0.8') AS v "
74-
+ "LIMIT 100");
75-
76-
assertTrue("Explain should contain wrapper query:\n" + explain, explain.contains("wrapper"));
77-
}
78-
79-
@Test
80-
public void testExplainPostFilterProducesBoolQuery() throws IOException {
81-
String explain =
82-
explainQuery(
83-
"SELECT v._id, v._score "
84-
+ "FROM vectorSearch(table='"
85-
+ TEST_INDEX
86-
+ "', field='embedding', "
87-
+ "vector='[1.0, 2.0, 3.0]', option='k=10') AS v "
88-
+ "WHERE v.state = 'TX' "
89-
+ "LIMIT 10");
90-
91-
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
92-
assertTrue(
93-
"Explain should contain must clause (knn in scoring context):\n" + explain,
94-
explain.contains("must"));
95-
assertTrue(
96-
"Explain should contain filter clause (WHERE in non-scoring context):\n" + explain,
97-
explain.contains("filter"));
98-
}
99-
10030
// ── Validation error paths ────────────────────────────────────────────
10131

10232
@Test
@@ -205,23 +135,6 @@ public void testMissingRequiredOptionRejects() throws IOException {
205135

206136
// ── Sort restriction validation ─────────────────────────────────────────
207137

208-
@Test
209-
public void testOrderByScoreDescExplainSucceeds() throws IOException {
210-
String explain =
211-
explainQuery(
212-
"SELECT v._id, v._score "
213-
+ "FROM vectorSearch(table='"
214-
+ TEST_INDEX
215-
+ "', field='embedding', "
216-
+ "vector='[1.0, 2.0]', option='k=5') AS v "
217-
+ "ORDER BY v._score DESC "
218-
+ "LIMIT 5");
219-
220-
assertTrue(
221-
"Explain should succeed with ORDER BY _score DESC:\n" + explain,
222-
explain.contains("wrapper"));
223-
}
224-
225138
@Test
226139
public void testOrderByNonScoreFieldRejects() throws IOException {
227140
ResponseException ex =
@@ -255,64 +168,4 @@ public void testOrderByScoreAscRejects() throws IOException {
255168

256169
assertThat(ex.getMessage(), containsString("_score ASC is not supported"));
257170
}
258-
259-
// ── Compound predicate and radial + WHERE ───────────────────────────────
260-
261-
@Test
262-
public void testExplainCompoundPredicateProducesBoolQuery() throws IOException {
263-
String explain =
264-
explainQuery(
265-
"SELECT v._id, v._score "
266-
+ "FROM vectorSearch(table='"
267-
+ TEST_INDEX
268-
+ "', field='embedding', "
269-
+ "vector='[1.0, 2.0, 3.0]', option='k=10') AS v "
270-
+ "WHERE v.state = 'TX' AND v.age > 30 "
271-
+ "LIMIT 10");
272-
273-
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
274-
assertTrue(
275-
"Explain should contain must clause (knn in scoring context):\n" + explain,
276-
explain.contains("must"));
277-
assertTrue(
278-
"Explain should contain filter clause (compound WHERE in non-scoring context):\n" + explain,
279-
explain.contains("filter"));
280-
}
281-
282-
@Test
283-
public void testExplainRadialWithWhereProducesBoolQuery() throws IOException {
284-
String explain =
285-
explainQuery(
286-
"SELECT v._id, v._score "
287-
+ "FROM vectorSearch(table='"
288-
+ TEST_INDEX
289-
+ "', field='embedding', "
290-
+ "vector='[1.0, 2.0]', option='max_distance=10.5') AS v "
291-
+ "WHERE v.state = 'TX' "
292-
+ "LIMIT 100");
293-
294-
assertTrue("Explain should contain bool query:\n" + explain, explain.contains("bool"));
295-
assertTrue(
296-
"Explain should contain must clause (knn in scoring context):\n" + explain,
297-
explain.contains("must"));
298-
assertTrue(
299-
"Explain should contain filter clause (WHERE in non-scoring context):\n" + explain,
300-
explain.contains("filter"));
301-
}
302-
303-
// ── LIMIT validation ───────────────────────────────────────────────────
304-
305-
@Test
306-
public void testExplainLimitWithinKSucceeds() throws IOException {
307-
String explain =
308-
explainQuery(
309-
"SELECT v._id, v._score "
310-
+ "FROM vectorSearch(table='"
311-
+ TEST_INDEX
312-
+ "', field='embedding', "
313-
+ "vector='[1.0, 2.0]', option='k=10') AS v "
314-
+ "LIMIT 5");
315-
316-
assertTrue("Explain should succeed with LIMIT <= k:\n" + explain, explain.contains("wrapper"));
317-
}
318171
}

0 commit comments

Comments
 (0)