DATAMONGO-2475 - Polishing.
Fix reduction of $and/$or operations with more than two arguments. Original Pull Request: #834
This commit is contained in:
@@ -21,6 +21,8 @@ import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bson.BsonJavaScript;
|
||||
@@ -183,25 +185,16 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
return asDocument(asDBKey(expr, 0), "");
|
||||
} else if (op == Ops.AND) {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Queue<Map<Object, Object>> pendingDocuments = collectConnectorArgs("$and", expr);
|
||||
List<Map<Object, Object>> unmergeableDocuments = new ArrayList<>();
|
||||
List<Map<Object, Object>> generatedDocuments = new ArrayList<>();
|
||||
|
||||
List<Map<Object, Object>> unmergeableDocuments = new LinkedList<>();
|
||||
while (!pendingDocuments.isEmpty()) {
|
||||
|
||||
List<Map<Object, Object>> generatedDocuments = new LinkedList<>();
|
||||
|
||||
do {
|
||||
Map<Object, Object> lhs = pendingDocuments.remove(0);
|
||||
Map<Object, Object> lhs = pendingDocuments.poll();
|
||||
|
||||
for (Map<Object, Object> rhs : pendingDocuments) {
|
||||
LinkedHashSet<Object> lhs2 = new LinkedHashSet<>(lhs.keySet());
|
||||
Set<Object> lhs2 = new LinkedHashSet<>(lhs.keySet());
|
||||
lhs2.retainAll(rhs.keySet());
|
||||
if (lhs2.isEmpty()) {
|
||||
lhs.putAll(rhs);
|
||||
@@ -211,18 +204,11 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
}
|
||||
|
||||
generatedDocuments.add(lhs);
|
||||
pendingDocuments = unmergeableDocuments;
|
||||
pendingDocuments = new LinkedList<>(unmergeableDocuments);
|
||||
unmergeableDocuments = new LinkedList<>();
|
||||
} while(!pendingDocuments.isEmpty());
|
||||
|
||||
if (generatedDocuments.size() == 1) {
|
||||
return generatedDocuments.get(0);
|
||||
} else {
|
||||
List<Object> list = new ArrayList<>(expr.getArgs().size());
|
||||
list.addAll(generatedDocuments);
|
||||
return asDocument("$and", list);
|
||||
}
|
||||
|
||||
return generatedDocuments.size() == 1 ? generatedDocuments.get(0) : asDocument("$and", generatedDocuments);
|
||||
} else if (op == Ops.NOT) {
|
||||
// Handle the not's child
|
||||
Operation<?> subOperation = (Operation<?>) expr.getArg(0);
|
||||
@@ -237,18 +223,7 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
}
|
||||
|
||||
} else if (op == Ops.OR) {
|
||||
|
||||
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);
|
||||
|
||||
return asDocument("$or", collectConnectorArgs("$or", expr));
|
||||
} else if (op == Ops.NE) {
|
||||
|
||||
Path<?> path = (Path<?>) expr.getArg(0);
|
||||
@@ -466,4 +441,19 @@ abstract class MongodbDocumentSerializer implements Visitor<Object, Void> {
|
||||
public Object visit(ParamExpression<?> expr, Void context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private LinkedList<Map<Object, Object>> collectConnectorArgs(String operator, Operation<?> operation) {
|
||||
|
||||
LinkedList<Map<Object, Object>> pendingDocuments = new LinkedList<>();
|
||||
for (Expression<?> exp : operation.getArgs()) {
|
||||
Map<Object, Object> document = (Map<Object, Object>) handle(exp);
|
||||
if (document.keySet().size() == 1 && document.containsKey(operator)) {
|
||||
pendingDocuments.addAll((Collection<Map<Object, Object>>) document.get(operator));
|
||||
} else {
|
||||
pendingDocuments.add(document);
|
||||
}
|
||||
}
|
||||
return pendingDocuments;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.querydsl.core.types.ExpressionUtils.predicate;
|
||||
import static com.querydsl.core.types.dsl.Expressions.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -29,7 +30,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
@@ -179,25 +179,52 @@ public class SpringDataMongodbSerializerUnitTests {
|
||||
@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());
|
||||
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 chainedNestedOrsInSameDocument() {
|
||||
|
||||
Predicate predicate = QPerson.person.firstname.eq("firstname_value")
|
||||
.or(QPerson.person.lastname.eq("lastname_value")).or(QPerson.person.address.street.eq("spring"));
|
||||
|
||||
assertThat(serializer.handle(predicate)).isEqualTo(Document.parse(
|
||||
"{\"$or\": [{\"firstname\": \"firstname_value\"}, {\"lastname\": \"lastname_value\"}, {\"add.street\": \"spring\"}]}"));
|
||||
}
|
||||
|
||||
@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());
|
||||
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}}]}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2475
|
||||
void chainMultipleAndFlattensCorrectly() {
|
||||
|
||||
Document p1doc = Document.parse("{ \"$or\" : [ { \"firstname\" : \"fn\"}, { \"lastname\" : \"ln\" } ] }");
|
||||
Document p2doc = Document
|
||||
.parse("{ \"$or\" : [ { \"age\" : { \"$gte\" : 20 } }, { \"age\" : { \"$lte\" : 30} } ] }");
|
||||
Document p3doc = Document.parse("{ \"$or\" : [ { \"add.city\" : \"c\"}, { \"add.zipCode\" : \"0\" } ] }");
|
||||
Document expected = new Document("$and", Arrays.asList(p1doc, p2doc, p3doc));
|
||||
|
||||
Predicate predicate1 = QPerson.person.firstname.eq("fn").or(QPerson.person.lastname.eq("ln"));
|
||||
Predicate predicate2 = QPerson.person.age.goe(20).or(QPerson.person.age.loe(30));
|
||||
Predicate predicate3 = QPerson.person.address.city.eq("c").or(QPerson.person.address.zipCode.eq("0"));
|
||||
PredicateOperation testExpression = predicate(Ops.AND, predicate1, predicate2, predicate3);
|
||||
|
||||
assertThat(serializer.handle(testExpression)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
class Address {
|
||||
String id;
|
||||
String street;
|
||||
|
||||
Reference in New Issue
Block a user