DATAMONGO-2475 - MongodbDocumentSerializer: reduce nested ANDs and ORs.
Reduce the number of nested $and / $or clauses by combining them into a single document where possible. This allows to simplify the following statement
{
"$or": [
{
"$or": [
{
"$or": [
{
"$or": [
{ "firstname": "Hencxjo" },
{ "lastname": "Leon" }
]
},
{ "age": { "$lte": 30 } }
]
},
{ "age": { "$gte": 20 } }
]
},
{ "uniqueId": { "$exists": false } }
]
}
to just
{
"$or": [
{ "firstname": "Hencxjo" },
{ "lastname": "Leon" },
{ "age": { "$lte": 30 } },
{ "age": { "$gte": 20 } },
{ "uniqueId": { "$exists": false } }
]
}
Original Pull Request: #834
This commit is contained in:
committed by
Christoph Strobl
parent
a04821ff90
commit
7b5fea960f
@@ -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<Object, Void> {
|
||||
@@ -181,19 +183,43 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
return asDocument(asDBKey(expr, 0), "");
|
||||
} else if (op == Ops.AND) {
|
||||
|
||||
Map<Object, Object> lhs = (Map<Object, Object>) handle(expr.getArg(0));
|
||||
Map<Object, Object> rhs = (Map<Object, Object>) handle(expr.getArg(1));
|
||||
List<Map<Object, Object>> pendingDocuments = new LinkedList<>();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Map<Object, Object> document = (Map<Object, Object>) handle(expr.getArg(i));
|
||||
if (document.keySet().size() == 1 && document.containsKey("$and")) {
|
||||
pendingDocuments.addAll((Collection<Map<Object, Object>>) document.get("$and"));
|
||||
} else {
|
||||
pendingDocuments.add(document);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedHashSet<Object> lhs2 = new LinkedHashSet<>(lhs.keySet());
|
||||
lhs2.retainAll(rhs.keySet());
|
||||
List<Map<Object, Object>> unmergeableDocuments = new LinkedList<>();
|
||||
|
||||
if (lhs2.isEmpty()) {
|
||||
lhs.putAll(rhs);
|
||||
return lhs;
|
||||
List<Map<Object, Object>> generatedDocuments = new LinkedList<>();
|
||||
|
||||
do {
|
||||
Map<Object, Object> lhs = pendingDocuments.remove(0);
|
||||
|
||||
for (Map<Object, Object> rhs : pendingDocuments) {
|
||||
LinkedHashSet<Object> 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<Object> list = new ArrayList<>(2);
|
||||
list.add(handle(expr.getArg(0)));
|
||||
list.add(handle(expr.getArg(1)));
|
||||
List<Object> list = new ArrayList<>(expr.getArgs().size());
|
||||
list.addAll(generatedDocuments);
|
||||
return asDocument("$and", list);
|
||||
}
|
||||
|
||||
@@ -212,9 +238,15 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
|
||||
} else if (op == Ops.OR) {
|
||||
|
||||
List<Object> list = new ArrayList<>(2);
|
||||
list.add(handle(expr.getArg(0)));
|
||||
list.add(handle(expr.getArg(1)));
|
||||
List<Object> list = new LinkedList<>();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Map<Object, Object> document = (Map<Object, Object>) handle(expr.getArg(i));
|
||||
if (document.keySet().size() == 1 && document.containsKey("$or")) {
|
||||
list.addAll((Collection<Object>) document.get("$or"));
|
||||
} else {
|
||||
list.add(document);
|
||||
}
|
||||
}
|
||||
return asDocument("$or", list);
|
||||
|
||||
} else if (op == Ops.NE) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user