Skip to content

Commit c10cca4

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

52 files changed

Lines changed: 3013 additions & 24 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.bucket.terms.TermsAggregationBuilder;
13+
import org.opensearch.search.builder.SearchSourceBuilder;
14+
15+
/**
16+
* Integration tests for DSL aggregation conversion.
17+
* Uses matchAllQuery; focus is on aggregation plan building.
18+
*/
19+
public class DslAggregationIT extends DslIntegTestBase {
20+
21+
public void testMetricOnly() {
22+
createTestIndex();
23+
assertOk(search(new SearchSourceBuilder()
24+
.size(0)
25+
.aggregation(AggregationBuilders.avg("avg_price").field("price"))
26+
));
27+
}
28+
29+
public void testMultipleMetrics() {
30+
createTestIndex();
31+
assertOk(search(new SearchSourceBuilder()
32+
.size(0)
33+
.aggregation(AggregationBuilders.avg("avg_price").field("price"))
34+
.aggregation(AggregationBuilders.sum("total_price").field("price"))
35+
.aggregation(AggregationBuilders.min("min_price").field("price"))
36+
.aggregation(AggregationBuilders.max("max_price").field("price"))
37+
));
38+
}
39+
40+
public void testTermsBucket() {
41+
createTestIndex();
42+
assertOk(search(new SearchSourceBuilder()
43+
.size(0)
44+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand"))
45+
));
46+
}
47+
48+
public void testTermsBucketWithMetric() {
49+
createTestIndex();
50+
assertOk(search(new SearchSourceBuilder()
51+
.size(0)
52+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand")
53+
.subAggregation(AggregationBuilders.avg("avg_price").field("price")))
54+
));
55+
}
56+
57+
public void testNestedBuckets() {
58+
createTestIndex();
59+
assertOk(search(new SearchSourceBuilder()
60+
.size(0)
61+
.aggregation(new TermsAggregationBuilder("by_brand").field("brand")
62+
.subAggregation(AggregationBuilders.sum("total").field("price"))
63+
.subAggregation(new TermsAggregationBuilder("by_name").field("name")
64+
.subAggregation(AggregationBuilders.avg("avg_price").field("price"))))
65+
));
66+
}
67+
68+
public void testAggsWithHits() {
69+
createTestIndex();
70+
// size > 0 with aggs produces both HITS + AGGREGATION plans
71+
assertOk(search(new SearchSourceBuilder()
72+
.size(10)
73+
.aggregation(AggregationBuilders.avg("avg_price").field("price"))
74+
));
75+
}
76+
}
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: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.fetch.subphase.FetchSourceContext;
13+
14+
/**
15+
* Integration tests for DSL _source filtering (projection) conversion.
16+
* Uses matchAllQuery; focus is on _source includes/excludes behavior.
17+
*/
18+
public class DslProjectIT extends DslIntegTestBase {
19+
20+
public void testNoSourceFiltering() {
21+
createTestIndex();
22+
assertOk(search(new SearchSourceBuilder()));
23+
}
24+
25+
public void testIncludeSpecificFields() {
26+
createTestIndex();
27+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[]{"name", "price"}, null)));
28+
}
29+
30+
public void testExcludeFields() {
31+
createTestIndex();
32+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[]{}, new String[]{"rating"})));
33+
}
34+
35+
public void testSourceDisabled() {
36+
createTestIndex();
37+
assertOk(search(new SearchSourceBuilder().fetchSource(false)));
38+
}
39+
40+
public void testWildcardIncludes() {
41+
createTestIndex();
42+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[]{"na*"}, null)));
43+
}
44+
45+
public void testWildcardExcludes() {
46+
createTestIndex();
47+
assertOk(search(new SearchSourceBuilder().fetchSource(new String[]{}, new String[]{"ra*"})));
48+
}
49+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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(Exception.class, () ->
44+
client().search(new SearchRequest("nonexistent-index").source(new SearchSourceBuilder())).actionGet()
45+
);
46+
}
47+
48+
public void testFailsForMultipleIndices() {
49+
createTestIndex();
50+
createIndex("test-index-2");
51+
ensureGreen();
52+
53+
expectThrows(Exception.class, () ->
54+
client().search(new SearchRequest(INDEX, "test-index-2").source(new SearchSourceBuilder())).actionGet()
55+
);
56+
}
57+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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(
38+
new SearchSourceBuilder()
39+
.sort("brand", SortOrder.ASC)
40+
.sort("price", SortOrder.DESC)
41+
));
42+
}
43+
44+
public void testCustomSize() {
45+
createTestIndex();
46+
assertOk(search(new SearchSourceBuilder().size(5)));
47+
}
48+
49+
public void testFromAndSize() {
50+
createTestIndex();
51+
assertOk(search(new SearchSourceBuilder().from(0).size(5)));
52+
}
53+
54+
public void testFromOffset() {
55+
createTestIndex();
56+
assertOk(search(new SearchSourceBuilder().from(10).size(5)));
57+
}
58+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.aggregation;
10+
11+
import org.apache.calcite.rel.core.AggregateCall;
12+
import org.apache.calcite.util.ImmutableBitSet;
13+
14+
import java.util.List;
15+
16+
/**
17+
* Pre-computed metadata for one aggregation granularity level.
18+
* Contains everything needed to build a single {@code LogicalAggregate}.
19+
*
20+
* <p>A multi-level aggregation tree (e.g., terms → terms → avg) produces
21+
* multiple metadata instances — one per distinct GROUP BY key set.
22+
*/
23+
public class AggregationMetadata {
24+
25+
private final ImmutableBitSet groupByBitSet;
26+
private final List<String> groupByFieldNames;
27+
private final List<AggregateCall> aggregateCalls;
28+
private final List<String> aggregateFieldNames;
29+
30+
/**
31+
* Creates aggregation metadata.
32+
*
33+
* @param groupByBitSet column indices for GROUP BY
34+
* @param groupByFieldNames field names for GROUP BY columns
35+
* @param aggregateCalls Calcite aggregate calls (AVG, SUM, etc.)
36+
* @param aggregateFieldNames output names for aggregate results
37+
*/
38+
public AggregationMetadata(
39+
ImmutableBitSet groupByBitSet,
40+
List<String> groupByFieldNames,
41+
List<AggregateCall> aggregateCalls,
42+
List<String> aggregateFieldNames
43+
) {
44+
this.groupByBitSet = groupByBitSet;
45+
this.groupByFieldNames = List.copyOf(groupByFieldNames);
46+
this.aggregateCalls = List.copyOf(aggregateCalls);
47+
this.aggregateFieldNames = List.copyOf(aggregateFieldNames);
48+
}
49+
50+
/** Returns the GROUP BY column indices. */
51+
public ImmutableBitSet getGroupByBitSet() {
52+
return groupByBitSet;
53+
}
54+
55+
/** Returns the GROUP BY field names. */
56+
public List<String> getGroupByFieldNames() {
57+
return groupByFieldNames;
58+
}
59+
60+
/** Returns the aggregate calls. */
61+
public List<AggregateCall> getAggregateCalls() {
62+
return aggregateCalls;
63+
}
64+
65+
/** Returns the output field names for aggregate results. */
66+
public List<String> getAggregateFieldNames() {
67+
return aggregateFieldNames;
68+
}
69+
}

0 commit comments

Comments
 (0)