From 73abbd024ae0b055e2f44e5d7b11e7e072fd4aaa Mon Sep 17 00:00:00 2001 From: zhouhao Date: Fri, 12 Jun 2026 10:29:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(PostgreSQL):=20=E6=94=AF=E6=8C=81=20jsonb?= =?UTF-8?q?=20=E9=80=9A=E7=94=A8=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...stgresqlJsonbExistTermFragmentBuilder.java | 107 ++++++++++----- .../PostgresqlJsonbTermFragmentBuilder.java | 124 ++++++++++++++++++ .../postgres/PostgresqlSchemaMetadata.java | 8 ++ .../postgres/PostgresqlBasicTest.java | 50 +++++++ ...stgresqlJsonbExistBuilderCoverageTest.java | 100 +++++++++++++- .../postgres/PostgresqlReactiveTests.java | 60 +++++++++ 6 files changed, 417 insertions(+), 32 deletions(-) create mode 100644 hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbTermFragmentBuilder.java diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistTermFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistTermFragmentBuilder.java index 69cb0556..10254004 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistTermFragmentBuilder.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistTermFragmentBuilder.java @@ -23,6 +23,9 @@ interface Options { String contained = "contained"; String all = "all"; String any = "any"; + String key = "key"; + String keys = "keys"; + String json = "json"; } @@ -33,35 +36,66 @@ public PostgresqlJsonbExistTermFragmentBuilder(String termType, String name) { @Override public SqlFragments createFragments(String columnFullName, RDBColumnMetadata column, Term term) { - String operator = getOperator(term); - if (Operator.needObject(operator)) { - PrepareSqlFragments fragments = PrepareSqlFragments.of(); - fragments.addSql(columnFullName, operator); - Object value = term.getValue(); - return appendPrepareOrNative(fragments, convertObjectValue(column, value)); - } - if (!Operator.base.equals(operator)) { - //转换值 - List values = convertList(term.getValue()); - if (values.isEmpty()) { - return EmptySqlFragments.INSTANCE; - } - return new BatchSqlFragments(4, 1) - .addSql(operator, "(", columnFullName, ",") - .addSql("array[") - .add(SqlUtils.createQuestionMarks(values.size())) - .addSql("])") - .addParameter(values); + return createFragments(columnFullName, column, getOperation(term), term.getValue()); + } + + static SqlFragments createFragments(String columnFullName, + RDBColumnMetadata column, + PostgresqlJsonbTermFragmentBuilder.JsonbOperation operation, + Object value) { + return switch (operation) { + case contains -> createObjectFragments(columnFullName, column, Operator.contains, value); + case contained -> createObjectFragments(columnFullName, column, Operator.contained, value); + case existsAll -> createArrayFragments(columnFullName, Operator.all, value); + case existsAny -> createArrayFragments(columnFullName, Operator.any, value); + case exists -> createBaseFragments(columnFullName, value); + }; + } + + private static SqlFragments createObjectFragments(String columnFullName, + RDBColumnMetadata column, + String operator, + Object value) { + PrepareSqlFragments fragments = PrepareSqlFragments.of(); + fragments.addSql(columnFullName, operator); + return appendPrepareOrNativeValue(fragments, convertObjectValue(column, value)); + } + + private static SqlFragments createArrayFragments(String columnFullName, String operator, Object value) { + //转换值 + List values = convertList(value); + if (values.isEmpty()) { + return EmptySqlFragments.INSTANCE; } + return new BatchSqlFragments(4, 1) + .addSql(operator, "(", columnFullName, ",") + .addSql("array[") + .add(SqlUtils.createQuestionMarks(values.size())) + .addSql("])") + .addParameter(values); + } + private static SqlFragments createBaseFragments(String columnFullName, Object value) { PrepareSqlFragments fragments = PrepareSqlFragments.of(); fragments.addSql(Operator.base, "(", columnFullName, ","); - appendPrepareOrNative(fragments, term.getValue()); + appendPrepareOrNativeValue(fragments, value); return fragments.addSql(")"); } + private static T appendPrepareOrNativeValue(T sql, Object value) { + if (value instanceof NativeSql) { + NativeSql nativeSql = ((NativeSql) value); + sql.addSql(nativeSql.getSql()) + .addParameter(nativeSql.getParameters()); + } else { + sql.add(SqlFragments.QUESTION_MARK) + .addParameter(value); + } + return sql; + } + @SneakyThrows - private Object convertObjectValue(RDBColumnMetadata column, Object value) { + private static Object convertObjectValue(RDBColumnMetadata column, Object value) { if (value instanceof NativeSql) { return value; } @@ -82,7 +116,7 @@ private Object convertObjectValue(RDBColumnMetadata column, Object value) { } - private List convertList(Object value) { + private static List convertList(Object value) { if (value == null) { return Collections.emptyList(); } @@ -99,20 +133,34 @@ private List convertList(Object value) { } public static String getOperator(Term term) { + return toOperator(getOperation(term)); + } + + private static PostgresqlJsonbTermFragmentBuilder.JsonbOperation getOperation(Term term) { List options = term.getOptions(); if (options.contains(Options.contains)) { - return Operator.contains; + return PostgresqlJsonbTermFragmentBuilder.JsonbOperation.contains; } if (options.contains(Options.contained)) { - return Operator.contained; + return PostgresqlJsonbTermFragmentBuilder.JsonbOperation.contained; } if (options.contains(Options.all)) { - return Operator.all; + return PostgresqlJsonbTermFragmentBuilder.JsonbOperation.existsAll; } - if (options.contains(Options.any)) { - return Operator.any; + if (options.contains(Options.any) || options.contains(Options.keys)) { + return PostgresqlJsonbTermFragmentBuilder.JsonbOperation.existsAny; } - return Operator.base; + return PostgresqlJsonbTermFragmentBuilder.JsonbOperation.exists; + } + + private static String toOperator(PostgresqlJsonbTermFragmentBuilder.JsonbOperation operation) { + return switch (operation) { + case contains -> Operator.contains; + case contained -> Operator.contained; + case existsAll -> Operator.all; + case existsAny -> Operator.any; + default -> Operator.base; + }; } private interface Operator { @@ -123,8 +171,5 @@ private interface Operator { String contains = "@>"; String contained = "<@"; - static boolean needObject(String operator) { - return contains.equals(operator) || contained.equals(operator); - } } } diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbTermFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbTermFragmentBuilder.java new file mode 100644 index 00000000..ded0b3a9 --- /dev/null +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbTermFragmentBuilder.java @@ -0,0 +1,124 @@ +package org.hswebframework.ezorm.rdb.supports.postgres; + +import org.hswebframework.ezorm.core.param.Term; +import org.hswebframework.ezorm.core.param.TermType; +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.BatchSqlFragments; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.SqlFragments; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.term.AbstractTermFragmentBuilder; + +import java.util.List; + +public class PostgresqlJsonbTermFragmentBuilder extends AbstractTermFragmentBuilder { + + public static final PostgresqlJsonbTermFragmentBuilder in = new PostgresqlJsonbTermFragmentBuilder( + TermType.in, + "jsonb包含任一键", + false + ); + + public static final PostgresqlJsonbTermFragmentBuilder notIn = new PostgresqlJsonbTermFragmentBuilder( + TermType.nin, + "jsonb不包含任一键", + true + ); + + public static final PostgresqlJsonbTermFragmentBuilder contains = new PostgresqlJsonbTermFragmentBuilder( + TermType.contains, + "jsonb包含", + false + ); + + public static final PostgresqlJsonbTermFragmentBuilder notContains = new PostgresqlJsonbTermFragmentBuilder( + TermType.ncontains, + "jsonb不包含", + true + ); + + public static final PostgresqlJsonbTermFragmentBuilder contained = new PostgresqlJsonbTermFragmentBuilder( + TermType.contained, + "jsonb被包含", + false + ); + + public static final PostgresqlJsonbTermFragmentBuilder notContained = new PostgresqlJsonbTermFragmentBuilder( + TermType.ncontained, + "jsonb不被包含", + true + ); + + public static final PostgresqlJsonbTermFragmentBuilder overlap = new PostgresqlJsonbTermFragmentBuilder( + TermType.overlap, + "jsonb包含任一键", + false + ); + + public static final PostgresqlJsonbTermFragmentBuilder notOverlap = new PostgresqlJsonbTermFragmentBuilder( + TermType.noverlap, + "jsonb不包含任一键", + true + ); + + private final boolean not; + + public PostgresqlJsonbTermFragmentBuilder(String termType, String name, boolean not) { + super(termType, name); + this.not = not; + } + + @Override + public SqlFragments createFragments(String columnFullName, RDBColumnMetadata column, Term term) { + SqlFragments fragments = PostgresqlJsonbExistTermFragmentBuilder.createFragments( + columnFullName, + column, + toJsonbOperation(term), + term.getValue() + ); + if (fragments.isEmpty() || !not) { + return fragments; + } + return new BatchSqlFragments(7, 1) + .add(SqlFragments.LEFT_BRACKET) + .addSql(columnFullName, "is null") + .add(SqlFragments.OR) + .addSql("not") + .add(SqlFragments.LEFT_BRACKET) + .add(fragments) + .add(SqlFragments.RIGHT_BRACKET) + .add(SqlFragments.RIGHT_BRACKET); + } + + private JsonbOperation toJsonbOperation(Term term) { + List options = term.getOptions(); + if (options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.key)) { + return JsonbOperation.exists; + } + if (options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.all)) { + return JsonbOperation.existsAll; + } + if (options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.any) || + options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.keys)) { + return JsonbOperation.existsAny; + } + if (options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.contained)) { + return JsonbOperation.contained; + } + if (options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.contains) || + options.contains(PostgresqlJsonbExistTermFragmentBuilder.Options.json)) { + return JsonbOperation.contains; + } + return switch (term.getTermType()) { + case TermType.contained, TermType.ncontained -> JsonbOperation.contained; + case TermType.in, TermType.nin, TermType.overlap, TermType.noverlap -> JsonbOperation.existsAny; + default -> JsonbOperation.contains; + }; + } + + enum JsonbOperation { + exists, + existsAll, + existsAny, + contains, + contained + } +} diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java index 63c88d30..f5eb575f 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java @@ -76,6 +76,14 @@ public RDBTableMetadata newTable(String name) { } if (column.getType() instanceof JsonbType) { column.addFeature(PostgresqlJsonbExistTermFragmentBuilder.exist); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.in); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.notIn); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.contains); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.notContains); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.contained); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.notContained); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.overlap); + column.addFeature(PostgresqlJsonbTermFragmentBuilder.notOverlap); } PostgresqlJsonTermFragmentBuilder.addJsonFeatures(column); }); diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBasicTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBasicTest.java index 39c9add5..af611ad4 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBasicTest.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlBasicTest.java @@ -114,11 +114,61 @@ public void testJsonbExistTerm() { .map(map -> String.valueOf(map.get("id"))) .collect(Collectors.toList()); + List commonContainsIds = operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contains", Collections.singletonMap("name", "JetLinks"))) + .fetch(ResultWrappers.mapStream()) + .sync() + .map(map -> String.valueOf(map.get("id"))) + .collect(Collectors.toList()); + + List commonContainedIds = operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contained", containedTarget)) + .fetch(ResultWrappers.mapStream()) + .sync() + .map(map -> String.valueOf(map.get("id"))) + .collect(Collectors.toList()); + + List commonAllKeyIds = operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contains$all", Arrays.asList("name", "age"))) + .fetch(ResultWrappers.mapStream()) + .sync() + .map(map -> String.valueOf(map.get("id"))) + .collect(Collectors.toList()); + + List commonOverlapIds = operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$overlap", Arrays.asList("name", "status"))) + .fetch(ResultWrappers.mapStream()) + .sync() + .map(map -> String.valueOf(map.get("id"))) + .collect(Collectors.toList()); + + List commonNotContainsIds = operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$ncontains", Collections.singletonMap("name", "JetLinks"))) + .fetch(ResultWrappers.mapStream()) + .sync() + .map(map -> String.valueOf(map.get("id"))) + .collect(Collectors.toList()); + Assert.assertEquals(Arrays.asList("1", "2"), existsIds); Assert.assertEquals(Arrays.asList("1", "2", "3"), anyIds); Assert.assertEquals(List.of("1"), allIds); Assert.assertEquals(List.of("1"), containsIds); Assert.assertEquals(List.of("1"), containedIds); + Assert.assertEquals(List.of("1"), commonContainsIds); + Assert.assertEquals(List.of("1"), commonContainedIds); + Assert.assertEquals(List.of("1"), commonAllKeyIds); + Assert.assertEquals(Arrays.asList("1", "2", "3"), commonOverlapIds); + Assert.assertEquals(Arrays.asList("2", "3"), commonNotContainsIds); } finally { try { operator.sql() diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistBuilderCoverageTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistBuilderCoverageTest.java index 87d0e4f0..ec557b6a 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistBuilderCoverageTest.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlJsonbExistBuilderCoverageTest.java @@ -6,6 +6,7 @@ import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; import org.hswebframework.ezorm.rdb.operator.builder.fragments.NativeSql; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.SimpleTermsFragmentBuilder; import org.hswebframework.ezorm.rdb.operator.builder.fragments.SqlFragments; import org.junit.Assert; import org.junit.Test; @@ -89,6 +90,99 @@ public void testContainsUsesColumnValueCodecBeforeJsonEncoding() { Assert.assertArrayEquals(new Object[]{null}, nullEncoded.getParameters()); } + + @Test + public void testJsonbCommonTermTypeDefaults() { + RDBColumnMetadata column = jsonbColumn(null); + Map value = new LinkedHashMap<>(); + value.put("name", "JetLinks"); + + SqlRequest contains = PostgresqlJsonbTermFragmentBuilder.contains + .createFragments("metadata", column, Term.of("metadata", "contains", value)) + .toRequest(); + Assert.assertEquals("metadata @> ?::jsonb", contains.getSql()); + Assert.assertEquals("{\"name\":\"JetLinks\"}", contains.getParameters()[0]); + + SqlRequest contained = PostgresqlJsonbTermFragmentBuilder.contained + .createFragments("metadata", column, Term.of("metadata", "contained", value)) + .toRequest(); + Assert.assertEquals("metadata <@ ?::jsonb", contained.getSql()); + Assert.assertEquals("{\"name\":\"JetLinks\"}", contained.getParameters()[0]); + + SqlRequest in = PostgresqlJsonbTermFragmentBuilder.in + .createFragments("metadata", column, Term.of("metadata", "in", Arrays.asList("name", "age"))) + .toRequest(); + Assert.assertEquals("jsonb_exists_any ( metadata , array[ ?,? ])", in.getSql()); + Assert.assertArrayEquals(new Object[]{"name", "age"}, in.getParameters()); + + SqlRequest overlap = PostgresqlJsonbTermFragmentBuilder.overlap + .createFragments("metadata", column, Term.of("metadata", "overlap", "name,age")) + .toRequest(); + Assert.assertEquals("jsonb_exists_any ( metadata , array[ ?,? ])", overlap.getSql()); + Assert.assertArrayEquals(new Object[]{"name", "age"}, overlap.getParameters()); + } + + @Test + public void testJsonbCommonTermTypeOptionsOverrideDefaultBehavior() { + RDBColumnMetadata column = jsonbColumn(null); + Map value = new LinkedHashMap<>(); + value.put("name", "JetLinks"); + + SqlRequest containsAllKeys = PostgresqlJsonbTermFragmentBuilder.contains + .createFragments("metadata", column, Term.of("metadata", "contains", Arrays.asList("name", "age"), "all")) + .toRequest(); + Assert.assertEquals("jsonb_exists_all ( metadata , array[ ?,? ])", containsAllKeys.getSql()); + Assert.assertArrayEquals(new Object[]{"name", "age"}, containsAllKeys.getParameters()); + + SqlRequest inJsonContains = PostgresqlJsonbTermFragmentBuilder.in + .createFragments("metadata", column, Term.of("metadata", "in", value, "json")) + .toRequest(); + Assert.assertEquals("metadata @> ?::jsonb", inJsonContains.getSql()); + Assert.assertEquals("{\"name\":\"JetLinks\"}", inJsonContains.getParameters()[0]); + + SqlRequest containsKey = PostgresqlJsonbTermFragmentBuilder.contains + .createFragments("metadata", column, Term.of("metadata", "contains", "name", "key")) + .toRequest(); + Assert.assertEquals("jsonb_exists ( metadata , ? )", containsKey.getSql()); + Assert.assertArrayEquals(new Object[]{"name"}, containsKey.getParameters()); + + SqlRequest negative = PostgresqlJsonbTermFragmentBuilder.notContains + .createFragments("metadata", column, Term.of("metadata", "ncontains", value)) + .toRequest(); + Assert.assertEquals("( metadata is null or not ( metadata @> ?::jsonb ) )", negative.getSql()); + Assert.assertEquals("{\"name\":\"JetLinks\"}", negative.getParameters()[0]); + } + + @Test + public void testJsonbCommonTermTypeRegisteredOnJsonbColumn() { + RDBColumnMetadata column = jsonbColumn(null); + Assert.assertTrue(column.findFeature(org.hswebframework.ezorm.rdb.operator.builder.fragments.TermFragmentBuilder.createFeatureId("contains")).isPresent()); + Assert.assertTrue(column.findFeature(org.hswebframework.ezorm.rdb.operator.builder.fragments.TermFragmentBuilder.createFeatureId("ncontains")).isPresent()); + Assert.assertTrue(column.findFeature(org.hswebframework.ezorm.rdb.operator.builder.fragments.TermFragmentBuilder.createFeatureId("in")).isPresent()); + Assert.assertTrue(column.findFeature(org.hswebframework.ezorm.rdb.operator.builder.fragments.TermFragmentBuilder.createFeatureId("overlap")).isPresent()); + } + + + + @Test + public void testJsonbCommonTermTypeWorksThroughColumnFeatureLookup() { + RDBTableMetadata table = jsonbTable(null); + + Term containsAll = new Term(); + containsAll.setColumn("metadata$contains$all"); + containsAll.setValue("name,age"); + SqlRequest containsAllRequest = SimpleTermsFragmentBuilder.createByTable(table, containsAll).toRequest(); + Assert.assertEquals("jsonb_exists_all ( \"metadata\" , array[ ?,? ])", containsAllRequest.getSql()); + Assert.assertArrayEquals(new Object[]{"name", "age"}, containsAllRequest.getParameters()); + + Term notOverlap = new Term(); + notOverlap.setColumn("metadata$noverlap"); + notOverlap.setValue(Arrays.asList("name", "status")); + SqlRequest notOverlapRequest = SimpleTermsFragmentBuilder.createByTable(table, notOverlap).toRequest(); + Assert.assertEquals("( \"metadata\" is null or not ( jsonb_exists_any ( \"metadata\" , array[ ?,? ]) ) )", notOverlapRequest.getSql()); + Assert.assertArrayEquals(new Object[]{"name", "status"}, notOverlapRequest.getParameters()); + } + @Test public void testOptionPriorityMatchesRepositoryColumnSyntax() { Term term = Term.of("metadata", "exist", Collections.singletonMap("name", "JetLinks"), "any", "all", "contained", "contains"); @@ -107,6 +201,10 @@ private SqlFragments create(RDBColumnMetadata column, Term term) { } private static RDBColumnMetadata jsonbColumn(ValueCodec codec) { + return jsonbTable(codec).getColumn("metadata").orElseThrow(); + } + + private static RDBTableMetadata jsonbTable(ValueCodec codec) { PostgresqlSchemaMetadata schema = new PostgresqlSchemaMetadata("public"); RDBTableMetadata table = schema.newTable("test_jsonb_exist"); RDBColumnMetadata column = table.newColumn(); @@ -114,7 +212,7 @@ private static RDBColumnMetadata jsonbColumn(ValueCodec codec) { column.setType(JsonbType.INSTANCE); column.setValueCodec(codec); table.addColumn(column); - return column; + return table; } private static class TestCodec implements ValueCodec { diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlReactiveTests.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlReactiveTests.java index 10f08355..3a290c95 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlReactiveTests.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlReactiveTests.java @@ -194,6 +194,66 @@ public void testJsonbExistTerm() { .as(StepVerifier::create) .assertNext(ids -> Assert.assertEquals(Collections.singletonList("1"), ids)) .verifyComplete(); + + operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contains", Collections.singletonMap("name", "JetLinks"))) + .fetch(ResultWrappers.mapStream()) + .reactive() + .map(map -> String.valueOf(map.get("id"))) + .collectList() + .as(StepVerifier::create) + .assertNext(ids -> Assert.assertEquals(Collections.singletonList("1"), ids)) + .verifyComplete(); + + operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contained", containedTarget)) + .fetch(ResultWrappers.mapStream()) + .reactive() + .map(map -> String.valueOf(map.get("id"))) + .collectList() + .as(StepVerifier::create) + .assertNext(ids -> Assert.assertEquals(Collections.singletonList("1"), ids)) + .verifyComplete(); + + operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$contains$all", Arrays.asList("name", "age"))) + .fetch(ResultWrappers.mapStream()) + .reactive() + .map(map -> String.valueOf(map.get("id"))) + .collectList() + .as(StepVerifier::create) + .assertNext(ids -> Assert.assertEquals(Collections.singletonList("1"), ids)) + .verifyComplete(); + + operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$overlap", Arrays.asList("name", "status"))) + .fetch(ResultWrappers.mapStream()) + .reactive() + .map(map -> String.valueOf(map.get("id"))) + .collectList() + .as(StepVerifier::create) + .assertNext(ids -> Assert.assertEquals(Arrays.asList("1", "2", "3"), ids)) + .verifyComplete(); + + operator.dml() + .query(jsonbTableName) + .select("id") + .where(q -> q.where("data$ncontains", Collections.singletonMap("name", "JetLinks"))) + .fetch(ResultWrappers.mapStream()) + .reactive() + .map(map -> String.valueOf(map.get("id"))) + .collectList() + .as(StepVerifier::create) + .assertNext(ids -> Assert.assertEquals(Arrays.asList("2", "3"), ids)) + .verifyComplete(); } finally { try { operator.sql()