Skip to content

Commit a784e52

Browse files
Query Grouping Integration Tests (#85) (#97)
* Add Query Grouping Integtests * Spotless apply * Fix settings name in tests --------- (cherry picked from commit 90a3450) Signed-off-by: Siddhant Deshmukh <deshsid@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 0f08e07 commit a784e52

File tree

4 files changed

+354
-14
lines changed

4 files changed

+354
-14
lines changed

src/test/java/org/opensearch/plugin/insights/QueryInsightsRestTestCase.java

+109-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
package org.opensearch.plugin.insights;
1010

1111
import java.io.IOException;
12+
import java.nio.charset.StandardCharsets;
1213
import java.util.Collections;
1314
import java.util.List;
1415
import java.util.Map;
1516
import java.util.Optional;
17+
import java.util.regex.Matcher;
18+
import java.util.regex.Pattern;
1619
import java.util.stream.Collectors;
1720
import org.apache.http.Header;
1821
import org.apache.http.HttpHost;
@@ -167,8 +170,21 @@ protected String defaultTopQueriesSettings() {
167170
return "{\n"
168171
+ " \"persistent\" : {\n"
169172
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
170-
+ " \"search.insights.top_queries.latency.window_size\" : \"600s\",\n"
171-
+ " \"search.insights.top_queries.latency.top_n_size\" : 5\n"
173+
+ " \"search.insights.top_queries.latency.window_size\" : \"1m\",\n"
174+
+ " \"search.insights.top_queries.latency.top_n_size\" : 5,\n"
175+
+ " \"search.insights.top_queries.group_by\" : \"none\"\n"
176+
+ " }\n"
177+
+ "}";
178+
}
179+
180+
protected String defaultTopQueryGroupingSettings() {
181+
return "{\n"
182+
+ " \"persistent\" : {\n"
183+
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
184+
+ " \"search.insights.top_queries.latency.window_size\" : \"1m\",\n"
185+
+ " \"search.insights.top_queries.latency.top_n_size\" : 5,\n"
186+
+ " \"search.insights.top_queries.group_by\" : \"similarity\",\n"
187+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 5\n"
172188
+ " }\n"
173189
+ "}";
174190
}
@@ -196,4 +212,95 @@ protected void doSearch(int times) throws IOException {
196212
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
197213
}
198214
}
215+
216+
protected void doSearch(String queryType, int times) throws IOException {
217+
for (int i = 0; i < times; i++) {
218+
// Do Search
219+
Request request = new Request("GET", "/my-index-0/_search?size=20&pretty");
220+
221+
// Set query based on the query type
222+
request.setJsonEntity(searchBody(queryType));
223+
224+
Response response = client().performRequest(request);
225+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
226+
}
227+
}
228+
229+
private String searchBody(String queryType) {
230+
switch (queryType) {
231+
case "match":
232+
// Query shape 1: Match query
233+
return "{\n" + " \"query\": {\n" + " \"match\": {\n" + " \"field1\": \"value1\"\n" + " }\n" + " }\n" + "}";
234+
235+
case "range":
236+
// Query shape 2: Range query
237+
return "{\n"
238+
+ " \"query\": {\n"
239+
+ " \"range\": {\n"
240+
+ " \"field2\": {\n"
241+
+ " \"gte\": 10,\n"
242+
+ " \"lte\": 50\n"
243+
+ " }\n"
244+
+ " }\n"
245+
+ " }\n"
246+
+ "}";
247+
248+
case "term":
249+
// Query shape 3: Term query
250+
return "{\n"
251+
+ " \"query\": {\n"
252+
+ " \"term\": {\n"
253+
+ " \"field3\": {\n"
254+
+ " \"value\": \"exact-value\"\n"
255+
+ " }\n"
256+
+ " }\n"
257+
+ " }\n"
258+
+ "}";
259+
260+
default:
261+
throw new IllegalArgumentException("Unknown query type: " + queryType);
262+
}
263+
}
264+
265+
protected int countTopQueries(String json) {
266+
// Basic pattern to match JSON array elements in `top_queries`
267+
Pattern pattern = Pattern.compile("\\{\\s*\"timestamp\"");
268+
Matcher matcher = pattern.matcher(json);
269+
270+
int count = 0;
271+
while (matcher.find()) {
272+
count++;
273+
}
274+
275+
return count;
276+
}
277+
278+
protected void waitForEmptyTopQueriesResponse() throws IOException, InterruptedException {
279+
boolean isEmpty = false;
280+
long timeoutMillis = 70000; // 70 seconds timeout
281+
long startTime = System.currentTimeMillis();
282+
283+
while (!isEmpty && (System.currentTimeMillis() - startTime) < timeoutMillis) {
284+
Request request = new Request("GET", "/_insights/top_queries?pretty");
285+
Response response = client().performRequest(request);
286+
287+
if (response.getStatusLine().getStatusCode() != 200) {
288+
Thread.sleep(1000); // Sleep before retrying
289+
continue;
290+
}
291+
292+
String responseBody = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8);
293+
294+
if (countTopQueries(responseBody) == 0) {
295+
isEmpty = true;
296+
} else {
297+
Thread.sleep(1000); // Sleep before retrying
298+
}
299+
}
300+
301+
if (!isEmpty) {
302+
throw new IllegalStateException("Top queries response did not become empty within the timeout period");
303+
}
304+
}
305+
199306
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
package org.opensearch.plugin.insights.core.service.grouper;
9+
10+
import java.io.IOException;
11+
import java.nio.charset.StandardCharsets;
12+
import org.junit.Assert;
13+
import org.opensearch.client.Request;
14+
import org.opensearch.client.Response;
15+
import org.opensearch.plugin.insights.QueryInsightsRestTestCase;
16+
import org.opensearch.plugin.insights.settings.QueryInsightsSettings;
17+
18+
/**
19+
* ITs for Grouping Top Queries by none
20+
*/
21+
public class MinMaxQueryGrouperByNoneIT extends QueryInsightsRestTestCase {
22+
23+
/**
24+
* Grouping by none should not group queries
25+
* @throws IOException
26+
* @throws InterruptedException
27+
*/
28+
public void testGroupingByNone() throws IOException, InterruptedException {
29+
30+
waitForEmptyTopQueriesResponse();
31+
32+
// Enable top N feature and grouping by none
33+
Request request = new Request("PUT", "/_cluster/settings");
34+
request.setJsonEntity(groupByNoneSettings());
35+
Response response = client().performRequest(request);
36+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
37+
38+
// Search
39+
doSearch("range", 2);
40+
doSearch("match", 6);
41+
doSearch("term", 4);
42+
43+
// Ensure records are drained to the top queries service
44+
Thread.sleep(QueryInsightsSettings.QUERY_RECORD_QUEUE_DRAIN_INTERVAL.millis());
45+
46+
// run five times to make sure the records are drained to the top queries services
47+
for (int i = 0; i < 5; i++) {
48+
// Get Top Queries and validate
49+
request = new Request("GET", "/_insights/top_queries?pretty");
50+
response = client().performRequest(request);
51+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
52+
String top_requests = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8);
53+
54+
int top_n_array_size = countTopQueries(top_requests);
55+
56+
// Validate that all queries are listed separately (no grouping)
57+
Assert.assertEquals(12, top_n_array_size);
58+
}
59+
}
60+
61+
private String groupByNoneSettings() {
62+
return "{\n"
63+
+ " \"persistent\" : {\n"
64+
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
65+
+ " \"search.insights.top_queries.latency.window_size\" : \"1m\",\n"
66+
+ " \"search.insights.top_queries.latency.top_n_size\" : 100,\n"
67+
+ " \"search.insights.top_queries.group_by\" : \"none\",\n"
68+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 5\n"
69+
+ " }\n"
70+
+ "}";
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
package org.opensearch.plugin.insights.core.service.grouper;
9+
10+
import java.io.IOException;
11+
import java.nio.charset.StandardCharsets;
12+
import org.junit.Assert;
13+
import org.opensearch.client.Request;
14+
import org.opensearch.client.Response;
15+
import org.opensearch.client.ResponseException;
16+
import org.opensearch.plugin.insights.QueryInsightsRestTestCase;
17+
import org.opensearch.plugin.insights.settings.QueryInsightsSettings;
18+
19+
/**
20+
* ITs for Grouping Top Queries by similarity
21+
*/
22+
public class MinMaxQueryGrouperBySimilarityIT extends QueryInsightsRestTestCase {
23+
24+
/**
25+
* test grouping top queries
26+
*
27+
* @throws IOException IOException
28+
*/
29+
public void testGroupingBySimilarity() throws IOException, InterruptedException {
30+
31+
waitForEmptyTopQueriesResponse();
32+
33+
// Enable top N feature and grouping feature
34+
Request request = new Request("PUT", "/_cluster/settings");
35+
request.setJsonEntity(defaultTopQueryGroupingSettings());
36+
Response response = client().performRequest(request);
37+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
38+
39+
// Search
40+
doSearch("range", 2);
41+
doSearch("match", 6);
42+
doSearch("term", 4);
43+
44+
// run five times to make sure the records are drained to the top queries services
45+
for (int i = 0; i < 5; i++) {
46+
// Get Top Queries
47+
request = new Request("GET", "/_insights/top_queries?pretty");
48+
response = client().performRequest(request);
49+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
50+
51+
String responseBody = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8);
52+
53+
// Extract and count top_queries
54+
int topNArraySize = countTopQueries(responseBody);
55+
56+
if (topNArraySize == 0) {
57+
Thread.sleep(QueryInsightsSettings.QUERY_RECORD_QUEUE_DRAIN_INTERVAL.millis());
58+
continue;
59+
}
60+
61+
Assert.assertEquals(3, topNArraySize);
62+
}
63+
}
64+
65+
/**
66+
* Test invalid query grouping settings
67+
*
68+
* @throws IOException IOException
69+
*/
70+
public void testInvalidQueryGroupingSettings() throws IOException {
71+
for (String setting : invalidQueryGroupingSettings()) {
72+
Request request = new Request("PUT", "/_cluster/settings");
73+
request.setJsonEntity(setting);
74+
try {
75+
client().performRequest(request);
76+
fail("Should not succeed with invalid query grouping settings");
77+
} catch (ResponseException e) {
78+
assertEquals(400, e.getResponse().getStatusLine().getStatusCode());
79+
}
80+
}
81+
}
82+
83+
/**
84+
* Test valid query grouping settings
85+
*
86+
* @throws IOException IOException
87+
*/
88+
public void testValidQueryGroupingSettings() throws IOException {
89+
for (String setting : validQueryGroupingSettings()) {
90+
Request request = new Request("PUT", "/_cluster/settings");
91+
request.setJsonEntity(setting);
92+
Response response = client().performRequest(request);
93+
assertEquals(200, response.getStatusLine().getStatusCode());
94+
}
95+
}
96+
97+
private String[] invalidQueryGroupingSettings() {
98+
return new String[] {
99+
// Invalid max_groups: below minimum (-1)
100+
"{\n"
101+
+ " \"persistent\" : {\n"
102+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : -1\n"
103+
+ " }\n"
104+
+ "}",
105+
106+
// Invalid max_groups: above maximum (10001)
107+
"{\n"
108+
+ " \"persistent\" : {\n"
109+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 10001\n"
110+
+ " }\n"
111+
+ "}",
112+
113+
// Invalid group_by: unsupported value
114+
"{\n"
115+
+ " \"persistent\" : {\n"
116+
+ " \"search.insights.top_queries.group_by\" : \"unsupported_value\"\n"
117+
+ " }\n"
118+
+ "}" };
119+
}
120+
121+
private String[] validQueryGroupingSettings() {
122+
return new String[] {
123+
// Valid max_groups: minimum value (0)
124+
"{\n"
125+
+ " \"persistent\" : {\n"
126+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 0\n"
127+
+ " }\n"
128+
+ "}",
129+
130+
// Valid max_groups: maximum value (10000)
131+
"{\n"
132+
+ " \"persistent\" : {\n"
133+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 10000\n"
134+
+ " }\n"
135+
+ "}",
136+
137+
// Valid group_by: supported value (SIMILARITY)
138+
"{\n" + " \"persistent\" : {\n" + " \"search.insights.top_queries.group_by\" : \"SIMILARITY\"\n" + " }\n" + "}" };
139+
}
140+
141+
private String groupByNoneSettings() {
142+
return "{\n"
143+
+ " \"persistent\" : {\n"
144+
+ " \"search.insights.top_queries.latency.enabled\" : \"true\",\n"
145+
+ " \"search.insights.top_queries.latency.window_size\" : \"1m\",\n"
146+
+ " \"search.insights.top_queries.latency.top_n_size\" : 100,\n"
147+
+ " \"search.insights.top_queries.group_by\" : \"none\",\n"
148+
+ " \"search.insights.top_queries.max_groups_excluding_topn\" : 5\n"
149+
+ " }\n"
150+
+ "}";
151+
}
152+
}

0 commit comments

Comments
 (0)