From e2e5af80926f84942b8b6965bdc3132774e7b022 Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Tue, 12 Aug 2025 13:44:23 +0200 Subject: [PATCH 1/8] allow to customize json search function --- .../rsql/jsonb/JsonbExpressionBuilder.java | 4 --- .../perplexhub/rsql/jsonb/JsonbSupport.java | 28 +++++++++------ .../jsonb/JsonbExpressionBuilderTest.java | 35 ++++++++++++++++++- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java index 5d946820..ee2b17c0 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java @@ -161,10 +161,6 @@ public ArgValue convert(String s) { Map.entry(BETWEEN, "(%1$s >= %2$s && %1$s <= %3$s)") ); - private static final String JSONB_PATH_EXISTS = "jsonb_path_exists"; - - private static final String JSONB_PATH_EXISTS_TZ = "jsonb_path_exists_tz"; - private final ComparisonOperator operator; private final String keyPath; private final List values; diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java index 4c0e7be3..7d5ce5b2 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java @@ -1,14 +1,6 @@ package io.github.perplexhub.rsql.jsonb; -import static io.github.perplexhub.rsql.RSQLVisitorBase.getEntityManagerMap; - -import java.lang.reflect.Field; -import java.util.EnumSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - import cz.jirutka.rsql.parser.ast.ComparisonNode; import cz.jirutka.rsql.parser.ast.ComparisonOperator; import io.github.perplexhub.rsql.RSQLOperators; @@ -16,13 +8,19 @@ import io.github.perplexhub.rsql.ResolvedExpression; import jakarta.persistence.Column; import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Path; -import jakarta.persistence.criteria.Predicate; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.ManagedType; import org.springframework.orm.jpa.vendor.Database; +import java.lang.reflect.Field; +import java.util.EnumSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static io.github.perplexhub.rsql.RSQLVisitorBase.getEntityManagerMap; + /** * Support for jsonb expression. */ @@ -30,6 +28,16 @@ public class JsonbSupport { public static boolean DATE_TIME_SUPPORT = false; + /** + * Postgresql {@code jsonb_path_exists} function to use + */ + public static String JSONB_PATH_EXISTS = "jsonb_path_exists"; + + /** + * Postgresql {@code jsonb_path_exists_tz} function to use + */ + public static String JSONB_PATH_EXISTS_TZ = "jsonb_path_exists_tz"; + private static final Set JSON_SUPPORT = EnumSet.of(Database.POSTGRESQL); private static final Map NEGATE_OPERATORS = diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index c97d7d34..462cd731 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -12,7 +12,8 @@ import java.util.Objects; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.params.provider.Arguments.arguments; class JsonbExpressionBuilderTest { @@ -37,6 +38,27 @@ void testJsonbPathExpressionWithTemporal(ComparisonOperator operator, String key assertEquals(expectedJsonbPath, expression.jsonbPath()); } + @ParameterizedTest + @MethodSource("customized") + void testJsonbPathExpressionCustomized(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { + String jsonbPathExists = JsonbSupport.JSONB_PATH_EXISTS; + String jsonbPathExistsTz = JsonbSupport.JSONB_PATH_EXISTS_TZ; + try { + JsonbSupport.JSONB_PATH_EXISTS = "my_jsonb_path_exists"; + JsonbSupport.JSONB_PATH_EXISTS_TZ = "my_jsonb_path_exists_tz"; + JsonbSupport.DATE_TIME_SUPPORT = true; + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments); + var expression = builder.getJsonPathExpression(); + assertEquals(expectedJsonbFunction, expression.jsonbFunction()); + assertEquals(expectedJsonbPath, expression.jsonbPath()); + } catch (Exception e) { + throw e; + } finally { + JsonbSupport.JSONB_PATH_EXISTS = jsonbPathExists; + JsonbSupport.JSONB_PATH_EXISTS_TZ = jsonbPathExistsTz; + } + } + static Stream data() { return Stream.of( allOperators(), @@ -78,6 +100,17 @@ static Stream conversion() { ).filter(Objects::nonNull); } + static Stream customized() { + + return Stream.of( + arguments(RSQLOperators.EQUAL, "json.equal_key", Collections.singletonList("value"), "my_jsonb_path_exists", "$.equal_key ? (@ == \"value\")"), + arguments(RSQLOperators.GREATER_THAN, "json.greater_than_key", Collections.singletonList("value"), "my_jsonb_path_exists", "$.greater_than_key ? (@ > \"value\")"), + arguments(RSQLOperators.EQUAL, "json.equal_key", Collections.singletonList("1970-01-01T00:00:00.000"), "my_jsonb_path_exists", "$.equal_key ? (@.datetime() == \"1970-01-01T00:00:00.000\".datetime())"), + arguments(RSQLOperators.EQUAL, "json.equal_key", Collections.singletonList("1970-01-01T00:00:00.000Z"), "my_jsonb_path_exists_tz", "$.equal_key ? (@.datetime() == \"1970-01-01T00:00:00.000Z\".datetime())"), + null + ).filter(Objects::nonNull); + } + static Stream temporal() { return Stream.of( From 79601648369fd36e74f2bfae776e9b331d0d0525 Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Tue, 12 Aug 2025 14:06:08 +0200 Subject: [PATCH 2/8] undo imports changes --- .../perplexhub/rsql/jsonb/JsonbSupport.java | 18 ++++++++++-------- .../rsql/jsonb/JsonbExpressionBuilderTest.java | 3 +-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java index 7d5ce5b2..4372ce13 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java @@ -1,6 +1,14 @@ package io.github.perplexhub.rsql.jsonb; +import static io.github.perplexhub.rsql.RSQLVisitorBase.getEntityManagerMap; + +import java.lang.reflect.Field; +import java.util.EnumSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + import cz.jirutka.rsql.parser.ast.ComparisonNode; import cz.jirutka.rsql.parser.ast.ComparisonOperator; import io.github.perplexhub.rsql.RSQLOperators; @@ -8,19 +16,13 @@ import io.github.perplexhub.rsql.ResolvedExpression; import jakarta.persistence.Column; import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.ManagedType; import org.springframework.orm.jpa.vendor.Database; -import java.lang.reflect.Field; -import java.util.EnumSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static io.github.perplexhub.rsql.RSQLVisitorBase.getEntityManagerMap; - /** * Support for jsonb expression. */ diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index 462cd731..28965ab3 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -12,8 +12,7 @@ import java.util.Objects; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.params.provider.Arguments.arguments; class JsonbExpressionBuilderTest { From ca63c3fbd2ae80578211b0fb70ec266791c48216 Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Tue, 19 Aug 2025 18:34:57 +0200 Subject: [PATCH 3/8] move json query functions configuration to QuerySupport --- .../github/perplexhub/rsql/QuerySupport.java | 8 ++++++ .../rsql/RSQLJPAPredicateConverter.java | 21 +++++++++++++-- .../perplexhub/rsql/RSQLJPASupport.java | 3 ++- .../rsql/jsonb/JsonbExpressionBuilder.java | 15 ++++++++++- .../perplexhub/rsql/jsonb/JsonbSupport.java | 16 ++---------- .../rsql/RSQLJPASupportPostgresJsonTest.java | 26 ++++++++++++++++--- .../jsonb/JsonbExpressionBuilderTest.java | 21 ++++----------- 7 files changed, 73 insertions(+), 37 deletions(-) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java index bd8cb261..7fffe5d5 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java @@ -27,6 +27,14 @@ public class QuerySupport { private Map, List> propertyBlacklist; private Collection procedureWhiteList; private Collection procedureBlackList; + /** + * Postgresql {@code jsonb_path_exists} function to use + */ + private String jsonbPathExists; + /** + * Postgresql {@code jsonb_path_exists_tz} function to use + */ + private String jsonbPathExistsTz; public static class QuerySupportBuilder {} diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index 9dcb9211..bbd8d156 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -37,6 +37,8 @@ public class RSQLJPAPredicateConverter extends RSQLVisitorBase private final Collection procedureBlackList; private final boolean strictEquality; private final Character likeEscapeCharacter; + private final String jsonbPathExists; + private final String jsonbPathExistsTz; public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper) { this(builder, propertyPathMapper, null, null); @@ -54,6 +56,17 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map pr this(builder, propertyPathMapper, customPredicates, joinHints, procedureWhiteList, procedureBlackList, false, null); } + public RSQLJPAPredicateConverter(CriteriaBuilder builder, + Map propertyPathMapper, + List> customPredicates, + Map joinHints, + Collection proceduresWhiteList, + Collection proceduresBlackList, + boolean strictEquality, + Character likeEscapeCharacter) { + this(builder, propertyPathMapper, customPredicates, joinHints, proceduresWhiteList, proceduresBlackList, strictEquality, likeEscapeCharacter, null, null); + } + public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper, List> customPredicates, @@ -61,7 +74,9 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, Collection proceduresWhiteList, Collection proceduresBlackList, boolean strictEquality, - Character likeEscapeCharacter) { + Character likeEscapeCharacter, + String jsonbPathExists, + String jsonbPathExistsTz) { this.builder = builder; this.propertyPathMapper = propertyPathMapper != null ? propertyPathMapper : Collections.emptyMap(); this.customPredicates = customPredicates != null ? customPredicates.stream().collect(Collectors.toMap(RSQLCustomPredicate::getOperator, Function.identity(), (a, b) -> a)) : Collections.emptyMap(); @@ -70,6 +85,8 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, this.procedureBlackList = proceduresBlackList != null ? proceduresBlackList : Collections.emptyList(); this.strictEquality = strictEquality; this.likeEscapeCharacter = likeEscapeCharacter; + this.jsonbPathExists = jsonbPathExists; + this.jsonbPathExistsTz = jsonbPathExistsTz; } RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { @@ -244,7 +261,7 @@ private ResolvedExpression resolveExpression(ComparisonNode node, From root, Sel String jsonbPath = JsonbSupport.jsonPathOfSelector(attribute, jsonSelector); if(jsonbPath.contains(".")) { ComparisonNode jsonbNode = node.withSelector(jsonbPath); - return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path); + return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path, jsonbPathExists, jsonbPathExistsTz); } else { final Expression expression; if (path instanceof JpaExpression jpaExpression) { diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java index c9400c32..3ac4a1e5 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java @@ -127,7 +127,8 @@ public static Specification toSpecification(final QuerySupport querySuppo RSQLJPAPredicateConverter visitor = new RSQLJPAPredicateConverter(cb, querySupport.getPropertyPathMapper(), querySupport.getCustomPredicates(), querySupport.getJoinHints(), querySupport.getProcedureWhiteList(), querySupport.getProcedureBlackList(), - querySupport.isStrictEquality(), querySupport.getLikeEscapeCharacter()); + querySupport.isStrictEquality(), querySupport.getLikeEscapeCharacter(), + querySupport.getJsonbPathExists(), querySupport.getJsonbPathExistsTz()); visitor.setPropertyWhitelist(querySupport.getPropertyWhitelist()); visitor.setPropertyBlacklist(querySupport.getPropertyBlacklist()); diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java index ee2b17c0..66832d67 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java @@ -15,6 +15,9 @@ */ public class JsonbExpressionBuilder { + private final String jsonbPathExistsTz; + private final String jsonbPathExists; + /** * The base json type. */ @@ -161,11 +164,19 @@ public ArgValue convert(String s) { Map.entry(BETWEEN, "(%1$s >= %2$s && %1$s <= %3$s)") ); + private static final String JSONB_PATH_EXISTS = "jsonb_path_exists"; + + private static final String JSONB_PATH_EXISTS_TZ = "jsonb_path_exists_tz"; + private final ComparisonOperator operator; private final String keyPath; private final List values; JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args) { + this(operator, keyPath, args, null, null); + } + + JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args, String jsonbPathExists, String jsonbPathExistsTz) { this.operator = Objects.requireNonNull(operator); this.keyPath = Objects.requireNonNull(keyPath); if(FORBIDDEN_NEGATION.contains(operator)) { @@ -185,6 +196,8 @@ public ArgValue convert(String s) { throw new IllegalArgumentException("Operator " + operator + " requires at least one value"); } this.values = findMoreTypes(operator, candidateValues); + this.jsonbPathExistsTz = jsonbPathExistsTz == null ? JSONB_PATH_EXISTS_TZ : jsonbPathExistsTz; + this.jsonbPathExists = jsonbPathExists == null ? JSONB_PATH_EXISTS : jsonbPathExists; } /** @@ -206,7 +219,7 @@ public JsonbPathExpression getJsonPathExpression() { List templateArguments = new ArrayList<>(); templateArguments.add(valueReference); templateArguments.addAll(valuesToCompare); - var function = isDateTimeTz ? JSONB_PATH_EXISTS_TZ : JSONB_PATH_EXISTS; + var function = isDateTimeTz ? jsonbPathExistsTz : jsonbPathExists; var expression = String.format("%s ? %s", targetPath, String.format(comparisonTemplate, templateArguments.toArray())); return new JsonbPathExpression(function, expression); } diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java index 4372ce13..24dda423 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java @@ -16,9 +16,7 @@ import io.github.perplexhub.rsql.ResolvedExpression; import jakarta.persistence.Column; import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Path; -import jakarta.persistence.criteria.Predicate; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.ManagedType; import org.springframework.orm.jpa.vendor.Database; @@ -30,16 +28,6 @@ public class JsonbSupport { public static boolean DATE_TIME_SUPPORT = false; - /** - * Postgresql {@code jsonb_path_exists} function to use - */ - public static String JSONB_PATH_EXISTS = "jsonb_path_exists"; - - /** - * Postgresql {@code jsonb_path_exists_tz} function to use - */ - public static String JSONB_PATH_EXISTS_TZ = "jsonb_path_exists_tz"; - private static final Set JSON_SUPPORT = EnumSet.of(Database.POSTGRESQL); private static final Map NEGATE_OPERATORS = @@ -73,9 +61,9 @@ record JsonbPathExpression(String jsonbFunction, String jsonbPath) { } - public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath) { + public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath, String jsonbPathExists, String jsonbPathExistsTz) { var mayBeInvertedOperator = Optional.ofNullable(NEGATE_OPERATORS.get(node.getOperator())); - var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments()); + var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments(), jsonbPathExists, jsonbPathExistsTz); var expression = jsb.getJsonPathExpression(); return ResolvedExpression.ofJson(builder.function(expression.jsonbFunction, Boolean.class, attrPath, builder.literal(expression.jsonbPath)), mayBeInvertedOperator.isPresent()); diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index bd0d86af..b8c680bb 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -17,6 +17,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.orm.jpa.vendor.Database; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; import java.util.Collection; import java.util.HashMap; @@ -542,9 +543,9 @@ private static Stream meltedTimeZone() { var e3 = new PostgresJsonEntity(Map.of("a", "2020-01-01T00:00:00")); var allCases = List.of(e1, e2, e3); return Stream.of( - arguments(allCases, "properties.a=ge=1970-01-02T00:00:00+00:00", List.of(e2, e3)), - arguments(allCases, "properties.a=ge=1970-01-02T00:00:00+01:00", List.of(e2, e3)), - arguments(allCases, "properties.a=lt=2022-01-01T00:00:00+01:00", List.of(e1, e2, e3)), + arguments(allCases, "properties.a=ge=1970-01-02T00:00:00+00:00", List.of(e2, e3)), + arguments(allCases, "properties.a=ge=1970-01-02T00:00:00+01:00", List.of(e2, e3)), + arguments(allCases, "properties.a=lt=2022-01-01T00:00:00+01:00", List.of(e1, e2, e3)), null ).filter(Objects::nonNull); } @@ -721,4 +722,23 @@ private static Map nullMap(String key) { nullValue.put(key, null); return nullValue; } + + @Sql(statements = "CREATE OR REPLACE FUNCTION my_jsonb_path_exists(arg1 jsonb,arg2 jsonpath) RETURNS boolean AS 'SELECT $1 @? $2' LANGUAGE 'sql' IMMUTABLE;") + @Sql(statements = "DROP FUNCTION IF EXISTS my_jsonb_path_exists;", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) + @ParameterizedTest + @MethodSource("data") + void testJsonSearchCustomFunction(List entities, String rsql, List expected) { + //given + repository.saveAllAndFlush(entities); + + //when + List result = repository.findAll(toSpecification(new QuerySupport.QuerySupportBuilder().rsqlQuery(rsql).jsonbPathExists("my_jsonb_path_exists").build())); + + //then + assertThat(result) + .hasSameSizeAs(expected) + .containsExactlyInAnyOrderElementsOf(expected); + + entities.forEach(e -> e.setId(null)); + } } diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index 28965ab3..a801b987 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -40,22 +40,11 @@ void testJsonbPathExpressionWithTemporal(ComparisonOperator operator, String key @ParameterizedTest @MethodSource("customized") void testJsonbPathExpressionCustomized(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - String jsonbPathExists = JsonbSupport.JSONB_PATH_EXISTS; - String jsonbPathExistsTz = JsonbSupport.JSONB_PATH_EXISTS_TZ; - try { - JsonbSupport.JSONB_PATH_EXISTS = "my_jsonb_path_exists"; - JsonbSupport.JSONB_PATH_EXISTS_TZ = "my_jsonb_path_exists_tz"; - JsonbSupport.DATE_TIME_SUPPORT = true; - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments); - var expression = builder.getJsonPathExpression(); - assertEquals(expectedJsonbFunction, expression.jsonbFunction()); - assertEquals(expectedJsonbPath, expression.jsonbPath()); - } catch (Exception e) { - throw e; - } finally { - JsonbSupport.JSONB_PATH_EXISTS = jsonbPathExists; - JsonbSupport.JSONB_PATH_EXISTS_TZ = jsonbPathExistsTz; - } + JsonbSupport.DATE_TIME_SUPPORT = true; + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, "my_jsonb_path_exists", "my_jsonb_path_exists_tz"); + var expression = builder.getJsonPathExpression(); + assertEquals(expectedJsonbFunction, expression.jsonbFunction()); + assertEquals(expectedJsonbPath, expression.jsonbPath()); } static Stream data() { From c1b8c390295c63d66f3b46a7d304a5a5975ac98e Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Tue, 19 Aug 2025 18:38:40 +0200 Subject: [PATCH 4/8] updated QuerySupport.toString --- .../src/main/java/io/github/perplexhub/rsql/QuerySupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java index 7fffe5d5..5c156c19 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java @@ -40,7 +40,7 @@ public static class QuerySupportBuilder {} @Override public String toString() { - return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s", - rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist); + return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s,jsonbPathExists:%s,jsonbPathExistsTz:%s", + rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist, jsonbPathExists, jsonbPathExistsTz); } } From 40c4ce91f928c44e62e08258511fcbceedd2574d Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Wed, 27 Aug 2025 08:22:57 +0200 Subject: [PATCH 5/8] JsonbExtractor --- .../github/perplexhub/rsql/QuerySupport.java | 15 ++---- .../rsql/RSQLJPAPredicateConverter.java | 28 +++++------ .../perplexhub/rsql/RSQLJPASupport.java | 2 +- .../rsql/jsonb/JsonbExpressionBuilder.java | 12 +++-- .../perplexhub/rsql/jsonb/JsonbExtractor.java | 48 +++++++++++++++++++ .../rsql/jsonb/JsonbExtractorSupport.java | 26 ++++++++++ .../perplexhub/rsql/jsonb/JsonbSupport.java | 6 +-- .../rsql/RSQLJPASupportPostgresJsonTest.java | 10 ++-- .../jsonb/JsonbExpressionBuilderTest.java | 9 ++-- 9 files changed, 108 insertions(+), 48 deletions(-) create mode 100644 rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java create mode 100644 rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java index 5c156c19..bb89bcff 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java @@ -1,5 +1,6 @@ package io.github.perplexhub.rsql; +import io.github.perplexhub.rsql.jsonb.JsonbExtractor; import lombok.Builder; import lombok.Data; @@ -27,20 +28,14 @@ public class QuerySupport { private Map, List> propertyBlacklist; private Collection procedureWhiteList; private Collection procedureBlackList; - /** - * Postgresql {@code jsonb_path_exists} function to use - */ - private String jsonbPathExists; - /** - * Postgresql {@code jsonb_path_exists_tz} function to use - */ - private String jsonbPathExistsTz; + @Builder.Default + private JsonbExtractor jsonbExtractor = JsonbExtractor.DEFAULT; public static class QuerySupportBuilder {} @Override public String toString() { - return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s,jsonbPathExists:%s,jsonbPathExistsTz:%s", - rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist, jsonbPathExists, jsonbPathExistsTz); + return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s,jsonbExtractor:%s", + rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist, jsonbExtractor); } } diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index bbd8d156..f16969b8 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -8,6 +8,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import io.github.perplexhub.rsql.jsonb.JsonbExtractor; import io.github.perplexhub.rsql.jsonb.JsonbSupport; import jakarta.persistence.criteria.*; import jakarta.persistence.metamodel.Attribute; @@ -37,8 +38,7 @@ public class RSQLJPAPredicateConverter extends RSQLVisitorBase private final Collection procedureBlackList; private final boolean strictEquality; private final Character likeEscapeCharacter; - private final String jsonbPathExists; - private final String jsonbPathExistsTz; + private final JsonbExtractor jsonbExtractor; public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper) { this(builder, propertyPathMapper, null, null); @@ -64,19 +64,18 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, Collection proceduresBlackList, boolean strictEquality, Character likeEscapeCharacter) { - this(builder, propertyPathMapper, customPredicates, joinHints, proceduresWhiteList, proceduresBlackList, strictEquality, likeEscapeCharacter, null, null); + this(builder, propertyPathMapper, customPredicates, joinHints, proceduresWhiteList, proceduresBlackList, strictEquality, likeEscapeCharacter, JsonbExtractor.DEFAULT); } public RSQLJPAPredicateConverter(CriteriaBuilder builder, - Map propertyPathMapper, - List> customPredicates, - Map joinHints, - Collection proceduresWhiteList, - Collection proceduresBlackList, - boolean strictEquality, - Character likeEscapeCharacter, - String jsonbPathExists, - String jsonbPathExistsTz) { + Map propertyPathMapper, + List> customPredicates, + Map joinHints, + Collection proceduresWhiteList, + Collection proceduresBlackList, + boolean strictEquality, + Character likeEscapeCharacter, + JsonbExtractor jsonbExtractor) { this.builder = builder; this.propertyPathMapper = propertyPathMapper != null ? propertyPathMapper : Collections.emptyMap(); this.customPredicates = customPredicates != null ? customPredicates.stream().collect(Collectors.toMap(RSQLCustomPredicate::getOperator, Function.identity(), (a, b) -> a)) : Collections.emptyMap(); @@ -85,8 +84,7 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, this.procedureBlackList = proceduresBlackList != null ? proceduresBlackList : Collections.emptyList(); this.strictEquality = strictEquality; this.likeEscapeCharacter = likeEscapeCharacter; - this.jsonbPathExists = jsonbPathExists; - this.jsonbPathExistsTz = jsonbPathExistsTz; + this.jsonbExtractor = jsonbExtractor; } RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { @@ -261,7 +259,7 @@ private ResolvedExpression resolveExpression(ComparisonNode node, From root, Sel String jsonbPath = JsonbSupport.jsonPathOfSelector(attribute, jsonSelector); if(jsonbPath.contains(".")) { ComparisonNode jsonbNode = node.withSelector(jsonbPath); - return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path, jsonbPathExists, jsonbPathExistsTz); + return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path, jsonbExtractor); } else { final Expression expression; if (path instanceof JpaExpression jpaExpression) { diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java index 3ac4a1e5..c9273789 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java @@ -128,7 +128,7 @@ public static Specification toSpecification(final QuerySupport querySuppo querySupport.getCustomPredicates(), querySupport.getJoinHints(), querySupport.getProcedureWhiteList(), querySupport.getProcedureBlackList(), querySupport.isStrictEquality(), querySupport.getLikeEscapeCharacter(), - querySupport.getJsonbPathExists(), querySupport.getJsonbPathExistsTz()); + querySupport.getJsonbExtractor()); visitor.setPropertyWhitelist(querySupport.getPropertyWhitelist()); visitor.setPropertyBlacklist(querySupport.getPropertyBlacklist()); diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java index 66832d67..c9246e37 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java @@ -17,6 +17,7 @@ public class JsonbExpressionBuilder { private final String jsonbPathExistsTz; private final String jsonbPathExists; + private final boolean useDateTime; /** * The base json type. @@ -173,10 +174,10 @@ public ArgValue convert(String s) { private final List values; JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args) { - this(operator, keyPath, args, null, null); + this(operator, keyPath, args, JsonbExtractor.DEFAULT); } - JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args, String jsonbPathExists, String jsonbPathExistsTz) { + JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args, JsonbExtractor extractor) { this.operator = Objects.requireNonNull(operator); this.keyPath = Objects.requireNonNull(keyPath); if(FORBIDDEN_NEGATION.contains(operator)) { @@ -195,9 +196,10 @@ public ArgValue convert(String s) { if(REQUIRE_AT_LEAST_ONE_ARGUMENT.contains(operator) && candidateValues.isEmpty()) { throw new IllegalArgumentException("Operator " + operator + " requires at least one value"); } + this.useDateTime = extractor.useDateTime(); + this.jsonbPathExistsTz = extractor.pathExistsTz(); + this.jsonbPathExists = extractor.pathExists(); this.values = findMoreTypes(operator, candidateValues); - this.jsonbPathExistsTz = jsonbPathExistsTz == null ? JSONB_PATH_EXISTS_TZ : jsonbPathExistsTz; - this.jsonbPathExists = jsonbPathExists == null ? JSONB_PATH_EXISTS : jsonbPathExists; } /** @@ -243,7 +245,7 @@ private List findMoreTypes(ComparisonOperator operator, List v return values.stream().map(s -> new ArgValue(s, BaseJsonType.STRING)).toList(); } - List argConverters = DATE_TIME_SUPPORT ? + List argConverters = useDateTime ? List.of(DATE_TIME_CONVERTER, DATE_TIME_CONVERTER_TZ, NUMBER_CONVERTER, BOOLEAN_ARG_CONVERTER) : List.of(NUMBER_CONVERTER, BOOLEAN_ARG_CONVERTER); Optional candidateConverter = argConverters.stream() diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java new file mode 100644 index 00000000..1eb6d585 --- /dev/null +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java @@ -0,0 +1,48 @@ +package io.github.perplexhub.rsql.jsonb; + +/** + * jsonb expression configuration + */ +public interface JsonbExtractor { + + JsonbExtractor DEFAULT = new JsonbExtractor() { + + @Override + public String pathExists() { + return "jsonb_path_exists"; + } + + @Override + public String pathExistsTz() { + return "jsonb_path_exists_tz"; + } + + @Override + public boolean useDateTime() { + return false; + } + + @Override + public String toString() { + return String.format("pathExists:%s,pathExistsTz:%s,useDateTime:%b", pathExists(), pathExistsTz(), useDateTime()); + } + }; + + /** + * + * @return Postgresql {@code jsonb_path_exists} function to use + */ + String pathExists(); + + /** + * + * @return Postgresql {@code jsonb_path_exists_tz} function to use + */ + String pathExistsTz(); + + /** + * + * @return enable temporal values support + */ + boolean useDateTime(); +} diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java new file mode 100644 index 00000000..61f805f2 --- /dev/null +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java @@ -0,0 +1,26 @@ +package io.github.perplexhub.rsql.jsonb; + +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.Accessors; + +/** + * convenient way to define configuration, based on default values + */ +@Builder +@Accessors(fluent = true) +@Getter +public class JsonbExtractorSupport implements JsonbExtractor { + + @Builder.Default + private final String pathExists = JsonbExtractor.DEFAULT.pathExists(); + @Builder.Default + private final String pathExistsTz = JsonbExtractor.DEFAULT.pathExistsTz(); + @Builder.Default + private final boolean useDateTime = JsonbExtractor.DEFAULT.useDateTime(); + + @Override + public String toString() { + return String.format("pathExists:%s,pathExistsTz:%s,useDateTime:%b", pathExists, pathExistsTz, useDateTime); + } +} diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java index 24dda423..5664b719 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java @@ -26,8 +26,6 @@ */ public class JsonbSupport { - public static boolean DATE_TIME_SUPPORT = false; - private static final Set JSON_SUPPORT = EnumSet.of(Database.POSTGRESQL); private static final Map NEGATE_OPERATORS = @@ -61,9 +59,9 @@ record JsonbPathExpression(String jsonbFunction, String jsonbPath) { } - public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath, String jsonbPathExists, String jsonbPathExistsTz) { + public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath, JsonbExtractor extractor) { var mayBeInvertedOperator = Optional.ofNullable(NEGATE_OPERATORS.get(node.getOperator())); - var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments(), jsonbPathExists, jsonbPathExistsTz); + var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments(), extractor); var expression = jsb.getJsonPathExpression(); return ResolvedExpression.ofJson(builder.function(expression.jsonbFunction, Boolean.class, attrPath, builder.literal(expression.jsonbPath)), mayBeInvertedOperator.isPresent()); diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index b8c680bb..44805068 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -1,6 +1,6 @@ package io.github.perplexhub.rsql; -import io.github.perplexhub.rsql.jsonb.JsonbSupport; +import io.github.perplexhub.rsql.jsonb.JsonbExtractorSupport; import io.github.perplexhub.rsql.model.EntityWithJsonb; import io.github.perplexhub.rsql.model.JsonbEntity; import io.github.perplexhub.rsql.model.PostgresJsonEntity; @@ -48,7 +48,6 @@ class RSQLJPASupportPostgresJsonTest { void setup(@Autowired EntityManager em) { RSQLVisitorBase.setEntityManagerDatabase(Map.of(em, Database.POSTGRESQL)); clear(); - JsonbSupport.DATE_TIME_SUPPORT = false; } @AfterEach @@ -84,12 +83,11 @@ void testJsonSearch(List entities, String rsql, List entities, String rsql, List expected) { - JsonbSupport.DATE_TIME_SUPPORT = true; //given repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(rsql)); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbExtractor(JsonbExtractorSupport.builder().useDateTime(true).build()).build())); //then assertThat(result) @@ -162,7 +160,6 @@ void testJsonSearchOnMappedRelation(List jsonbEntities, String rsql @ParameterizedTest @MethodSource("sortByRelation") void testJsonSortOnRelation(List jsonbEntities, String rsql, List expected) { - JsonbSupport.DATE_TIME_SUPPORT = true; //given Collection entitiesWithJsonb = jsonbEntityRepository.saveAllAndFlush(jsonbEntities).stream() .map(jsonbEntity -> EntityWithJsonb.builder().jsonb(jsonbEntity).build()) @@ -184,7 +181,6 @@ void testJsonSortOnRelation(List jsonbEntities, String rsql, List jsonbEntities, String rsql, List expected) { - JsonbSupport.DATE_TIME_SUPPORT = true; //given Collection entitiesWithJsonb = jsonbEntityRepository.saveAllAndFlush(jsonbEntities).stream() .map(jsonbEntity -> EntityWithJsonb.builder().jsonb(jsonbEntity).build()) @@ -732,7 +728,7 @@ void testJsonSearchCustomFunction(List entities, String rsql repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(new QuerySupport.QuerySupportBuilder().rsqlQuery(rsql).jsonbPathExists("my_jsonb_path_exists").build())); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbExtractor(JsonbExtractorSupport.builder().pathExists("my_jsonb_path_exists").build()).build())); //then assertThat(result) diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index a801b987..c471d0d6 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -20,8 +20,7 @@ class JsonbExpressionBuilderTest { @ParameterizedTest @MethodSource("data") void testJsonbPathExpression(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbSupport.DATE_TIME_SUPPORT = false; - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractor.DEFAULT); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); @@ -30,8 +29,7 @@ void testJsonbPathExpression(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbSupport.DATE_TIME_SUPPORT = true; - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractorSupport.builder().useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); @@ -40,8 +38,7 @@ void testJsonbPathExpressionWithTemporal(ComparisonOperator operator, String key @ParameterizedTest @MethodSource("customized") void testJsonbPathExpressionCustomized(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbSupport.DATE_TIME_SUPPORT = true; - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, "my_jsonb_path_exists", "my_jsonb_path_exists_tz"); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractorSupport.builder().pathExists("my_jsonb_path_exists").pathExistsTz("my_jsonb_path_exists_tz").useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); From fd6b1d00631a62c3ddde91f8d94ee638a21f6572 Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Thu, 28 Aug 2025 09:03:38 +0200 Subject: [PATCH 6/8] JsonbExtractor -> JsonbConfiguration --- .../github/perplexhub/rsql/QuerySupport.java | 8 ++++---- .../rsql/RSQLJPAPredicateConverter.java | 12 +++++------ .../perplexhub/rsql/RSQLJPASupport.java | 2 +- ...Extractor.java => JsonbConfiguration.java} | 4 ++-- ...rt.java => JsonbConfigurationSupport.java} | 8 ++++---- .../rsql/jsonb/JsonbExpressionBuilder.java | 20 ++++++------------- .../perplexhub/rsql/jsonb/JsonbSupport.java | 4 ++-- .../rsql/RSQLJPASupportPostgresJsonTest.java | 6 +++--- .../jsonb/JsonbExpressionBuilderTest.java | 6 +++--- 9 files changed, 31 insertions(+), 39 deletions(-) rename rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/{JsonbExtractor.java => JsonbConfiguration.java} (90%) rename rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/{JsonbExtractorSupport.java => JsonbConfigurationSupport.java} (59%) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java index bb89bcff..68136c84 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/QuerySupport.java @@ -1,6 +1,6 @@ package io.github.perplexhub.rsql; -import io.github.perplexhub.rsql.jsonb.JsonbExtractor; +import io.github.perplexhub.rsql.jsonb.JsonbConfiguration; import lombok.Builder; import lombok.Data; @@ -29,13 +29,13 @@ public class QuerySupport { private Collection procedureWhiteList; private Collection procedureBlackList; @Builder.Default - private JsonbExtractor jsonbExtractor = JsonbExtractor.DEFAULT; + private JsonbConfiguration jsonbConfiguration = JsonbConfiguration.DEFAULT; public static class QuerySupportBuilder {} @Override public String toString() { - return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s,jsonbExtractor:%s", - rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist, jsonbExtractor); + return String.format("%s,distinct:%b,propertyPathMapper:%s,customPredicates:%d,joinHints:%s,propertyWhitelist:%s,propertyBlacklist:%s,jsonbConfiguration:%s", + rsqlQuery, distinct, propertyPathMapper, customPredicates == null ? 0 : customPredicates.size(), joinHints, propertyWhitelist, propertyBlacklist, jsonbConfiguration); } } diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java index f16969b8..f6162f84 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java @@ -8,7 +8,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -import io.github.perplexhub.rsql.jsonb.JsonbExtractor; +import io.github.perplexhub.rsql.jsonb.JsonbConfiguration; import io.github.perplexhub.rsql.jsonb.JsonbSupport; import jakarta.persistence.criteria.*; import jakarta.persistence.metamodel.Attribute; @@ -38,7 +38,7 @@ public class RSQLJPAPredicateConverter extends RSQLVisitorBase private final Collection procedureBlackList; private final boolean strictEquality; private final Character likeEscapeCharacter; - private final JsonbExtractor jsonbExtractor; + private final JsonbConfiguration jsonbConfiguration; public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper) { this(builder, propertyPathMapper, null, null); @@ -64,7 +64,7 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, Collection proceduresBlackList, boolean strictEquality, Character likeEscapeCharacter) { - this(builder, propertyPathMapper, customPredicates, joinHints, proceduresWhiteList, proceduresBlackList, strictEquality, likeEscapeCharacter, JsonbExtractor.DEFAULT); + this(builder, propertyPathMapper, customPredicates, joinHints, proceduresWhiteList, proceduresBlackList, strictEquality, likeEscapeCharacter, JsonbConfiguration.DEFAULT); } public RSQLJPAPredicateConverter(CriteriaBuilder builder, @@ -75,7 +75,7 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, Collection proceduresBlackList, boolean strictEquality, Character likeEscapeCharacter, - JsonbExtractor jsonbExtractor) { + JsonbConfiguration jsonbConfiguration) { this.builder = builder; this.propertyPathMapper = propertyPathMapper != null ? propertyPathMapper : Collections.emptyMap(); this.customPredicates = customPredicates != null ? customPredicates.stream().collect(Collectors.toMap(RSQLCustomPredicate::getOperator, Function.identity(), (a, b) -> a)) : Collections.emptyMap(); @@ -84,7 +84,7 @@ public RSQLJPAPredicateConverter(CriteriaBuilder builder, this.procedureBlackList = proceduresBlackList != null ? proceduresBlackList : Collections.emptyList(); this.strictEquality = strictEquality; this.likeEscapeCharacter = likeEscapeCharacter; - this.jsonbExtractor = jsonbExtractor; + this.jsonbConfiguration = jsonbConfiguration; } RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) { @@ -259,7 +259,7 @@ private ResolvedExpression resolveExpression(ComparisonNode node, From root, Sel String jsonbPath = JsonbSupport.jsonPathOfSelector(attribute, jsonSelector); if(jsonbPath.contains(".")) { ComparisonNode jsonbNode = node.withSelector(jsonbPath); - return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path, jsonbExtractor); + return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path, jsonbConfiguration); } else { final Expression expression; if (path instanceof JpaExpression jpaExpression) { diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java index c9273789..0ddadd63 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPASupport.java @@ -128,7 +128,7 @@ public static Specification toSpecification(final QuerySupport querySuppo querySupport.getCustomPredicates(), querySupport.getJoinHints(), querySupport.getProcedureWhiteList(), querySupport.getProcedureBlackList(), querySupport.isStrictEquality(), querySupport.getLikeEscapeCharacter(), - querySupport.getJsonbExtractor()); + querySupport.getJsonbConfiguration()); visitor.setPropertyWhitelist(querySupport.getPropertyWhitelist()); visitor.setPropertyBlacklist(querySupport.getPropertyBlacklist()); diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java similarity index 90% rename from rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java rename to rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java index 1eb6d585..9b032eb8 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractor.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java @@ -3,9 +3,9 @@ /** * jsonb expression configuration */ -public interface JsonbExtractor { +public interface JsonbConfiguration { - JsonbExtractor DEFAULT = new JsonbExtractor() { + JsonbConfiguration DEFAULT = new JsonbConfiguration() { @Override public String pathExists() { diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java similarity index 59% rename from rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java rename to rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java index 61f805f2..4c80ceaa 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExtractorSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java @@ -10,14 +10,14 @@ @Builder @Accessors(fluent = true) @Getter -public class JsonbExtractorSupport implements JsonbExtractor { +public final class JsonbConfigurationSupport implements JsonbConfiguration { @Builder.Default - private final String pathExists = JsonbExtractor.DEFAULT.pathExists(); + private final String pathExists = JsonbConfiguration.DEFAULT.pathExists(); @Builder.Default - private final String pathExistsTz = JsonbExtractor.DEFAULT.pathExistsTz(); + private final String pathExistsTz = JsonbConfiguration.DEFAULT.pathExistsTz(); @Builder.Default - private final boolean useDateTime = JsonbExtractor.DEFAULT.useDateTime(); + private final boolean useDateTime = JsonbConfiguration.DEFAULT.useDateTime(); @Override public String toString() { diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java index c9246e37..c9333f12 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilder.java @@ -15,9 +15,7 @@ */ public class JsonbExpressionBuilder { - private final String jsonbPathExistsTz; - private final String jsonbPathExists; - private final boolean useDateTime; + private final JsonbConfiguration configuration; /** * The base json type. @@ -165,19 +163,15 @@ public ArgValue convert(String s) { Map.entry(BETWEEN, "(%1$s >= %2$s && %1$s <= %3$s)") ); - private static final String JSONB_PATH_EXISTS = "jsonb_path_exists"; - - private static final String JSONB_PATH_EXISTS_TZ = "jsonb_path_exists_tz"; - private final ComparisonOperator operator; private final String keyPath; private final List values; JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args) { - this(operator, keyPath, args, JsonbExtractor.DEFAULT); + this(operator, keyPath, args, JsonbConfiguration.DEFAULT); } - JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args, JsonbExtractor extractor) { + JsonbExpressionBuilder(ComparisonOperator operator, String keyPath, List args, JsonbConfiguration configuration) { this.operator = Objects.requireNonNull(operator); this.keyPath = Objects.requireNonNull(keyPath); if(FORBIDDEN_NEGATION.contains(operator)) { @@ -196,9 +190,7 @@ public ArgValue convert(String s) { if(REQUIRE_AT_LEAST_ONE_ARGUMENT.contains(operator) && candidateValues.isEmpty()) { throw new IllegalArgumentException("Operator " + operator + " requires at least one value"); } - this.useDateTime = extractor.useDateTime(); - this.jsonbPathExistsTz = extractor.pathExistsTz(); - this.jsonbPathExists = extractor.pathExists(); + this.configuration = configuration; this.values = findMoreTypes(operator, candidateValues); } @@ -221,7 +213,7 @@ public JsonbPathExpression getJsonPathExpression() { List templateArguments = new ArrayList<>(); templateArguments.add(valueReference); templateArguments.addAll(valuesToCompare); - var function = isDateTimeTz ? jsonbPathExistsTz : jsonbPathExists; + var function = isDateTimeTz ? configuration.pathExistsTz() : configuration.pathExists(); var expression = String.format("%s ? %s", targetPath, String.format(comparisonTemplate, templateArguments.toArray())); return new JsonbPathExpression(function, expression); } @@ -245,7 +237,7 @@ private List findMoreTypes(ComparisonOperator operator, List v return values.stream().map(s -> new ArgValue(s, BaseJsonType.STRING)).toList(); } - List argConverters = useDateTime ? + List argConverters = configuration.useDateTime() ? List.of(DATE_TIME_CONVERTER, DATE_TIME_CONVERTER_TZ, NUMBER_CONVERTER, BOOLEAN_ARG_CONVERTER) : List.of(NUMBER_CONVERTER, BOOLEAN_ARG_CONVERTER); Optional candidateConverter = argConverters.stream() diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java index 5664b719..bac49e1c 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java @@ -59,9 +59,9 @@ record JsonbPathExpression(String jsonbFunction, String jsonbPath) { } - public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath, JsonbExtractor extractor) { + public static ResolvedExpression jsonbPathExistsExpression(CriteriaBuilder builder, ComparisonNode node, Path attrPath, JsonbConfiguration configuration) { var mayBeInvertedOperator = Optional.ofNullable(NEGATE_OPERATORS.get(node.getOperator())); - var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments(), extractor); + var jsb = new JsonbExpressionBuilder(mayBeInvertedOperator.orElse(node.getOperator()), node.getSelector(), node.getArguments(), configuration); var expression = jsb.getJsonPathExpression(); return ResolvedExpression.ofJson(builder.function(expression.jsonbFunction, Boolean.class, attrPath, builder.literal(expression.jsonbPath)), mayBeInvertedOperator.isPresent()); diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index 44805068..a5cd3156 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -1,6 +1,6 @@ package io.github.perplexhub.rsql; -import io.github.perplexhub.rsql.jsonb.JsonbExtractorSupport; +import io.github.perplexhub.rsql.jsonb.JsonbConfigurationSupport; import io.github.perplexhub.rsql.model.EntityWithJsonb; import io.github.perplexhub.rsql.model.JsonbEntity; import io.github.perplexhub.rsql.model.PostgresJsonEntity; @@ -87,7 +87,7 @@ void testJsonSearchOfTemporal(List entities, String rsql, Li repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbExtractor(JsonbExtractorSupport.builder().useDateTime(true).build()).build())); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfigurationSupport.builder().useDateTime(true).build()).build())); //then assertThat(result) @@ -728,7 +728,7 @@ void testJsonSearchCustomFunction(List entities, String rsql repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbExtractor(JsonbExtractorSupport.builder().pathExists("my_jsonb_path_exists").build()).build())); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfigurationSupport.builder().pathExists("my_jsonb_path_exists").build()).build())); //then assertThat(result) diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index c471d0d6..641648b4 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -20,7 +20,7 @@ class JsonbExpressionBuilderTest { @ParameterizedTest @MethodSource("data") void testJsonbPathExpression(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractor.DEFAULT); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfiguration.DEFAULT); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); @@ -29,7 +29,7 @@ void testJsonbPathExpression(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractorSupport.builder().useDateTime(true).build()); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfigurationSupport.builder().useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); @@ -38,7 +38,7 @@ void testJsonbPathExpressionWithTemporal(ComparisonOperator operator, String key @ParameterizedTest @MethodSource("customized") void testJsonbPathExpressionCustomized(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbExtractorSupport.builder().pathExists("my_jsonb_path_exists").pathExistsTz("my_jsonb_path_exists_tz").useDateTime(true).build()); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfigurationSupport.builder().pathExists("my_jsonb_path_exists").pathExistsTz("my_jsonb_path_exists_tz").useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); From 2f45d432f5f12f4f8158755f6626b96ab74fa40e Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Mon, 1 Sep 2025 09:31:08 +0200 Subject: [PATCH 7/8] JsonbConfigurationSupport record --- .../rsql/jsonb/JsonbConfigurationSupport.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java index 4c80ceaa..1c06143d 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java @@ -1,23 +1,20 @@ package io.github.perplexhub.rsql.jsonb; import lombok.Builder; -import lombok.Getter; -import lombok.experimental.Accessors; /** * convenient way to define configuration, based on default values */ @Builder -@Accessors(fluent = true) -@Getter -public final class JsonbConfigurationSupport implements JsonbConfiguration { +public record JsonbConfigurationSupport(String pathExists, String pathExistsTz, boolean useDateTime) implements JsonbConfiguration { - @Builder.Default - private final String pathExists = JsonbConfiguration.DEFAULT.pathExists(); - @Builder.Default - private final String pathExistsTz = JsonbConfiguration.DEFAULT.pathExistsTz(); - @Builder.Default - private final boolean useDateTime = JsonbConfiguration.DEFAULT.useDateTime(); + public static class JsonbConfigurationSupportBuilder { + JsonbConfigurationSupportBuilder() { + pathExists = DEFAULT.pathExists(); + pathExistsTz = DEFAULT.pathExistsTz(); + useDateTime = DEFAULT.useDateTime(); + } + } @Override public String toString() { From d06e76547c014ad550f2be1c676eeb607265609b Mon Sep 17 00:00:00 2001 From: Martin Fekete Date: Tue, 2 Sep 2025 10:42:24 +0200 Subject: [PATCH 8/8] JsonbConfigurationSupport -> JsonbConfiguration --- .../rsql/jsonb/JsonbConfiguration.java | 59 +++++++------------ .../rsql/jsonb/JsonbConfigurationSupport.java | 23 -------- .../rsql/RSQLJPASupportPostgresJsonTest.java | 6 +- .../jsonb/JsonbExpressionBuilderTest.java | 4 +- 4 files changed, 25 insertions(+), 67 deletions(-) delete mode 100644 rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java index 9b032eb8..4bec7cbb 100644 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java +++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfiguration.java @@ -1,48 +1,29 @@ package io.github.perplexhub.rsql.jsonb; +import lombok.Builder; + /** - * jsonb expression configuration + * convenient way to define configuration, based on default values + * + * @param pathExists Postgresql {@code jsonb_path_exists} function to use + * @param pathExistsTz Postgresql {@code jsonb_path_exists_tz} function to use + * @param useDateTime enable temporal values support */ -public interface JsonbConfiguration { - - JsonbConfiguration DEFAULT = new JsonbConfiguration() { +@Builder +public record JsonbConfiguration(String pathExists, String pathExistsTz, boolean useDateTime) { - @Override - public String pathExists() { - return "jsonb_path_exists"; - } - - @Override - public String pathExistsTz() { - return "jsonb_path_exists_tz"; - } + public static final JsonbConfiguration DEFAULT = JsonbConfiguration.builder().build(); - @Override - public boolean useDateTime() { - return false; + public static class JsonbConfigurationBuilder { + JsonbConfigurationBuilder() { + pathExists = "jsonb_path_exists"; + pathExistsTz = "jsonb_path_exists_tz"; + useDateTime = false; } + } - @Override - public String toString() { - return String.format("pathExists:%s,pathExistsTz:%s,useDateTime:%b", pathExists(), pathExistsTz(), useDateTime()); - } - }; - - /** - * - * @return Postgresql {@code jsonb_path_exists} function to use - */ - String pathExists(); - - /** - * - * @return Postgresql {@code jsonb_path_exists_tz} function to use - */ - String pathExistsTz(); - - /** - * - * @return enable temporal values support - */ - boolean useDateTime(); + @Override + public String toString() { + return String.format("pathExists:%s,pathExistsTz:%s,useDateTime:%b", pathExists, pathExistsTz, useDateTime); + } } diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java deleted file mode 100644 index 1c06143d..00000000 --- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbConfigurationSupport.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.perplexhub.rsql.jsonb; - -import lombok.Builder; - -/** - * convenient way to define configuration, based on default values - */ -@Builder -public record JsonbConfigurationSupport(String pathExists, String pathExistsTz, boolean useDateTime) implements JsonbConfiguration { - - public static class JsonbConfigurationSupportBuilder { - JsonbConfigurationSupportBuilder() { - pathExists = DEFAULT.pathExists(); - pathExistsTz = DEFAULT.pathExistsTz(); - useDateTime = DEFAULT.useDateTime(); - } - } - - @Override - public String toString() { - return String.format("pathExists:%s,pathExistsTz:%s,useDateTime:%b", pathExists, pathExistsTz, useDateTime); - } -} diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java index a5cd3156..7d968634 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportPostgresJsonTest.java @@ -1,6 +1,6 @@ package io.github.perplexhub.rsql; -import io.github.perplexhub.rsql.jsonb.JsonbConfigurationSupport; +import io.github.perplexhub.rsql.jsonb.JsonbConfiguration; import io.github.perplexhub.rsql.model.EntityWithJsonb; import io.github.perplexhub.rsql.model.JsonbEntity; import io.github.perplexhub.rsql.model.PostgresJsonEntity; @@ -87,7 +87,7 @@ void testJsonSearchOfTemporal(List entities, String rsql, Li repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfigurationSupport.builder().useDateTime(true).build()).build())); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfiguration.builder().useDateTime(true).build()).build())); //then assertThat(result) @@ -728,7 +728,7 @@ void testJsonSearchCustomFunction(List entities, String rsql repository.saveAllAndFlush(entities); //when - List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfigurationSupport.builder().pathExists("my_jsonb_path_exists").build()).build())); + List result = repository.findAll(toSpecification(QuerySupport.builder().rsqlQuery(rsql).jsonbConfiguration(JsonbConfiguration.builder().pathExists("my_jsonb_path_exists").build()).build())); //then assertThat(result) diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java index 641648b4..f0b15431 100644 --- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java +++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/jsonb/JsonbExpressionBuilderTest.java @@ -29,7 +29,7 @@ void testJsonbPathExpression(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfigurationSupport.builder().useDateTime(true).build()); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfiguration.builder().useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath()); @@ -38,7 +38,7 @@ void testJsonbPathExpressionWithTemporal(ComparisonOperator operator, String key @ParameterizedTest @MethodSource("customized") void testJsonbPathExpressionCustomized(ComparisonOperator operator, String keyPath, List arguments, String expectedJsonbFunction, String expectedJsonbPath) { - JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfigurationSupport.builder().pathExists("my_jsonb_path_exists").pathExistsTz("my_jsonb_path_exists_tz").useDateTime(true).build()); + JsonbExpressionBuilder builder = new JsonbExpressionBuilder(operator, keyPath, arguments, JsonbConfiguration.builder().pathExists("my_jsonb_path_exists").pathExistsTz("my_jsonb_path_exists_tz").useDateTime(true).build()); var expression = builder.getJsonPathExpression(); assertEquals(expectedJsonbFunction, expression.jsonbFunction()); assertEquals(expectedJsonbPath, expression.jsonbPath());