in pinot-segment-local/src/main/java/org/apache/pinot/segment/local/realtime/impl/json/MutableJsonIndexImpl.java [320:509]
private LazyBitmap getMatchingFlattenedDocIds(Predicate predicate) {
ExpressionContext lhs = predicate.getLhs();
Preconditions.checkArgument(lhs.getType() == ExpressionContext.Type.IDENTIFIER,
"Left-hand side of the predicate must be an identifier, got: %s (%s). Put double quotes around the identifier"
+ " if needed.", lhs, lhs.getType());
String key = lhs.getIdentifier();
// Support 2 formats:
// - JSONPath format (e.g. "$.a[1].b"='abc', "$[0]"=1, "$"='abc')
// - Legacy format (e.g. "a[1].b"='abc')
if (key.charAt(0) == '$') {
key = key.substring(1);
} else {
key = JsonUtils.KEY_SEPARATOR + key;
}
Pair<String, LazyBitmap> pair = getKeyAndFlattenedDocIds(key);
key = pair.getLeft();
LazyBitmap matchingDocIds = pair.getRight();
if (matchingDocIds != null && matchingDocIds.isEmpty()) {
return LazyBitmap.EMPTY_BITMAP;
}
Predicate.Type predicateType = predicate.getType();
switch (predicateType) {
case EQ: {
String value = ((EqPredicate) predicate).getValue();
String keyValuePair = key + JsonIndexCreator.KEY_VALUE_SEPARATOR + value;
RoaringBitmap result = _postingListMap.get(keyValuePair);
return filter(result, matchingDocIds);
}
case NOT_EQ: {
String notEqualValue = ((NotEqPredicate) predicate).getValue();
LazyBitmap result = null;
RoaringBitmap allDocIds = _postingListMap.get(key);
if (allDocIds != null && !allDocIds.isEmpty()) {
result = new LazyBitmap(allDocIds);
RoaringBitmap notEqualDocIds =
_postingListMap.get(key + JsonIndexCreator.KEY_VALUE_SEPARATOR + notEqualValue);
if (notEqualDocIds != null && !notEqualDocIds.isEmpty()) {
result = result.andNot(notEqualDocIds);
}
}
return filter(result, matchingDocIds);
}
case IN: {
List<String> values = ((InPredicate) predicate).getValues();
LazyBitmap result = null;
StringBuilder buffer = new StringBuilder(key);
buffer.append(JsonIndexCreator.KEY_VALUE_SEPARATOR);
int pos = buffer.length();
for (String value : values) {
buffer.setLength(pos);
buffer.append(value);
String keyValue = buffer.toString();
RoaringBitmap docIds = _postingListMap.get(keyValue);
if (docIds != null && !docIds.isEmpty()) {
if (result == null) {
result = new LazyBitmap(docIds);
} else {
result = result.or(docIds);
}
}
}
return filter(result, matchingDocIds);
}
case NOT_IN: {
List<String> notInValues = ((NotInPredicate) predicate).getValues();
LazyBitmap result = null;
RoaringBitmap allDocIds = _postingListMap.get(key);
if (allDocIds != null && !allDocIds.isEmpty()) {
result = new LazyBitmap(allDocIds);
StringBuilder buffer = new StringBuilder(key);
buffer.append(JsonIndexCreator.KEY_VALUE_SEPARATOR);
int pos = buffer.length();
for (String notInValue : notInValues) {
buffer.setLength(pos);
buffer.append(notInValue);
String keyValuePair = buffer.toString();
RoaringBitmap docIds = _postingListMap.get(keyValuePair);
if (docIds != null && !docIds.isEmpty()) {
result = result.andNot(docIds);
}
}
}
return filter(result, matchingDocIds);
}
case IS_NOT_NULL:
case IS_NULL: {
RoaringBitmap result = _postingListMap.get(key);
return filter(result, matchingDocIds);
}
case REGEXP_LIKE: {
Map<String, RoaringBitmap> subMap = getMatchingKeysMap(key);
if (subMap.isEmpty()) {
return LazyBitmap.EMPTY_BITMAP;
}
Pattern pattern = ((RegexpLikePredicate) predicate).getPattern();
Matcher matcher = pattern.matcher("");
LazyBitmap result = null;
StringBuilder value = new StringBuilder();
for (Map.Entry<String, RoaringBitmap> entry : subMap.entrySet()) {
String keyValue = entry.getKey();
value.setLength(0);
value.append(keyValue, key.length() + 1, keyValue.length());
if (!matcher.reset(value).matches()) {
continue;
}
RoaringBitmap docIds = entry.getValue();
if (docIds != null && !docIds.isEmpty()) {
if (result == null) {
result = new LazyBitmap(docIds);
} else {
result = result.or(docIds);
}
}
}
return filter(result, matchingDocIds);
}
case RANGE: {
Map<String, RoaringBitmap> subMap = getMatchingKeysMap(key);
if (subMap.isEmpty()) {
return LazyBitmap.EMPTY_BITMAP;
}
LazyBitmap result = null;
RangePredicate rangePredicate = (RangePredicate) predicate;
FieldSpec.DataType rangeDataType = rangePredicate.getRangeDataType();
// Simplify to only support numeric and string types
if (rangeDataType.isNumeric()) {
rangeDataType = FieldSpec.DataType.DOUBLE;
} else {
rangeDataType = FieldSpec.DataType.STRING;
}
boolean lowerUnbounded = rangePredicate.getLowerBound().equals(RangePredicate.UNBOUNDED);
boolean upperUnbounded = rangePredicate.getUpperBound().equals(RangePredicate.UNBOUNDED);
boolean lowerInclusive = lowerUnbounded || rangePredicate.isLowerInclusive();
boolean upperInclusive = upperUnbounded || rangePredicate.isUpperInclusive();
Object lowerBound = lowerUnbounded ? null : rangeDataType.convert(rangePredicate.getLowerBound());
Object upperBound = upperUnbounded ? null : rangeDataType.convert(rangePredicate.getUpperBound());
for (Map.Entry<String, RoaringBitmap> entry : subMap.entrySet()) {
Object valueObj = rangeDataType.convert(entry.getKey().substring(key.length() + 1));
boolean lowerCompareResult =
lowerUnbounded || (lowerInclusive ? rangeDataType.compare(valueObj, lowerBound) >= 0
: rangeDataType.compare(valueObj, lowerBound) > 0);
boolean upperCompareResult =
upperUnbounded || (upperInclusive ? rangeDataType.compare(valueObj, upperBound) <= 0
: rangeDataType.compare(valueObj, upperBound) < 0);
if (lowerCompareResult && upperCompareResult) {
if (result == null) {
result = new LazyBitmap(entry.getValue());
} else {
result = result.or(entry.getValue());
}
}
}
return filter(result, matchingDocIds);
}
default:
throw new IllegalStateException("Unsupported json_match predicate type: " + predicate);
}
}