Skip to content

Commit b90cab4

Browse files
committed
Add query filter, project, aggregation, sort converters
Signed-off-by: Vinay Krishna Pudyodu <vinkrish.neo@gmail.com>
1 parent 181b4da commit b90cab4

61 files changed

Lines changed: 5683 additions & 33 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

sandbox/plugins/dsl-query-executor/build.gradle

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,27 @@ opensearchplugin {
1414
extendedPlugins = ['analytics-engine']
1515
}
1616

17+
// Guava comes transitively from calcite-core — forbidden on compile classpaths
18+
// by OpenSearch policy. Calcite API exposes ImmutableList in type annotations,
19+
// so the compiler needs Guava. Bypass via custom config (same pattern as analytics-engine).
20+
configurations {
21+
calciteCompile
22+
compileClasspath { exclude group: 'com.google.guava' }
23+
testCompileClasspath { exclude group: 'com.google.guava' }
24+
}
25+
sourceSets.main.compileClasspath += configurations.calciteCompile
26+
sourceSets.test.compileClasspath += configurations.calciteCompile
27+
1728
dependencies {
1829
compileOnly project(':server')
1930
compileOnly project(':sandbox:libs:analytics-framework')
2031
compileOnly project(':sandbox:plugins:analytics-engine')
2132
// TODO: Consume Calcite dependency from Analytics Framework instead of declaring it separately.
2233
compileOnly 'org.apache.calcite.avatica:avatica-core:1.27.0'
2334

35+
// Guava for compilation — Calcite API exposes guava types in annotations
36+
calciteCompile "com.google.guava:guava:${versions.guava}"
37+
2438
testImplementation project(':test:framework')
2539
testImplementation "org.mockito:mockito-core:${versions.mockito}"
2640

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
9+
package org.opensearch.dsl;
10+
11+
import org.opensearch.search.aggregations.AggregationBuilders;
12+
import org.opensearch.search.aggregations.BucketOrder;
13+
import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
14+
import org.opensearch.search.builder.SearchSourceBuilder;
15+
16+
/**
17+
* Integration tests for DSL aggregation conversion.
18+
* Uses matchAllQuery; focus is on aggregation plan building.
19+
*/
20+
public class DslAggregationIT extends DslIntegTestBase {
21+
22+
public void testMetricOnly() {
23+
createTestIndex();
24+
assertOk(search(new SearchSourceBuilder().size(0).aggregation(AggregationBuilders.avg("avg_price").field("price"))));
25+
}
26+
27+
public void testMultipleMetrics() {
28+
createTestIndex();
29+
assertOk(
30+
search(
31+
new SearchSourceBuilder().size(0)
32+
.aggregation(AggregationBuilders.avg("avg_price").field("price"))
33+
.aggregation(AggregationBuilders.sum("total_price").field("price"))
34+
.aggregation(AggregationBuilders.min("min_price").field("price"))
35+
.aggregation(AggregationBuilders.max("max_price").field("price"))
36+
)
37+
);
38+
}
39+
40+
public void testTermsBucket() {
41+
createTestIndex();
42+
assertOk(search(new SearchSourceBuilder().size(0).aggregation(new TermsAggregationBuilder("by_brand").field("brand"))));
43+
}
44+
45+
public void testTermsBucketWithMetric() {
46+
createTestIndex();
47+
assertOk(
48+
search(
49+
new SearchSourceBuilder().size(0)
50+
.aggregation(
51+
new TermsAggregationBuilder("by_brand").field("brand")
52+
.subAggregation(AggregationBuilders.avg("avg_price").field("price"))
53+
)
54+
)
55+
);
56+
}
57+
58+
public void testNestedBuckets() {
59+
createTestIndex();
60+
assertOk(
61+
search(
62+
new SearchSourceBuilder().size(0)
63+
.aggregation(
64+
new TermsAggregationBuilder("by_brand").field("brand")
65+
.subAggregation(AggregationBuilders.sum("total").field("price"))
66+
.subAggregation(
67+
new TermsAggregationBuilder("by_name").field("name")
68+
.subAggregation(AggregationBuilders.avg("avg_price").field("price"))
69+
)
70+
)
71+
)
72+
);
73+
}
74+
75+
public void testAggsWithHits() {
76+
createTestIndex();
77+
// size > 0 with aggs produces both HITS + AGGREGATION plans
78+
assertOk(search(new SearchSourceBuilder().size(10).aggregation(AggregationBuilders.avg("avg_price").field("price"))));
79+
}
80+
81+
public void testTermsBucketOrderByKeyAsc() {
82+
createTestIndex();
83+
assertOk(
84+
search(
85+
new SearchSourceBuilder().size(0)
86+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand").order(BucketOrder.key(true)))
87+
)
88+
);
89+
}
90+
91+
public void testTermsBucketOrderByKeyDesc() {
92+
createTestIndex();
93+
assertOk(
94+
search(
95+
new SearchSourceBuilder().size(0)
96+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand").order(BucketOrder.key(false)))
97+
)
98+
);
99+
}
100+
101+
public void testTermsBucketOrderByCountAsc() {
102+
createTestIndex();
103+
assertOk(
104+
search(
105+
new SearchSourceBuilder().size(0)
106+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand").order(BucketOrder.count(true)))
107+
)
108+
);
109+
}
110+
111+
public void testTermsBucketOrderByMetric() {
112+
createTestIndex();
113+
assertOk(
114+
search(
115+
new SearchSourceBuilder().size(0)
116+
.aggregation(
117+
new TermsAggregationBuilder("by_brand").field("brand")
118+
.order(BucketOrder.aggregation("avg_price", false))
119+
.subAggregation(AggregationBuilders.avg("avg_price").field("price"))
120+
)
121+
)
122+
);
123+
}
124+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
9+
package org.opensearch.dsl;
10+
11+
import org.opensearch.action.search.SearchRequest;
12+
import org.opensearch.action.search.SearchResponse;
13+
import org.opensearch.analytics.AnalyticsPlugin;
14+
import org.opensearch.common.xcontent.XContentType;
15+
import org.opensearch.plugins.Plugin;
16+
import org.opensearch.search.builder.SearchSourceBuilder;
17+
import org.opensearch.test.OpenSearchIntegTestCase;
18+
19+
import java.util.Collection;
20+
import java.util.List;
21+
22+
// TODO: once end-to-end execution returns real results, update ITs to verify
23+
// actual hit count, field values, sort order, and aggregation buckets.
24+
/**
25+
* Base class for DSL query executor integration tests.
26+
* Provides shared index setup and search helper.
27+
*/
28+
@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 1)
29+
public abstract class DslIntegTestBase extends OpenSearchIntegTestCase {
30+
31+
protected static final String INDEX = "test-index";
32+
33+
@Override
34+
protected Collection<Class<? extends Plugin>> nodePlugins() {
35+
return List.of(AnalyticsPlugin.class, DslQueryExecutorPlugin.class);
36+
}
37+
38+
protected void createTestIndex() {
39+
createIndex(INDEX);
40+
ensureGreen();
41+
client().prepareIndex(INDEX)
42+
.setId("1")
43+
.setSource("{\"name\":\"laptop\",\"price\":1200,\"brand\":\"brandX\",\"rating\":4.5}", XContentType.JSON)
44+
.get();
45+
refresh(INDEX);
46+
}
47+
48+
protected SearchResponse search(SearchSourceBuilder source) {
49+
return client().search(new SearchRequest(INDEX).source(source)).actionGet();
50+
}
51+
52+
protected void assertOk(SearchResponse response) {
53+
assertNotNull(response);
54+
assertEquals(200, response.status().getStatus());
55+
}
56+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
9+
package org.opensearch.dsl;
10+
11+
import org.opensearch.search.builder.SearchSourceBuilder;
12+
13+
/**
14+
* Integration tests for DSL _source filtering (projection) conversion.
15+
* Uses matchAllQuery; focus is on _source includes/excludes behavior.
16+
*/
17+
public class DslProjectIT extends DslIntegTestBase {
18+
19+
public void testNoSourceFiltering() {
20+
createTestIndex();
21+
assertOk(search(new SearchSourceBuilder()));
22+
}
23+
24+
public void testIncludeSpecificFields() {
25+
createTestIndex();
26+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[] { "name", "price" }, null)));
27+
}
28+
29+
public void testExcludeFields() {
30+
createTestIndex();
31+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[] {}, new String[] { "rating" })));
32+
}
33+
34+
public void testSourceDisabled() {
35+
createTestIndex();
36+
assertOk(search(new SearchSourceBuilder().fetchSource(false)));
37+
}
38+
39+
public void testWildcardIncludes() {
40+
createTestIndex();
41+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[] { "na*" }, null)));
42+
}
43+
44+
public void testWildcardExcludes() {
45+
createTestIndex();
46+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[] {}, new String[] { "ra*" })));
47+
}
48+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
9+
package org.opensearch.dsl;
10+
11+
import org.opensearch.action.search.SearchRequest;
12+
import org.opensearch.index.query.QueryBuilders;
13+
import org.opensearch.search.builder.SearchSourceBuilder;
14+
15+
/**
16+
* Integration tests for DSL query conversion (filter path).
17+
* Uses various query types; sort and projection use defaults.
18+
*/
19+
public class DslQueryIT extends DslIntegTestBase {
20+
21+
public void testNoQuery() {
22+
createTestIndex();
23+
assertOk(search(new SearchSourceBuilder()));
24+
}
25+
26+
public void testMatchAll() {
27+
createTestIndex();
28+
assertOk(search(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())));
29+
}
30+
31+
public void testTermQuery() {
32+
createTestIndex();
33+
assertOk(search(new SearchSourceBuilder().query(QueryBuilders.termQuery("name", "laptop"))));
34+
}
35+
36+
public void testWildcardQueryWithUnresolvedNode() {
37+
createTestIndex();
38+
// Wildcard query is not converted to standard Rex — wraps in UnresolvedQueryCall.
39+
assertOk(search(new SearchSourceBuilder().query(QueryBuilders.wildcardQuery("name", "lap*"))));
40+
}
41+
42+
public void testFailsForNonexistentIndex() {
43+
expectThrows(
44+
Exception.class,
45+
() -> client().search(new SearchRequest("nonexistent-index").source(new SearchSourceBuilder())).actionGet()
46+
);
47+
}
48+
49+
public void testFailsForMultipleIndices() {
50+
createTestIndex();
51+
createIndex("test-index-2");
52+
ensureGreen();
53+
54+
expectThrows(
55+
Exception.class,
56+
() -> client().search(new SearchRequest(INDEX, "test-index-2").source(new SearchSourceBuilder())).actionGet()
57+
);
58+
}
59+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
9+
package org.opensearch.dsl;
10+
11+
import org.opensearch.search.builder.SearchSourceBuilder;
12+
import org.opensearch.search.sort.SortOrder;
13+
14+
/**
15+
* Integration tests for DSL sort and pagination conversion.
16+
* Uses matchAllQuery; focus is on sort/from/size behavior.
17+
*/
18+
public class DslSortIT extends DslIntegTestBase {
19+
20+
public void testDefaultPagination() {
21+
createTestIndex();
22+
assertOk(search(new SearchSourceBuilder()));
23+
}
24+
25+
public void testSortAscending() {
26+
createTestIndex();
27+
assertOk(search(new SearchSourceBuilder().sort("name", SortOrder.ASC)));
28+
}
29+
30+
public void testSortDescending() {
31+
createTestIndex();
32+
assertOk(search(new SearchSourceBuilder().sort("price", SortOrder.DESC)));
33+
}
34+
35+
public void testMultipleSortFields() {
36+
createTestIndex();
37+
assertOk(search(new SearchSourceBuilder().sort("brand", SortOrder.ASC).sort("price", SortOrder.DESC)));
38+
}
39+
40+
public void testCustomSize() {
41+
createTestIndex();
42+
assertOk(search(new SearchSourceBuilder().size(5)));
43+
}
44+
45+
public void testFromAndSize() {
46+
createTestIndex();
47+
assertOk(search(new SearchSourceBuilder().from(0).size(5)));
48+
}
49+
50+
public void testFromOffset() {
51+
createTestIndex();
52+
assertOk(search(new SearchSourceBuilder().from(10).size(5)));
53+
}
54+
}

0 commit comments

Comments
 (0)