Skip to content
Merged
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ private Predicate predicate(final Comparison comparison) {
INVALID_SYNTAX,
String.format("Operator %s is not supported for map fields with value null", op));
};
} else if (isNot(op)) {
return compare(comparison, toMapValuePath(pathResolver.getJoinOnInner(attribute, split[1])));
} else {
// map entry with key not null (exist) - correlated EXISTS semi-join per predicate;
// avoids the LEFT JOIN + DISTINCT row explosion on large attribute tables
return existsMapEntry(comparison, attribute, split[1]);
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) pathResolver.getPath(attribute);
return cb.and(equal(mapPath.key(), split[1]), compare(comparison, toMapValuePath(mapPath)));
}
} else if (attribute instanceof SetAttribute<?, ?> setAttribute) {
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
Expand Down Expand Up @@ -215,14 +216,6 @@ private static Path<String> toMapValuePath(final Path<?> mapJoin) {
return valuePath.getJavaType() == String.class ? (Path<String>) valuePath : valuePath.get("value");
}

// correlated EXISTS semi-join for a map entry with a non-null value filter (key/value in the subquery where)
private Predicate existsMapEntry(final Comparison comparison, final Attribute<? super T, ?> attribute, final String mapKey) {
final Subquery<Integer> subquery = query.subquery(Integer.class);
final MapJoin<?, ?, ?> mapJoin = (MapJoin<?, ?, ?>) subquery.correlate(root).join(attribute.getName(), JoinType.INNER);
return cb.exists(subquery.select(cb.literal(1))
.where(cb.and(equal(mapJoin.key(), mapKey), compare(comparison, toMapValuePath(mapJoin)))));
}

private Predicate compare(final Comparison comparison, final Path<?> fieldPath) {
final List<Object> values = getValues(comparison, fieldPath.getJavaType());
final Object firstValue = values.get(0);
Expand Down Expand Up @@ -469,6 +462,10 @@ private Path<?> getPath(final Attribute<? super T, ?> attribute) {
return getCollectionPathResolver(attribute.getName()).getJoinOn(value);
}

private MapJoin<?, ?, ?> getJoinOnInner(final Attribute<?, ?> attribute, final Object value) {
return getCollectionPathResolver(attribute.getName()).getJoinOnInner(value);
}

private Map<String, Integer> getState() {
return attributeToPathResolver.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, resolver -> resolver.getValue().getPos()));
Expand All @@ -491,6 +488,7 @@ private class CollectionPathResolver {
@Setter
private int pos;
private final Map<Object, MapJoin<?, ?, ?>> joinOnCache = new HashMap<>();
private final Map<Object, MapJoin<?, ?, ?>> joinOnInnerCache = new HashMap<>();

private CollectionPathResolver(final String attributeName) {
this.attributeName = attributeName;
Expand All @@ -514,6 +512,14 @@ private Path<?> getPath() {
return mapPath;
});
}

private MapJoin<?, ?, ?> getJoinOnInner(final Object value) {
return joinOnInnerCache.computeIfAbsent(value, k -> {
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) root.join(attributeName, JoinType.INNER);
mapPath.on(equal(mapPath.key(), k));
return mapPath;
});
}
}
}
}
Expand Down
Loading