Skip to content

Instantly share code, notes, and snippets.

@ascott42
Last active September 25, 2020 05:59
Show Gist options
  • Save ascott42/ea9b2dec6487b3e30b7c828ecc480a4f to your computer and use it in GitHub Desktop.
Save ascott42/ea9b2dec6487b3e30b7c828ecc480a4f to your computer and use it in GitHub Desktop.
Querydsl Predicate/OrderSpecifier into JSON
package com.asis.kis.persistence.core.querydsl;
import com.asis.kis.base.commons.stream.SimpleStreams;
import com.asis.kis.commons.util.JsonTypeConverter;
import com.asis.kis.commons.util.Jsons;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.CaseFormat;
import com.google.common.collect.Lists;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.core.types.dsl.PathBuilderValidator;
import restx.factory.Component;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import static com.querydsl.core.types.dsl.Expressions.constant;
/**
* Permits to deserialize a json predicate and orderBys into queryDSL object.
*/
public class DefaultQueryDslDeserializer implements QueryDslDeserializer {
/**
* the jackson object mapper
*/
protected final ObjectMapper mapper;
/**
* The value converter
*/
protected final JsonTypeConverter converter;
/**
* Default constructor of {@link DefaultQueryDslDeserializer}
*/
public DefaultQueryDslDeserializer() {
this.mapper = Jsons.buildMapper();
this.converter = new JsonTypeConverter();
}
/**
* Create a queryDSL predicate from the specified json
*
* @param json the json predicate
* @param entityClass the root entity class
* @return the queryDSL predicate
*/
public BooleanExpression deserializePredicate(String json, Class<?> entityClass) {
try {
JsonNode jsonNode = mapper.readTree(json);
return buildPredicates(jsonNode, entityClass);
} catch (Exception e) {
throw new DSLJsonDeserializerException("Unable to deserialize json in queryDSL predicate", e);
}
}
/**
* Create a collection of queryDSL order bys from the specified json
*
* @param json the json order bys
* @param entityClass the root entity class
* @return the collection of queryDSL order bys
*/
public List<OrderSpecifier> deserializeOrderBys(String json, Class<?> entityClass) {
try {
JsonNode jsonNode = mapper.readTree(json);
return buildOrderBys(jsonNode, entityClass);
} catch (Exception e) {
throw new DSLJsonDeserializerException("Unable to deserialize json in queryDSL order bys", e);
}
}
/**
* Build all order specifier from the specified JSONObject
*/
private List<OrderSpecifier> buildOrderBys(JsonNode rootNode, Class<?> entityClass) {
return SimpleStreams
.of(rootNode::fields)
.map(node -> buildPathAndOrderBy(node.getKey(), node.getValue(), entityClass))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
/**
* Build an order by expression
*/
protected List<OrderSpecifier> buildPathAndOrderBy(String key, JsonNode value, Class<?> entityClass) {
EntityPath entityPath = buildPath(key, entityClass);
return buildOrderBy(entityPath, value);
}
@SuppressWarnings("unchecked")
protected List<OrderSpecifier> buildOrderBy(EntityPath entityPath, JsonNode value) {
if (!value.isObject() && value.isTextual()) {
return Lists.newArrayList(new OrderSpecifier(convert(value.asText(), Order.class), entityPath));
}
throw new IllegalArgumentException("The object value have to be a string : " + value.toString());
}
/**
* Build all predicates from the specified map
*/
protected BooleanExpression buildPredicates(JsonNode rootNode, Class<?> entityClass) {
List<BooleanExpression> expressions = SimpleStreams
.of(rootNode::fields)
.map(node -> buildPredicate(node.getKey(), node.getValue(), entityClass))
.collect(Collectors.toList());
return Expressions.allOf(expressions.toArray(new BooleanExpression[expressions.size()]));
}
/**
* Build a predicate with the specified key and object value
*/
protected BooleanExpression buildPredicate(String key, JsonNode nodeValue, Class<?> entityClass) {
Optional<QueryDslOperator> operator = getOperator(key);
if (operator.isPresent()) {
// the key is an operator
// it is an array of and or or predicates
return buildOrAndPredicate(nodeValue, operator.get(), entityClass);
} else {
// build the path
EntityPath entityPath = buildPath(key, entityClass);
return buildBasePredicate(entityPath, nodeValue);
}
}
/**
* Build base predicate : path - operator - value
*/
protected BooleanExpression buildBasePredicate(EntityPath entityPath, JsonNode nodeValue) {
List<Expression> expressions = new ArrayList<>();
// add the path to the expressions list
expressions.add(entityPath);
QueryDslOperator valueOp = QueryDslOperator.EQ;
// if it is an object like { "$lt" : "value" }
if (nodeValue.isObject()) {
Iterator<Entry<String, JsonNode>> fields = nodeValue.fields();
while (fields.hasNext()) {
Entry<String, JsonNode> objectNode = fields.next();
// the key is the operator
valueOp = QueryDslOperator.getFromJson(objectNode.getKey());
if (valueOp.equals(QueryDslOperator.BETWEEN)) {
SimpleStreams
.of(() -> objectNode.getValue().elements())
.forEach(v -> expressions.add(createValue(v, entityPath)));
} else if (valueOp.equals(QueryDslOperator.IN) || valueOp.equals(QueryDslOperator.NOT_IN)) {
expressions.add(constant(
SimpleStreams.of(() -> objectNode.getValue().elements())
.map(v -> (Object) convert(v.asText(), entityPath.getType()))
.collect(Collectors.toList())));
} else {
// simple value
expressions.add(createValue(objectNode.getValue(), entityPath));
}
}
} else {
// it is a value
expressions.add(createValue(nodeValue, entityPath));
}
return Expressions.predicate(valueOp.getDSLOperator(), expressions.toArray(new Expression[expressions.size()]));
}
/**
* create a queryDSL constant value for the given json node
*/
protected Expression createValue(JsonNode value, EntityPath entityPath) {
return constant(convert(value.asText(), entityPath.getType()));
}
/**
* Build a predicate with operator and, or (array of predicates)
*/
protected BooleanExpression buildOrAndPredicate(JsonNode nodeValue, QueryDslOperator operator, Class<?> entityClass) {
List<BooleanExpression> booleanExpressions = new ArrayList<>();
// the key is an operator ($and - $or - ...)
if (nodeValue.isArray()) {
Iterator<JsonNode> elements = nodeValue.elements();
while (elements.hasNext()) {
JsonNode element = elements.next();
if (element.isObject()) {
booleanExpressions.add(buildPredicates(element, entityClass));
} else {
throw new IllegalStateException("expected to have an object instead of " + element.toString());
}
}
if (operator.equals(QueryDslOperator.AND)) {
return Expressions.allOf(booleanExpressions.toArray(new BooleanExpression[booleanExpressions.size()]));
} else if (operator.equals(QueryDslOperator.OR)) {
return Expressions.anyOf(booleanExpressions.toArray(new BooleanExpression[booleanExpressions.size()]));
} else {
throw new IllegalStateException("Don't supported operator here : " + operator);
}
} else {
throw new IllegalStateException("expected to have an array type instead of "
+ nodeValue.toString());
}
}
/**
* Cast the json string value into an object of the associated field type
*
* @param value the string value
* @param type the field type
* @return the value transformed into the associated field type
*/
protected <T> T convert(String value, Class<T> type) {
return converter.convert(value, type);
}
/**
* Build a queryDSL path from the specified string path
*
* @param path the string path. ex : content.product.packaging
* @param entityClass the entity root class
* @return the queryDSL path
*/
@SuppressWarnings("unchecked")
protected EntityPath<?> buildPath(String path, Class entityClass) {
String[] paths = path.split("\\.");
if (paths.length > 0) {
PathBuilder builder = new PathBuilder(entityClass, paths[0], PathBuilderValidator.FIELDS);
for (int i = 1; i < paths.length; i++) {
builder = builder.get(paths[i]);
}
return builder;
} else {
throw new IllegalArgumentException("The specified path is incorrect : " + path);
}
}
/**
* Get the JSONOperator from the json value
*
* @param operator the json operator value
* @return optional operator
*/
protected Optional<QueryDslOperator> getOperator(String operator) {
try {
return Optional.of(QueryDslOperator.getFromJson(operator));
} catch (Exception e) {
return Optional.empty();
}
}
/**
* QueryDslToJsonDeserializer exception
*/
public static class DSLJsonDeserializerException extends RuntimeException {
public DSLJsonDeserializerException(String message, Throwable cause) {
super(message, cause);
}
}
}
package com.asis.kis.persistence.core.querydsl;
import com.asis.kis.persistence.core.querydsl.domain.Content;
import com.google.common.collect.Lists;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier;
import org.joda.time.DateTime;
import org.junit.Test;
import java.util.Date;
import java.util.List;
import static com.asis.kis.persistence.core.querydsl.domain.QContent.content;
import static com.asis.kis.persistence.core.querydsl.domain.ReplenishmentMode.FIRST_EXPIRED_FIRST_OUT;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit test of {@link DefaultQueryDslDeserializer}
*
* @author aescot
*/
public class DefaultQueryDslDeserializerTest {
private DefaultQueryDslDeserializer deserializer = new DefaultQueryDslDeserializer();
@Test
public void should_deserialize_string_equal_predicate() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":\"ZONE2\"}", Content.class);
assertThat(content.support().location().zone.eq("ZONE2")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_string_integer() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.product.price\":{\"$lte\":699}}", Content.class);
assertThat(content.product().price.loe(699)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_long_and_gt_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$gt\":10}}", Content.class);
assertThat(content.quantity.gt(Long.parseLong("10"))).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_start_with_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":{\"$start\":\"ZONE\"}}", Content.class);
assertThat(content.support().location().zone.startsWith("ZONE")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_end_with_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":{\"$end\":\"ZONE\"}}", Content.class);
assertThat(content.support().location().zone.endsWith("ZONE")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_contains_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":{\"$contains\":\"ZONE\"}}", Content.class);
assertThat(content.support().location().zone.contains("ZONE")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_like_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":{\"$like\":\"ZONE\"}}", Content.class);
assertThat(content.support().location().zone.like("ZONE")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_lt_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$lt\":10}}", Content.class);
assertThat(content.quantity.lt(10)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_loe_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$lte\":10}}", Content.class);
assertThat(content.quantity.loe(10)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_gt_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$gt\":10}}", Content.class);
assertThat(content.quantity.gt(10)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_enum_field_type() {
Expression predicate = deserializer.deserializePredicate("{\"content.product.replenishmentMode\":\"FIRST_EXPIRED_FIRST_OUT\"}", Content.class);
assertThat(content.product().replenishmentMode.eq(FIRST_EXPIRED_FIRST_OUT)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_between_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$between\":[10,100]}}", Content.class);
assertThat(content.quantity.between(10, 100)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_date() {
Expression predicate = deserializer.deserializePredicate("{\"content.createdDate\":\"2015-05-01T14:00:00.000+0000\"}", Content.class);
Date date = new DateTime(2015, 5, 1, 16, 0).toDate();
assertThat(content.createdDate.eq(date)).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_and_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":\"ZONE1\",\"content.quantity\":{\"$gt\":10}"
+ ",\"content.product.replenishmentMode\":\"FIRST_EXPIRED_FIRST_OUT\"}", Content.class);
assertThat(content.support().location().zone.eq("ZONE1")
.and(content.quantity.gt(10))
.and(content.product().replenishmentMode.eq(FIRST_EXPIRED_FIRST_OUT))).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_or_operator() throws Exception {
Expression predicate = deserializer.deserializePredicate("{\"$or\":[{\"content.support.location.zone\":\"ZONE1\"},{\"content.quantity\":{\"$gt\":10}}]}", Content.class);
assertThat(content.support().location().zone.eq("ZONE1")
.or(content.quantity.gt(10))).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_nested_or_and_predicate() {
// nested or predicate
Expression predicate = deserializer.deserializePredicate("{\"content.support.location.zone\":\"ZONE1\",\"$or\":[{\"content.product.replenishmentMode\":\"FIRST_EXPIRED_FIRST_OUT\"},{\"content.quantity\":{\"$gt\":10}}]}", Content.class);
assertThat(content.support().location().zone.eq("ZONE1")
.and(content.product().replenishmentMode.eq(FIRST_EXPIRED_FIRST_OUT)
.or(content.quantity.gt(10)))).isEqualTo(predicate);
// or predicate with nested and
predicate = deserializer.deserializePredicate("{\"$or\":[{\"content.support.location.zone\":\"ZONE1\"},{\"content.product.replenishmentMode\":\"FIRST_EXPIRED_FIRST_OUT\",\"content.quantity\":{\"$gt\":10}}]}", Content.class);
assertThat(content.support().location().zone.eq("ZONE1")
.or(content.product().replenishmentMode.eq(FIRST_EXPIRED_FIRST_OUT)
.and(content.quantity.gt(10)))).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_in_operator() {
Expression predicate = deserializer.deserializePredicate("{\"content.packaging\":{\"$in\":[\"a\",\"b\",\"c\"]}}", Content.class);
assertThat(content.packaging.in("a", "b", "c")).isEqualTo(predicate);
}
@Test
public void should_deserialize_predicate_with_not_in_operator() {
Expression predicate = deserializer.deserializePredicate("{\"content.quantity\":{\"$nin\":[10,100,99]}}", Content.class);
assertThat(content.quantity.notIn(10,100,99)).isEqualTo(predicate);
}
@Test
public void should_deserialize_simple_order_by() {
List<OrderSpecifier> orders = deserializer.deserializeOrderBys("{\"content.quantity\":\"ASC\"}", Content.class);
assertThat(Lists.<OrderSpecifier>newArrayList(content.quantity.asc())).isEqualTo(orders);
}
@Test
public void should_deserialize_multiple_order_by() {
List<OrderSpecifier> orders = deserializer.deserializeOrderBys("{\"content.quantity\":\"ASC\",\"content.support.location.zone\":\"DESC\"}", Content.class);
assertThat(Lists.<OrderSpecifier>newArrayList(content.quantity.asc(), content.support().location().zone.desc())).isEqualTo(orders);
}
}
package com.asis.kis.persistence.core.querydsl;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import com.asis.kis.commons.util.Jsons;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.querydsl.core.types.*;
import restx.factory.Component;
/**
* Permits to serialize in json a queryDSL predicate and order
*/
public class DefaultQueryDslSerializer implements Visitor<Object, Void>, QueryDslSerializer {
protected final ObjectMapper mapper;
public DefaultQueryDslSerializer() {
this.mapper = Jsons.buildMapper();
}
/**
* Serialize the specified list of orderBys
*
* @param orderBys collection of orderBys
* @return the json of the queryDSl orders
*/
public JsonNode serializeAsNode(List<OrderSpecifier> orderBys) {
ObjectNode node = mapper.createObjectNode();
for (OrderSpecifier<?> orderBy : orderBys) {
Object key = orderBy.getTarget().accept(this, null);
node.put(key.toString(), orderBy.getOrder().toString());
}
return node;
}
/**
* Serialize the specified expression in the json object representation
*
* @param expression the queryDSL expression
* @return a expressions object (the json object representation)
*/
public JsonNode serializeAsNode(Expression<?> expression) {
return mapper.valueToTree(expression.accept(this, null));
}
protected ObjectNode asJsonObject(String key, Object value) {
return (ObjectNode) mapper.createObjectNode().set(key, mapper.valueToTree(value));
}
protected ObjectNode asJsonObject(QueryDslOperator op, Object value) {
return asJsonObject(op.getJson(), value);
}
private String asJsonKey(Operation<?> expr, int index) {
return (String) asJsonValue(expr, index);
}
private Object asJsonValue(Operation<?> expr, int index) {
return expr.getArg(index).accept(this, null);
}
@Nullable
@Override
public Object visit(Constant<?> expr, @Nullable Void context) {
if (Enum.class.isAssignableFrom(expr.getType())) {
return ((Enum<?>) expr.getConstant()).name();
} else {
return expr.getConstant();
}
}
@Nullable
@Override
public Object visit(Operation<?> expr, @Nullable Void context) {
Operator op = expr.getOperator();
if (op == Ops.EQ) {
return asJsonObject(asJsonKey(expr, 0), asJsonValue(expr, 1));
} else if (op == Ops.STRING_IS_EMPTY) {
return asJsonObject(asJsonKey(expr, 0), "");
} else if (op == Ops.AND) {
ObjectNode lhs = (ObjectNode) serializeAsNode(expr.getArg(0));
ObjectNode rhs = (ObjectNode) serializeAsNode(expr.getArg(1));
lhs.setAll(rhs);
return lhs;
} else if (op == Ops.OR) {
ArrayNode arrayNode = mapper.createArrayNode()
.add(serializeAsNode(expr.getArg(0)))
.add(serializeAsNode(expr.getArg(1)));
return asJsonObject(QueryDslOperator.getFromDsl(op), arrayNode);
} else if (op == Ops.BETWEEN) {
ArrayNode arrayNode = mapper.createArrayNode()
.add(serializeAsNode(expr.getArg(1)))
.add(serializeAsNode(expr.getArg(2)));
return asJsonObject(asJsonKey(expr, 0), asJsonObject(QueryDslOperator.getFromDsl(op), arrayNode));
} else if (op == Ops.IN || op.equals(Ops.NOT_IN)) {
int constIndex = 1;
if (Collection.class.isAssignableFrom(expr.getArg(constIndex).getType())) {
@SuppressWarnings("unchecked") //guarded by previous check
Collection<?> values = ((Constant<? extends Collection<?>>) expr.getArg(constIndex)).getConstant();
return asJsonObject(asJsonKey(expr, 0), asJsonObject(QueryDslOperator.getFromDsl(op), values.toArray()));
}
} else if (op == Ops.NE || op == Ops.STARTS_WITH || op == Ops.ENDS_WITH
|| op == Ops.STRING_CONTAINS || op == Ops.LIKE || op == Ops.LT
|| op == Ops.GT || op == Ops.LOE || op == Ops.GOE) {
return asJsonObject(asJsonKey(expr, 0), asJsonObject(QueryDslOperator.getFromDsl(op), asJsonValue(expr, 1)));
}
throw new UnsupportedOperationException("Illegal operation " + expr);
}
@Nullable
@Override
public Object visit(Path<?> expr, @Nullable Void context) {
PathMetadata metadata = expr.getMetadata();
if (metadata.getParent() != null) {
if (metadata.getPathType() == PathType.COLLECTION_ANY) {
return visit(metadata.getParent(), context);
} else {
String rv = getKeyForPath(metadata);
return visit(metadata.getParent(), context) + "." + rv;
}
}
return getKeyForPath(metadata);
}
protected String getKeyForPath(PathMetadata metadata) {
return metadata.getElement().toString();
}
@Nullable
@Override
public Object visit(SubQueryExpression<?> expr, @Nullable Void context) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public Object visit(TemplateExpression<?> expr, @Nullable Void context) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public Object visit(ParamExpression<?> expr, @Nullable Void context) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public Object visit(FactoryExpression<?> expr, @Nullable Void context) {
throw new UnsupportedOperationException();
}
}
package com.asis.kis.persistence.core.querydsl;
import com.asis.kis.persistence.core.querydsl.domain.ReplenishmentMode;
import org.joda.time.DateTime;
import org.junit.Test;
import java.util.Date;
import static com.asis.kis.persistence.core.querydsl.domain.QContent.content;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit test of {@link QueryDslSerializer}
*/
public class DefaultQueryDslSerializerTest {
private QueryDslSerializer serializer = new DefaultQueryDslSerializer();
@Test
public void should_serialize_in_json_equal_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.eq("ZONE1"));
assertThat(json).isEqualTo("{\"content.support.location.zone\":\"ZONE1\"}");
}
@Test
public void should_serialize_in_json_not_equal_predicate() throws Exception {
String json = serializer.serialize(content.quantity.ne((long) 100));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$ne\":100}}");
}
@Test
public void should_serialize_in_json_empty_string_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.isEmpty());
assertThat(json).isEqualTo("{\"content.support.location.zone\":\"\"}");
}
@Test
public void should_serialize_in_json_and_predicate() throws Exception {
// simple and
String json = serializer.serialize(
content.support().location().zone.eq("ZONE1")
.and(content.packaging.isEmpty()));
assertThat(json).isEqualTo("{\"content.support.location.zone\":\"ZONE1\",\"content.packaging\":\"\"}");
// multiple and
json = serializer.serialize(
content.support().location().zone.eq("ZONE1")
.and(content.packaging.isEmpty()
.and(content.quantity.gt(10))));
assertThat(json).isEqualTo("{\"content.support.location.zone\":\"ZONE1\",\"content.packaging\":\"\",\"content.quantity\":{\"$gt\":10}}");
}
@Test
public void should_serialize_in_json_or_predicate() throws Exception {
String json = serializer.serialize(
content.support().location().zone.eq("ZONE1")
.or(content.packaging.isEmpty()));
assertThat(json).isEqualTo("{\"$or\":[{\"content.support.location.zone\":\"ZONE1\"},{\"content.packaging\":\"\"}]}");
}
@Test
public void should_serialize_in_json_nested_or_and_predicate() {
String json = serializer.serialize(
content.support().location().zone.eq("ZONE1").and(content.packaging.isEmpty()
.or(content.quantity.gt(10))));
assertThat(json).isEqualTo("{\"content.support.location.zone\":\"ZONE1\",\"$or\":[{\"content.packaging\":\"\"},{\"content.quantity\":{\"$gt\":10}}]}");
// or predicate
json = serializer.serialize(
content.support().location().zone.eq("ZONE1")
.or(content.packaging.isEmpty()
.and(content.quantity.gt(10))));
assertThat(json).isEqualTo("{\"$or\":[{\"content.support.location.zone\":\"ZONE1\"},{\"content.packaging\":\"\",\"content.quantity\":{\"$gt\":10}}]}");
}
@Test
public void should_serialize_in_json_start_with_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.startsWith("ZONE"));
assertThat(json).isEqualTo("{\"content.support.location.zone\":{\"$start\":\"ZONE\"}}");
}
@Test
public void should_serialize_in_json_end_with_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.endsWith("ZONE"));
assertThat(json).isEqualTo("{\"content.support.location.zone\":{\"$end\":\"ZONE\"}}");
}
@Test
public void should_serialize_in_json_contains_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.contains("ZONE"));
assertThat(json).isEqualTo("{\"content.support.location.zone\":{\"$contains\":\"ZONE\"}}");
}
@Test
public void should_serialize_in_json_like_predicate() throws Exception {
String json = serializer.serialize(content.support().location().zone.like("ZONE"));
assertThat(json).isEqualTo("{\"content.support.location.zone\":{\"$like\":\"ZONE\"}}");
}
@Test
public void should_serialize_in_json_between_predicate() throws Exception {
String json = serializer.serialize(content.quantity.between(10, 100));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$between\":[10,100]}}");
}
@Test
public void should_serialize_in_json_lt_predicate() throws Exception {
String json = serializer.serialize(content.quantity.lt(10));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$lt\":10}}");
}
@Test
public void should_serialize_in_json_loe_predicate() throws Exception {
String json = serializer.serialize(content.quantity.loe(10));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$lte\":10}}");
}
@Test
public void should_serialize_in_json_gt_predicate() throws Exception {
String json = serializer.serialize(content.quantity.gt(10));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$gt\":10}}");
}
@Test
public void should_serialize_in_json_goe_predicate() throws Exception {
String json = serializer.serialize(content.quantity.goe(10));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$gte\":10}}");
}
@Test
public void should_serialize_in_json_orders() {
String json = serializer.serialize(content.quantity.asc());
assertThat(json).isEqualTo("{\"content.quantity\":\"ASC\"}");
}
@Test
public void should_serialize_in_json_multiple_orders() {
String json = serializer.serialize(content.quantity.asc(), content.support().location().zone.desc());
assertThat(json).isEqualTo("{\"content.quantity\":\"ASC\",\"content.support.location.zone\":\"DESC\"}");
}
@Test
public void should_serialize_predicate_with_enum_field() {
String json = serializer.serialize(content.product().replenishmentMode.eq(ReplenishmentMode.FIRST_EXPIRED_FIRST_OUT));
assertThat(json).isEqualTo("{\"content.product.replenishmentMode\":\"FIRST_EXPIRED_FIRST_OUT\"}");
}
@Test
public void should_serialize_predicate_with_date_field() {
Date date = new DateTime(2015, 5, 1, 16, 0).toDate();
String json = serializer.serialize(content.createdDate.eq(date));
assertThat(json).isEqualTo("{\"content.createdDate\":\"2015-05-01T14:00:00.000+0000\"}");
json = serializer.serialize(content.createdDate.lt(date));
assertThat(json).isEqualTo("{\"content.createdDate\":{\"$lt\":\"2015-05-01T14:00:00.000+0000\"}}");
}
@Test
public void should_serialize_predicate_with_in_operator() {
String json = serializer.serialize(content.packaging.in("a", "b", "c"));
assertThat(json).isEqualTo("{\"content.packaging\":{\"$in\":[\"a\",\"b\",\"c\"]}}");
}
@Test
public void should_serialize_predicate_with_not_in_operator() {
String json = serializer.serialize(content.quantity.notIn(10,100,99));
assertThat(json).isEqualTo("{\"content.quantity\":{\"$nin\":[10,100,99]}}");
}
}
package com.asis.kis.commons.util;
import java.util.Date;
import java.util.UUID;
import org.joda.time.DateTime;
import com.github.drapostolos.typeparser.TypeParser;
/**
* Permit to transform an a string or integer value into the specified type.
*
*/
public class JsonTypeConverter {
private TypeParser parser;
/**
* Default constructor of {@link JsonTypeConverter}
*/
public JsonTypeConverter() {
// create a TypeParser instance with default settings.
parser = TypeParser.newBuilder().build();
}
/**
* Convert the specified string into the specified type
*
* @param value the string value
* @param type expected type
* @return the converted object
*/
public <T> T convert(String value, Class<T> type) {
return doConvert(value, type);
}
/**
* Convert the specified integer into the specified type
*
* @param value the integer value
* @param type expected type
* @return the converted object
*/
public <T> T convert(Integer value, Class<T> type) {
return doConvert(value, type);
}
/**
* Convert the specified object into the specified type
*
* @param value the object value
* @param type expected type
* @return the converted object
*/
private <T> T doConvert(Object value, Class<T> type) {
// nothing to do - it is the good type
if (value.getClass().equals(type)) {
return (T) value;
}
// date converter
if (value instanceof String) {
if (type.equals(Date.class)) {
return (T) DateTime.parse((String) value).toDate();
} else if(type.equals(DateTime.class)) {
return (T) DateTime.parse((String) value);
}
else if(type.equals(UUID.class)) {
return (T) UUID.fromString(value.toString());
}
}
return parser.parse(value.toString(), type);
}
}
package com.asis.kis.persistence.core.querydsl;
import com.querydsl.core.types.Operator;
import com.querydsl.core.types.Ops;
/**
* This class represents all queryDSL operators and the associated Json operator.
* <p>
* It permits to link the json operator to the queryDSL operator.
*/
public enum QueryDslOperator {
EQ("$eq", Ops.EQ),
NE("$ne", Ops.NE),
LT("$lt", Ops.LT),
GT("$gt", Ops.GT),
LOE("$lte", Ops.LOE),
GOE("$gte", Ops.GOE),
LIKE("$like", Ops.LIKE),
STARTS_WITH("$start", Ops.STARTS_WITH),
ENDS_WITH("$end", Ops.ENDS_WITH),
AND("$and", Ops.AND),
OR("$or", Ops.OR),
BETWEEN("$between", Ops.BETWEEN),
IN("$in", Ops.IN),
NOT_IN("$nin", Ops.NOT_IN),
STRING_CONTAINS("$contains", Ops.STRING_CONTAINS);
/**
* the associated json operator
*/
private String json;
/**
* the associated queryDSL operator
*/
private Operator DSLOperator;
/**
* Default constructor of {@link QueryDslOperator}
*
* @param json the associated json operator
* @param DSLOperator the associated queryDSL operator
*/
QueryDslOperator(String json, Operator DSLOperator) {
this.json = json;
this.DSLOperator = DSLOperator;
}
/**
* Get the expression operator of the specified queryDSL operator
*
* @param op the queryDSL operator
* @return the expression operator
*/
public static QueryDslOperator getFromDsl(Operator op) {
for (QueryDslOperator queryDslOperator : QueryDslOperator.values()) {
if (queryDslOperator.getDSLOperator().equals(op)) {
return queryDslOperator;
}
}
throw new IllegalArgumentException("Illegal operator " + op);
}
/**
* get the expression operator from the json
*
* @param jsonValue the json value of the operator
* @return the expression operator
*/
public static QueryDslOperator getFromJson(String jsonValue) {
for (QueryDslOperator expressionOperator : QueryDslOperator.values()) {
if (expressionOperator.getJson().equals(jsonValue)) {
return expressionOperator;
}
}
throw new IllegalArgumentException("QueryDSL operator " + jsonValue + " unknown.");
}
public String getJson() {
return json;
}
public Operator getDSLOperator() {
return DSLOperator;
}
@Override
public String toString() {
return json;
}
}
@Ramblurr
Copy link

Ramblurr commented Mar 1, 2017

This is great! Thanks for sharing, but there are a few things missing before this can be useful:

  • JsonTypeConverter
  • ClassUniverse
  • ObjectMapper configuration

Any chance you can provide implementations of those?

@ascott42
Copy link
Author

ascott42 commented Jul 28, 2017

Sorry for delay, i was out of keyboard during some months.

I add the class JsonTypeConverter which permits to convert a json value into the specified java type. For doing this transformation, I have used this external library but you could do it by your own or find better I think. I am not a big fan of my class JsonTypeConverter, it could be improved.

ObjectMapper is build without any special option.

The method which use ClassUniverse has been deleted, no need it, it was for my special need sorry.

Hope it helps.

@Ahulkv
Copy link

Ahulkv commented Nov 16, 2017

Hi ,

I got few Reference problems can you please update on all the relevant classes

@GitJoedg
Copy link

Hi, missing Jsons, SimpleStreams classes

@ysykzheng
Copy link

hello, new version querydsl not include class QueryDslDeserializer, which version you used?
<querydsl.version>4.4.0</querydsl.version>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment