diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongodbDocumentSerializer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongodbDocumentSerializer.java index ffbc39d00..216092127 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongodbDocumentSerializer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongodbDocumentSerializer.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.support; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -49,6 +50,7 @@ import com.querydsl.mongodb.MongodbOps; * @author Mark Paluch * @author Christoph Strobl * @author Mikhail Kaduchka + * @author Enrique Leon Molina * @since 2.1 */ abstract class MongodbDocumentSerializer implements Visitor { @@ -181,19 +183,43 @@ abstract class MongodbDocumentSerializer implements Visitor { return asDocument(asDBKey(expr, 0), ""); } else if (op == Ops.AND) { - Map lhs = (Map) handle(expr.getArg(0)); - Map rhs = (Map) handle(expr.getArg(1)); + List> pendingDocuments = new LinkedList<>(); + for (int i = 0; i < 2; i++) { + Map document = (Map) handle(expr.getArg(i)); + if (document.keySet().size() == 1 && document.containsKey("$and")) { + pendingDocuments.addAll((Collection>) document.get("$and")); + } else { + pendingDocuments.add(document); + } + } - LinkedHashSet lhs2 = new LinkedHashSet<>(lhs.keySet()); - lhs2.retainAll(rhs.keySet()); + List> unmergeableDocuments = new LinkedList<>(); - if (lhs2.isEmpty()) { - lhs.putAll(rhs); - return lhs; + List> generatedDocuments = new LinkedList<>(); + + do { + Map lhs = pendingDocuments.remove(0); + + for (Map rhs : pendingDocuments) { + LinkedHashSet lhs2 = new LinkedHashSet<>(lhs.keySet()); + lhs2.retainAll(rhs.keySet()); + if (lhs2.isEmpty()) { + lhs.putAll(rhs); + } else { + unmergeableDocuments.add(rhs); + } + } + + generatedDocuments.add(lhs); + pendingDocuments = unmergeableDocuments; + unmergeableDocuments = new LinkedList<>(); + } while(!pendingDocuments.isEmpty()); + + if (generatedDocuments.size() == 1) { + return generatedDocuments.get(0); } else { - List list = new ArrayList<>(2); - list.add(handle(expr.getArg(0))); - list.add(handle(expr.getArg(1))); + List list = new ArrayList<>(expr.getArgs().size()); + list.addAll(generatedDocuments); return asDocument("$and", list); } @@ -212,9 +238,15 @@ abstract class MongodbDocumentSerializer implements Visitor { } else if (op == Ops.OR) { - List list = new ArrayList<>(2); - list.add(handle(expr.getArg(0))); - list.add(handle(expr.getArg(1))); + List list = new LinkedList<>(); + for (int i = 0; i < 2; i++) { + Map document = (Map) handle(expr.getArg(i)); + if (document.keySet().size() == 1 && document.containsKey("$or")) { + list.addAll((Collection) document.get("$or")); + } else { + list.add(document); + } + } return asDocument("$or", list); } else if (op == Ops.NE) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java index 15eae62aa..7ad95cf67 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java @@ -43,6 +43,7 @@ import org.springframework.data.mongodb.repository.QAddress; import org.springframework.data.mongodb.repository.QPerson; import com.querydsl.core.types.Ops; +import com.querydsl.core.types.Predicate; import com.querydsl.core.types.PredicateOperation; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanOperation; @@ -57,6 +58,7 @@ import com.querydsl.core.types.dsl.StringPath; * @author Christoph Strobl * @author Mark Paluch * @author Mikhail Kaduchka + * @author Enrique Leon Molina */ @ExtendWith(MockitoExtension.class) public class SpringDataMongodbSerializerUnitTests { @@ -174,6 +176,28 @@ public class SpringDataMongodbSerializerUnitTests { "{\"$and\": [{\"$or\": [{\"firstname\": \"John\"}, {\"firstname\": \"Sarah\"}]}, {\"$or\": [{\"lastname\": \"Smith\"}, {\"lastname\": \"Connor\"}]}]}")); } + @Test // DATAMONGO-2475 + public void chainedOrsInSameDocument() { + + Predicate predicate = QPerson.person.firstname.eq("firstname_value").or( + QPerson.person.lastname.eq("lastname_value")).or(QPerson.person.age.goe(30)).or( + QPerson.person.age.loe(20)).or(QPerson.person.uniqueId.isNull()); + + assertThat(serializer.handle(predicate)).isEqualTo(Document.parse( + "{\"$or\": [{\"firstname\": \"firstname_value\"}, {\"lastname\": \"lastname_value\"}, {\"age\": {\"$gte\": 30}}, {\"age\": {\"$lte\": 20}}, {\"uniqueId\": {\"$exists\": false}}]}")); + } + + @Test // DATAMONGO-2475 + public void chainedAndsInSameDocument() { + + Predicate predicate = QPerson.person.firstname.eq("firstname_value").and( + QPerson.person.lastname.eq("lastname_value")).and(QPerson.person.age.goe(30)).and( + QPerson.person.age.loe(20)).and(QPerson.person.uniqueId.isNull()); + + assertThat(serializer.handle(predicate)).isEqualTo(Document.parse( + "{\"$and\": [{\"firstname\": \"firstname_value\", \"lastname\": \"lastname_value\", \"age\": {\"$gte\": 30}, \"uniqueId\": {\"$exists\": false}}, {\"age\": {\"$lte\": 20}}]}")); + } + class Address { String id; String street;