Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ interface Options {
String contained = "contained";
String all = "all";
String any = "any";
String key = "key";
String keys = "keys";
String json = "json";
}


Expand All @@ -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<Object> 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<Object> 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 extends AppendableSqlFragments> 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;
}
Expand All @@ -82,7 +116,7 @@ private Object convertObjectValue(RDBColumnMetadata column, Object value) {
}


private List<Object> convertList(Object value) {
private static List<Object> convertList(Object value) {
if (value == null) {
return Collections.emptyList();
}
Expand All @@ -99,20 +133,34 @@ private List<Object> convertList(Object value) {
}

public static String getOperator(Term term) {
return toOperator(getOperation(term));
}

private static PostgresqlJsonbTermFragmentBuilder.JsonbOperation getOperation(Term term) {
List<String> 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 {
Expand All @@ -123,8 +171,5 @@ private interface Operator {
String contains = "@>";
String contained = "<@";

static boolean needObject(String operator) {
return contains.equals(operator) || contained.equals(operator);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,61 @@ public void testJsonbExistTerm() {
.map(map -> String.valueOf(map.get("id")))
.collect(Collectors.toList());

List<String> 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<String> 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<String> 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<String> 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<String> 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()
Expand Down
Loading
Loading