Skip to content

[Star Tree] Support of Boolean Queries in Aggregations #17941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
641d617
Add bool filter provider
imRishN Apr 7, 2025
23b6b87
Merge remote-tracking branch 'origin/main' into bool-star-tree
imRishN Apr 7, 2025
525e2d2
should clause support
imRishN Apr 14, 2025
9dbe1bf
Handle nested bool queries with merging
imRishN Apr 17, 2025
1adb472
Remove test cases
imRishN Apr 17, 2025
0d22537
Add should clause support
imRishN Apr 18, 2025
308c3c9
Add tests
imRishN Apr 18, 2025
d766111
Add tests
imRishN Apr 18, 2025
ac34a73
Add test
imRishN Apr 18, 2025
5a0ecf1
Add tests
imRishN Apr 18, 2025
ae76035
Tests for basic must clause
imRishN Apr 20, 2025
4cb1f66
Add tests for basic should clause
imRishN Apr 20, 2025
c937541
Add tests
imRishN Apr 20, 2025
b508ea6
Add tests
imRishN Apr 20, 2025
25e611e
Add tests
imRishN Apr 20, 2025
f883f32
Add tests
imRishN Apr 20, 2025
04eb4c5
Add tests
imRishN Apr 20, 2025
5d9fe8d
Clean up
imRishN Apr 21, 2025
a17d242
Handle must inside should
imRishN Apr 21, 2025
6db89a5
Add tests for must inside should
imRishN Apr 21, 2025
a5adef0
Move BoolStarTreeFilterProvider to different source file
imRishN Apr 22, 2025
e4d43f9
Handle filter clause
imRishN Apr 22, 2025
1496990
refactor
imRishN Apr 22, 2025
b82451f
Add keyword and float range tests
imRishN Apr 22, 2025
8750aa4
Add float intersection test
imRishN Apr 22, 2025
00175f9
Move comparator to DimensionFilterMapper
imRishN Apr 22, 2025
04b55d0
Resolve conflicts
imRishN Apr 23, 2025
c38e5c6
Handle unsigned long
imRishN Apr 23, 2025
e35753b
Handle min should match
imRishN Apr 23, 2025
761a2c5
Add Changelog
imRishN Apr 23, 2025
67ea842
Fix spotless check
imRishN Apr 23, 2025
80cc481
Revert ff change
imRishN Apr 23, 2025
4ad0040
Add java doc
imRishN Apr 24, 2025
02452fb
Rename merger class
imRishN Apr 24, 2025
a99a01c
Fix conflict
imRishN Apr 24, 2025
65bf043
Fix spotless check
imRishN Apr 25, 2025
2596863
Fix conflict
imRishN Apr 25, 2025
fa3382c
Refactor
imRishN Apr 25, 2025
4cbf846
Add tests for null checks
imRishN Apr 28, 2025
63b786d
Minor changes and add tests
imRishN Apr 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Unset discovery nodes for every transport node actions request ([#17682](https://github.com/opensearch-project/OpenSearch/pull/17682))
- Implement parallel shard refresh behind cluster settings ([#17782](https://github.com/opensearch-project/OpenSearch/pull/17782))
- Bump OpenSearch Core main branch to 3.0.0 ([#18039](https://github.com/opensearch-project/OpenSearch/pull/18039))
- [Star Tree] Support of Boolean Queries in Aggregations ([#17941](https://github.com/opensearch-project/OpenSearch/pull/17941))
- Update API of Message in index to add the timestamp for lag calculation in ingestion polling ([#17977](https://github.com/opensearch-project/OpenSearch/pull/17977/))
- Enabled default throttling for all tasks submitted to cluster manager ([#17711](https://github.com/opensearch-project/OpenSearch/pull/17711))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
*/
boolean matchDimValue(long ordinal, StarTreeValues starTreeValues);

default String getDimensionName() {
return null;

Check warning on line 78 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilter.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilter.java#L78

Added line #L78 was not covered by tests
}

/**
* Represents how to match a value when comparing during StarTreeTraversal
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.search.startree.filter;

import org.opensearch.search.startree.filter.provider.DimensionFilterMapper;

import java.util.ArrayList;
import java.util.List;

/**
* Utility class for merging different types of {@link DimensionFilter}
* Handles intersection operations between {@link ExactMatchDimFilter} and {@link RangeMatchDimFilter}
*/
public class DimensionFilterMergerUtils {

Check warning on line 20 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L20

Added line #L20 was not covered by tests

/**
* Gets intersection of two DimensionFilters
* Returns null if intersection results in no possible matches.
*/
public static DimensionFilter intersect(DimensionFilter filter1, DimensionFilter filter2, DimensionFilterMapper mapper) {
if (filter1 == null || filter2 == null) {
return null;

Check warning on line 28 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L28

Added line #L28 was not covered by tests
}

if (filter1.getDimensionName() == null || filter2.getDimensionName() == null) {
throw new IllegalArgumentException("Cannot intersect filters with null dimension name");

Check warning on line 32 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L32

Added line #L32 was not covered by tests
}

// Verify filters are for same dimension
if (!filter1.getDimensionName().equals(filter2.getDimensionName())) {
throw new IllegalArgumentException(
"Cannot intersect filters for different dimensions: " + filter1.getDimensionName() + " and " + filter2.getDimensionName()
);
}

// Handle Range + Range combination
if (filter1 instanceof RangeMatchDimFilter && filter2 instanceof RangeMatchDimFilter) {
return intersectRangeFilters((RangeMatchDimFilter) filter1, (RangeMatchDimFilter) filter2, mapper);
}

// Handle ExactMatch + ExactMatch combination
if (filter1 instanceof ExactMatchDimFilter && filter2 instanceof ExactMatchDimFilter) {
return intersectExactMatchFilters((ExactMatchDimFilter) filter1, (ExactMatchDimFilter) filter2);
}

// Handle Range + ExactMatch combination
if (filter1 instanceof RangeMatchDimFilter && filter2 instanceof ExactMatchDimFilter) {
return intersectRangeWithExactMatch((RangeMatchDimFilter) filter1, (ExactMatchDimFilter) filter2, mapper);
}

// Handle ExactMatch + Range combination
if (filter1 instanceof ExactMatchDimFilter && filter2 instanceof RangeMatchDimFilter) {
return intersectRangeWithExactMatch((RangeMatchDimFilter) filter2, (ExactMatchDimFilter) filter1, mapper);
}

// throw exception for unsupported exception
throw new IllegalArgumentException(
"Unsupported filter combination: " + filter1.getClass().getSimpleName() + " and " + filter2.getClass().getSimpleName()

Check warning on line 64 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L63-L64

Added lines #L63 - L64 were not covered by tests
);
}

/**
* Intersects two range filters
* Returns null if ranges don't overlap
*/
private static DimensionFilter intersectRangeFilters(
RangeMatchDimFilter range1,
RangeMatchDimFilter range2,
DimensionFilterMapper mapper
) {
Object low1 = range1.getLow();
Object high1 = range1.getHigh();
Object low2 = range2.getLow();
Object high2 = range2.getHigh();

// Find the more restrictive bounds
Object newLow;
boolean includeLow;
if (low1 == null) {
newLow = low2;
includeLow = range2.isIncludeLow();
} else if (low2 == null) {
newLow = low1;
includeLow = range1.isIncludeLow();

Check warning on line 90 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L89-L90

Added lines #L89 - L90 were not covered by tests
} else {
int comparison = mapper.compareValues(low1, low2);
if (comparison > 0) {
newLow = low1;
includeLow = range1.isIncludeLow();
} else if (comparison < 0) {
newLow = low2;
includeLow = range2.isIncludeLow();
} else {
newLow = low1;
includeLow = range1.isIncludeLow() && range2.isIncludeLow();
}
}

Object newHigh;
boolean includeHigh;
if (high1 == null) {
newHigh = high2;
includeHigh = range2.isIncludeHigh();

Check warning on line 109 in server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/search/startree/filter/DimensionFilterMergerUtils.java#L108-L109

Added lines #L108 - L109 were not covered by tests
} else if (high2 == null) {
newHigh = high1;
includeHigh = range1.isIncludeHigh();
} else {
int comparison = mapper.compareValues(high1, high2);
if (comparison < 0) {
newHigh = high1;
includeHigh = range1.isIncludeHigh();
} else if (comparison > 0) {
newHigh = high2;
includeHigh = range2.isIncludeHigh();
} else {
newHigh = high1;
includeHigh = range1.isIncludeHigh() && range2.isIncludeHigh();
}
}

// Check if range is valid
if (newLow != null && newHigh != null) {
if (!mapper.isValidRange(newLow, newHigh, includeLow, includeHigh)) {
return null; // No overlap
}
}

return new RangeMatchDimFilter(range1.getDimensionName(), newLow, newHigh, includeLow, includeHigh);
}

/**
* Intersects two exact match filters
* Returns null if no common values
*/
private static DimensionFilter intersectExactMatchFilters(ExactMatchDimFilter exact1, ExactMatchDimFilter exact2) {
List<Object> values1 = exact1.getRawValues();
List<Object> values2 = exact2.getRawValues();

List<Object> intersection = new ArrayList<>();
for (Object value : values1) {
if (values2.contains(value)) {
intersection.add(value);
}
}

if (intersection.isEmpty()) {
return null;
}

return new ExactMatchDimFilter(exact1.getDimensionName(), intersection);
}

/**
* Intersects a range filter with an exact match filter.
* Returns null if no values from exact match are within range.
*/
private static DimensionFilter intersectRangeWithExactMatch(
RangeMatchDimFilter range,
ExactMatchDimFilter exact,
DimensionFilterMapper mapper
) {
List<Object> validValues = new ArrayList<>();

for (Object value : exact.getRawValues()) {
if (isValueInRange(value, range, mapper)) {
validValues.add(value);
}
}

if (validValues.isEmpty()) {
return null;
}

return new ExactMatchDimFilter(exact.getDimensionName(), validValues);
}

/**
* Checks if a value falls within a range.
*/
private static boolean isValueInRange(Object value, RangeMatchDimFilter range, DimensionFilterMapper mapper) {
return mapper.isValueInRange(value, range.getLow(), range.getHigh(), range.isIncludeLow(), range.isIncludeHigh());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,13 @@ public void matchStarTreeNodes(StarTreeNode parentNode, StarTreeValues starTreeV
public boolean matchDimValue(long ordinal, StarTreeValues starTreeValues) {
return convertedOrdinals.contains(ordinal);
}

public List<Object> getRawValues() {
return rawValues;
}

@Override
public String getDimensionName() {
return dimensionName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
@ExperimentalApi
public class MatchNoneFilter implements DimensionFilter {

@Override
public void initialiseForSegment(StarTreeValues starTreeValues, SearchContext searchContext) {
// Nothing to do as we won't match anything.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
public class RangeMatchDimFilter implements DimensionFilter {

private final String dimensionName;

private final Object low;
private final Object high;
private final boolean includeLow;
Expand All @@ -35,6 +34,7 @@ public class RangeMatchDimFilter implements DimensionFilter {
private Long lowOrdinal;
private Long highOrdinal;

// TODO - see if we need to handle this while intersecting
private boolean skipRangeCollection = false;

private DimensionFilterMapper dimensionFilterMapper;
Expand Down Expand Up @@ -90,4 +90,25 @@ public boolean matchDimValue(long ordinal, StarTreeValues starTreeValues) {
&& dimensionFilterMapper.comparator().compare(ordinal, highOrdinal) <= 0;
}

@Override
public String getDimensionName() {
return dimensionName;
}

public Object getLow() {
return low;
}

public Object getHigh() {
return high;
}

public boolean isIncludeLow() {
return includeLow;
}

public boolean isIncludeHigh() {
return includeHigh;
}

}
Loading
Loading