diff --git a/pom.xml b/pom.xml
index 0ff20c5825..aa17486981 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index 9baccaa905..d9e456b116 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index 47a5b7aba7..cb11335699 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -6,7 +6,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
../pom.xml
@@ -50,7 +50,7 @@
org.springframework.data
spring-data-mongodb
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index e5c865ea08..3ef74f0491 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -13,7 +13,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index b86dc2808c..fdd6ad4a86 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.1.0.BUILD-SNAPSHOT
+ 2.1.0.DATAMONGO-1848-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java
index ef6c1423c9..15a9ae691b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java
@@ -62,7 +62,10 @@ public interface MongoDbFactory extends CodecRegistryProvider, MongoSessionProvi
* Get the legacy database entry point. Please consider {@link #getDb()} instead.
*
* @return
+ * @deprecated since 2.1, use {@link #getDb()}. This method will be removed with a future version as it works only
+ * with the legacy MongoDB driver.
*/
+ @Deprecated
DB getLegacyDb();
/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java
index 6b2202db43..73e9ecdb36 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java
@@ -22,15 +22,13 @@
import java.util.UUID;
import java.util.regex.Pattern;
-import org.bson.BsonObjectId;
+import org.bson.*;
import org.bson.types.Binary;
import org.bson.types.CodeWScope;
import org.bson.types.CodeWithScope;
import org.bson.types.Decimal128;
import org.bson.types.ObjectId;
import org.springframework.data.mapping.model.SimpleTypeHolder;
-import org.springframework.data.mongodb.util.MongoClientVersion;
-import org.springframework.util.ClassUtils;
import com.mongodb.DBRef;
@@ -62,6 +60,24 @@ public abstract class MongoSimpleTypes {
simpleTypes.add(Binary.class);
simpleTypes.add(UUID.class);
simpleTypes.add(Decimal128.class);
+
+ simpleTypes.add(BsonBinary.class);
+ simpleTypes.add(BsonBoolean.class);
+ simpleTypes.add(BsonDateTime.class);
+ simpleTypes.add(BsonDbPointer.class);
+ simpleTypes.add(BsonDecimal128.class);
+ simpleTypes.add(BsonDocument.class);
+ simpleTypes.add(BsonDocument.class);
+ simpleTypes.add(BsonDouble.class);
+ simpleTypes.add(BsonInt32.class);
+ simpleTypes.add(BsonInt64.class);
+ simpleTypes.add(BsonJavaScript.class);
+ simpleTypes.add(BsonJavaScriptWithScope.class);
+ simpleTypes.add(BsonObjectId.class);
+ simpleTypes.add(BsonRegularExpression.class);
+ simpleTypes.add(BsonString.class);
+ simpleTypes.add(BsonTimestamp.class);
+
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
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
new file mode 100644
index 0000000000..048233d5ba
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongodbDocumentSerializer.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import org.bson.BsonJavaScript;
+import org.bson.BsonRegularExpression;
+import org.bson.Document;
+import org.bson.types.ObjectId;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+import com.mongodb.DBRef;
+import com.querydsl.core.types.*;
+import com.querydsl.mongodb.MongodbOps;
+
+/**
+ *
+ * Serializes the given Querydsl query to a Document query for MongoDB.
+ *
+ *
+ * Original implementation source {@link com.querydsl.mongodb.MongodbSerializer} by {@literal The Querydsl Team}
+ * (http://www.querydsl.com/team ) licensed under the Apache License, Version
+ * 2.0.
+ *
+ * Modified to use {@link Document} instead of {@link com.mongodb.DBObject}, updated nullable types and code format. Use
+ * Bson specific types and add {@link QuerydslMongoOps#NO_MATCH}.
+ *
+ * @author laimw
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+abstract class MongodbDocumentSerializer implements Visitor {
+
+ @Nullable
+ Object handle(Expression> expression) {
+ return expression.accept(this, null);
+ }
+
+ /**
+ * Create the MongoDB specific query document.
+ *
+ * @param predicate must not be {@literal null}.
+ * @return empty {@link Document} by default.
+ */
+ Document toQuery(Predicate predicate) {
+
+ Object value = handle(predicate);
+
+ if (value == null) {
+ return new Document();
+ }
+
+ Assert.isInstanceOf(Document.class, value,
+ () -> String.format("Invalid type. Expected Document but found %s", value.getClass()));
+
+ return (Document) value;
+ }
+
+ /**
+ * Create the MongoDB specific sort document.
+ *
+ * @param orderBys must not be {@literal null}.
+ * @return empty {@link Document} by default.
+ */
+ Document toSort(List> orderBys) {
+
+ Document sort = new Document();
+
+ orderBys.forEach(orderSpecifier -> {
+
+ Object key = orderSpecifier.getTarget().accept(this, null);
+
+ Assert.notNull(key, () -> String.format("Mapped sort key for %s must not be null!", orderSpecifier));
+ sort.append(key.toString(), orderSpecifier.getOrder() == Order.ASC ? 1 : -1);
+ });
+
+ return sort;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.types.Visitor#visit(com.querydsl.core.types.Constant, java.lang.Void)
+ */
+ @Override
+ public Object visit(Constant> expr, Void context) {
+
+ if (!Enum.class.isAssignableFrom(expr.getType())) {
+ return expr.getConstant();
+ }
+
+ @SuppressWarnings("unchecked") // Guarded by previous check
+ Constant extends Enum>> expectedExpr = (Constant extends Enum>>) expr;
+ return expectedExpr.getConstant().name();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.types.Visitor#visit(com.querydsl.core.types.TemplateExpression, java.lang.Void)
+ */
+ @Override
+ public Object visit(TemplateExpression> expr, Void context) {
+ throw new UnsupportedOperationException();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.types.Visitor#visit(com.querydsl.core.types.FactoryExpression, java.lang.Void)
+ */
+ @Override
+ public Object visit(FactoryExpression> expr, Void context) {
+ throw new UnsupportedOperationException();
+ }
+
+ protected String asDBKey(Operation> expr, int index) {
+
+ String key = (String) asDBValue(expr, index);
+
+ Assert.hasText(key, () -> String.format("Mapped key must not be null nor empty for expression %s.", expr));
+ return key;
+ }
+
+ @Nullable
+ protected Object asDBValue(Operation> expr, int index) {
+ return expr.getArg(index).accept(this, null);
+ }
+
+ private String regexValue(Operation> expr, int index) {
+
+ Object value = expr.getArg(index).accept(this, null);
+
+ Assert.notNull(value, () -> String.format("Regex for %s must not be null.", expr));
+ return Pattern.quote(value.toString());
+ }
+
+ protected Document asDocument(String key, @Nullable Object value) {
+ return new Document(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object visit(Operation> expr, Void context) {
+
+ Operator op = expr.getOperator();
+ if (op == Ops.EQ) {
+
+ if (expr.getArg(0) instanceof Operation) {
+ Operation> lhs = (Operation>) expr.getArg(0);
+ if (lhs.getOperator() == Ops.COL_SIZE || lhs.getOperator() == Ops.ARRAY_SIZE) {
+ return asDocument(asDBKey(lhs, 0), asDocument("$size", asDBValue(expr, 1)));
+ } else {
+ throw new UnsupportedOperationException("Illegal operation " + expr);
+ }
+ } else if (expr.getArg(0) instanceof Path) {
+ Path> path = (Path>) expr.getArg(0);
+ Constant> constant = (Constant>) expr.getArg(1);
+ return asDocument(asDBKey(expr, 0), convert(path, constant));
+ }
+ } else if (op == Ops.STRING_IS_EMPTY) {
+ return asDocument(asDBKey(expr, 0), "");
+ } else if (op == Ops.AND) {
+
+ Map lhs = (Map) handle(expr.getArg(0));
+ Map rhs = (Map) handle(expr.getArg(1));
+
+ LinkedHashSet> lhs2 = new LinkedHashSet<>(lhs.entrySet());
+ lhs2.retainAll(rhs.entrySet());
+
+ if (lhs2.isEmpty()) {
+ lhs.putAll(rhs);
+ return lhs;
+ } else {
+ List list = new ArrayList<>(2);
+ list.add(handle(expr.getArg(0)));
+ list.add(handle(expr.getArg(1)));
+ return asDocument("$and", list);
+ }
+
+ } else if (op == Ops.NOT) {
+ // Handle the not's child
+ Operation> subOperation = (Operation>) expr.getArg(0);
+ Operator subOp = subOperation.getOperator();
+ if (subOp == Ops.IN) {
+ return visit(
+ ExpressionUtils.operation(Boolean.class, Ops.NOT_IN, subOperation.getArg(0), subOperation.getArg(1)),
+ context);
+ } else {
+ Document arg = (Document) handle(expr.getArg(0));
+ return negate(arg);
+ }
+
+ } else if (op == Ops.OR) {
+
+ List list = new ArrayList<>(2);
+ list.add(handle(expr.getArg(0)));
+ list.add(handle(expr.getArg(1)));
+ return asDocument("$or", list);
+
+ } else if (op == Ops.NE) {
+
+ Path> path = (Path>) expr.getArg(0);
+ Constant> constant = (Constant>) expr.getArg(1);
+ return asDocument(asDBKey(expr, 0), asDocument("$ne", convert(path, constant)));
+
+ } else if (op == Ops.STARTS_WITH) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression("^" + regexValue(expr, 1)));
+ } else if (op == Ops.STARTS_WITH_IC) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression("^" + regexValue(expr, 1), "i"));
+ } else if (op == Ops.ENDS_WITH) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(regexValue(expr, 1) + "$"));
+ } else if (op == Ops.ENDS_WITH_IC) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(regexValue(expr, 1) + "$", "i"));
+ } else if (op == Ops.EQ_IGNORE_CASE) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression("^" + regexValue(expr, 1) + "$", "i"));
+ } else if (op == Ops.STRING_CONTAINS) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(".*" + regexValue(expr, 1) + ".*"));
+ } else if (op == Ops.STRING_CONTAINS_IC) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(".*" + regexValue(expr, 1) + ".*", "i"));
+ } else if (op == Ops.MATCHES) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(asDBValue(expr, 1).toString()));
+ } else if (op == Ops.MATCHES_IC) {
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(asDBValue(expr, 1).toString(), "i"));
+ } else if (op == Ops.LIKE) {
+
+ String regex = ExpressionUtils.likeToRegex((Expression) expr.getArg(1)).toString();
+ return asDocument(asDBKey(expr, 0), new BsonRegularExpression(regex));
+ } else if (op == Ops.BETWEEN) {
+
+ Document value = new Document("$gte", asDBValue(expr, 1));
+ value.append("$lte", asDBValue(expr, 2));
+ return asDocument(asDBKey(expr, 0), value);
+ } else if (op == Ops.IN) {
+
+ int constIndex = 0;
+ int exprIndex = 1;
+ if (expr.getArg(1) instanceof Constant>) {
+ constIndex = 1;
+ exprIndex = 0;
+ }
+ if (Collection.class.isAssignableFrom(expr.getArg(constIndex).getType())) {
+ @SuppressWarnings("unchecked") // guarded by previous check
+ Collection> values = ((Constant extends Collection>>) expr.getArg(constIndex)).getConstant();
+ return asDocument(asDBKey(expr, exprIndex), asDocument("$in", values));
+ } else {
+ Path> path = (Path>) expr.getArg(exprIndex);
+ Constant> constant = (Constant>) expr.getArg(constIndex);
+ return asDocument(asDBKey(expr, exprIndex), convert(path, constant));
+ }
+ } else if (op == Ops.NOT_IN) {
+
+ int constIndex = 0;
+ int exprIndex = 1;
+ if (expr.getArg(1) instanceof Constant>) {
+
+ constIndex = 1;
+ exprIndex = 0;
+ }
+ if (Collection.class.isAssignableFrom(expr.getArg(constIndex).getType())) {
+
+ @SuppressWarnings("unchecked") // guarded by previous check
+ Collection> values = ((Constant extends Collection>>) expr.getArg(constIndex)).getConstant();
+ return asDocument(asDBKey(expr, exprIndex), asDocument("$nin", values));
+ } else {
+
+ Path> path = (Path>) expr.getArg(exprIndex);
+ Constant> constant = (Constant>) expr.getArg(constIndex);
+ return asDocument(asDBKey(expr, exprIndex), asDocument("$ne", convert(path, constant)));
+ }
+ } else if (op == Ops.COL_IS_EMPTY) {
+
+ List list = new ArrayList<>(2);
+ list.add(asDocument(asDBKey(expr, 0), new ArrayList()));
+ list.add(asDocument(asDBKey(expr, 0), asDocument("$exists", false)));
+ return asDocument("$or", list);
+ } else if (op == Ops.LT) {
+ return asDocument(asDBKey(expr, 0), asDocument("$lt", asDBValue(expr, 1)));
+ } else if (op == Ops.GT) {
+ return asDocument(asDBKey(expr, 0), asDocument("$gt", asDBValue(expr, 1)));
+ } else if (op == Ops.LOE) {
+ return asDocument(asDBKey(expr, 0), asDocument("$lte", asDBValue(expr, 1)));
+ } else if (op == Ops.GOE) {
+ return asDocument(asDBKey(expr, 0), asDocument("$gte", asDBValue(expr, 1)));
+ } else if (op == Ops.IS_NULL) {
+ return asDocument(asDBKey(expr, 0), asDocument("$exists", false));
+ } else if (op == Ops.IS_NOT_NULL) {
+ return asDocument(asDBKey(expr, 0), asDocument("$exists", true));
+ } else if (op == Ops.CONTAINS_KEY) {
+
+ Path> path = (Path>) expr.getArg(0);
+ Expression> key = expr.getArg(1);
+ return asDocument(visit(path, context) + "." + key.toString(), asDocument("$exists", true));
+ } else if (op == MongodbOps.NEAR) {
+ return asDocument(asDBKey(expr, 0), asDocument("$near", asDBValue(expr, 1)));
+ } else if (op == MongodbOps.NEAR_SPHERE) {
+ return asDocument(asDBKey(expr, 0), asDocument("$nearSphere", asDBValue(expr, 1)));
+ } else if (op == MongodbOps.ELEM_MATCH) {
+ return asDocument(asDBKey(expr, 0), asDocument("$elemMatch", asDBValue(expr, 1)));
+ } else if (op == QuerydslMongoOps.NO_MATCH) {
+ return new Document("$where", new BsonJavaScript("function() { return false }"));
+ }
+
+ throw new UnsupportedOperationException("Illegal operation " + expr);
+ }
+
+ private Object negate(Document arg) {
+
+ List list = new ArrayList<>();
+ for (Map.Entry entry : arg.entrySet()) {
+
+ if (entry.getKey().equals("$or")) {
+ list.add(asDocument("$nor", entry.getValue()));
+ } else if (entry.getKey().equals("$and")) {
+
+ List list2 = new ArrayList<>();
+ for (Object o : ((Collection) entry.getValue())) {
+ list2.add(negate((Document) o));
+ }
+ list.add(asDocument("$or", list2));
+ } else if (entry.getValue() instanceof Pattern || entry.getValue() instanceof BsonRegularExpression) {
+ list.add(asDocument(entry.getKey(), asDocument("$not", entry.getValue())));
+ } else if (entry.getValue() instanceof Document) {
+ list.add(negate(entry.getKey(), (Document) entry.getValue()));
+ } else {
+ list.add(asDocument(entry.getKey(), asDocument("$ne", entry.getValue())));
+ }
+ }
+ return list.size() == 1 ? list.get(0) : asDocument("$or", list);
+ }
+
+ private Object negate(String key, Document value) {
+
+ if (value.size() == 1) {
+ return asDocument(key, asDocument("$not", value));
+ } else {
+
+ List list2 = new ArrayList<>();
+ for (Map.Entry entry2 : value.entrySet()) {
+ list2.add(asDocument(key, asDocument("$not", asDocument(entry2.getKey(), entry2.getValue()))));
+ }
+
+ return asDocument("$or", list2);
+ }
+ }
+
+ protected Object convert(Path> property, Constant> constant) {
+
+ if (isReference(property)) {
+ return asReference(constant.getConstant());
+ } else if (isId(property)) {
+
+ if (isReference(property.getMetadata().getParent())) {
+ return asReferenceKey(property.getMetadata().getParent().getType(), constant.getConstant());
+ } else if (constant.getType().equals(String.class) && isImplicitObjectIdConversion()) {
+
+ String id = (String) constant.getConstant();
+ return ObjectId.isValid(id) ? new ObjectId(id) : id;
+ }
+ }
+ return visit(constant, null);
+ }
+
+ protected boolean isImplicitObjectIdConversion() {
+ return true;
+ }
+
+ protected DBRef asReferenceKey(Class> entity, Object id) {
+ // TODO override in subclass
+ throw new UnsupportedOperationException();
+ }
+
+ protected abstract DBRef asReference(Object constant);
+
+ protected abstract boolean isReference(@Nullable Path> arg);
+
+ protected boolean isId(Path> arg) {
+ // TODO override in subclass
+ return false;
+ }
+
+ @Override
+ public String visit(Path> expr, Void context) {
+
+ PathMetadata metadata = expr.getMetadata();
+
+ if (metadata.getParent() != null) {
+
+ Path> parent = metadata.getParent();
+ if (parent.getMetadata().getPathType() == PathType.DELEGATE) {
+ parent = parent.getMetadata().getParent();
+ }
+ if (metadata.getPathType() == PathType.COLLECTION_ANY) {
+ return visit(parent, context);
+ } else if (parent.getMetadata().getPathType() != PathType.VARIABLE) {
+
+ String rv = getKeyForPath(expr, metadata);
+ String parentStr = visit(parent, context);
+ return rv != null ? parentStr + "." + rv : parentStr;
+ }
+ }
+ return getKeyForPath(expr, metadata);
+ }
+
+ protected String getKeyForPath(Path> expr, PathMetadata metadata) {
+ return metadata.getElement().toString();
+ }
+
+ @Override
+ public Object visit(SubQueryExpression> expr, Void context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object visit(ParamExpression> expr, Void context) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAbstractMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAbstractMongodbQuery.java
new file mode 100644
index 0000000000..74ff1789b9
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAbstractMongodbQuery.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import java.util.List;
+
+import org.bson.Document;
+import org.springframework.lang.Nullable;
+
+import com.querydsl.core.DefaultQueryMetadata;
+import com.querydsl.core.QueryModifiers;
+import com.querydsl.core.SimpleQuery;
+import com.querydsl.core.support.QueryMixin;
+import com.querydsl.core.types.Expression;
+import com.querydsl.core.types.FactoryExpression;
+import com.querydsl.core.types.OrderSpecifier;
+import com.querydsl.core.types.ParamExpression;
+import com.querydsl.core.types.Predicate;
+
+/**
+ * {@code QuerydslAbstractMongodbQuery} provides a base class for general Querydsl query implementation.
+ *
+ * Original implementation source {@link com.querydsl.mongodb.AbstractMongodbQuery} by {@literal The Querydsl Team}
+ * (http://www.querydsl.com/team ) licensed under the Apache License, Version
+ * 2.0.
+ *
+ * Modified for usage with {@link MongodbDocumentSerializer}.
+ *
+ * @param concrete subtype
+ * @author laimw
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+public abstract class QuerydslAbstractMongodbQuery>
+ implements SimpleQuery {
+
+ private final MongodbDocumentSerializer serializer;
+ private final QueryMixin queryMixin;
+
+ /**
+ * Create a new MongodbQuery instance
+ *
+ * @param serializer serializer
+ */
+ @SuppressWarnings("unchecked")
+ QuerydslAbstractMongodbQuery(MongodbDocumentSerializer serializer) {
+
+ this.queryMixin = new QueryMixin<>((Q) this, new DefaultQueryMetadata(), false);
+ this.serializer = serializer;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#distinct()
+ */
+ @Override
+ public Q distinct() {
+ return queryMixin.distinct();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.FilteredClause#where(com.querydsl.core.types.Predicate[])
+ */
+ @Override
+ public Q where(Predicate... e) {
+ return queryMixin.where(e);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#limit(long)
+ */
+ @Override
+ public Q limit(long limit) {
+ return queryMixin.limit(limit);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#offset()
+ */
+ @Override
+ public Q offset(long offset) {
+ return queryMixin.offset(offset);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#restrict(com.querydsl.core.QueryModifiers)
+ */
+ @Override
+ public Q restrict(QueryModifiers modifiers) {
+ return queryMixin.restrict(modifiers);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#orderBy(com.querydsl.core.types.OrderSpecifier)
+ */
+ @Override
+ public Q orderBy(OrderSpecifier>... o) {
+ return queryMixin.orderBy(o);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.SimpleQuery#set(com.querydsl.core.types.ParamExpression, Object)
+ */
+ @Override
+ public Q set(ParamExpression param, T value) {
+ return queryMixin.set(param, value);
+ }
+
+ /**
+ * Compute the actual projection {@link Document} from a given projectionExpression by serializing the contained
+ * {@link Expression expressions} individually.
+ *
+ * @param projectionExpression the computed projection {@link Document}.
+ * @return never {@literal null}. An empty {@link Document} by default.
+ * @see MongodbDocumentSerializer#handle(Expression)
+ */
+ protected Document createProjection(@Nullable Expression> projectionExpression) {
+
+ if (!(projectionExpression instanceof FactoryExpression)) {
+ return new Document();
+ }
+
+ Document projection = new Document();
+ ((FactoryExpression>) projectionExpression).getArgs().stream() //
+ .filter(Expression.class::isInstance) //
+ .map(Expression.class::cast) //
+ .map(serializer::handle) //
+ .forEach(it -> projection.append(it.toString(), 1));
+
+ return projection;
+ }
+
+ /**
+ * Compute the filer {@link Document} from the given {@link Predicate}.
+ *
+ * @param predicate can be {@literal null}.
+ * @return an empty {@link Document} if predicate is {@literal null}.
+ * @see MongodbDocumentSerializer#toQuery(Predicate)
+ */
+ protected Document createQuery(@Nullable Predicate predicate) {
+
+ if (predicate == null) {
+ return new Document();
+ }
+
+ return serializer.toQuery(predicate);
+ }
+
+ /**
+ * Compute the sort {@link Document} from the given list of {@link OrderSpecifier order specifiers}.
+ *
+ * @param orderSpecifiers can be {@literal null}.
+ * @return an empty {@link Document} if predicate is {@literal null}.
+ * @see MongodbDocumentSerializer#toSort(List)
+ */
+ protected Document createSort(List> orderSpecifiers) {
+ return serializer.toSort(orderSpecifiers);
+ }
+
+ /**
+ * Get the actual {@link QueryMixin} delegate.
+ *
+ * @return
+ */
+ QueryMixin getQueryMixin() {
+ return queryMixin;
+ }
+
+ /**
+ * Get the where definition as a Document instance
+ *
+ * @return
+ */
+ Document asDocument() {
+ return createQuery(queryMixin.getMetadata().getWhere());
+ }
+
+ @Override
+ public String toString() {
+ return asDocument().toString();
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAnyEmbeddedBuilder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAnyEmbeddedBuilder.java
new file mode 100644
index 0000000000..af9380ba2f
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslAnyEmbeddedBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import java.util.Collection;
+
+import com.querydsl.core.support.QueryMixin;
+import com.querydsl.core.types.ExpressionUtils;
+import com.querydsl.core.types.Path;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.mongodb.MongodbOps;
+
+/**
+ * {@code QuerydslAnyEmbeddedBuilder} is a builder for constraints on embedded objects.
+ *
+ * Original implementation source {@link com.querydsl.mongodb.AnyEmbeddedBuilder} by {@literal The Querydsl Team}
+ * (http://www.querydsl.com/team ) licensed under the Apache License, Version
+ * 2.0.
+ *
+ * Modified for usage with {@link QuerydslAbstractMongodbQuery}.
+ *
+ * @param query type
+ * @author tiwe
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+public class QuerydslAnyEmbeddedBuilder, K> {
+
+ private final QueryMixin queryMixin;
+ private final Path extends Collection>> collection;
+
+ QuerydslAnyEmbeddedBuilder(QueryMixin queryMixin, Path extends Collection>> collection) {
+
+ this.queryMixin = queryMixin;
+ this.collection = collection;
+ }
+
+ /**
+ * Add the given where conditions.
+ *
+ * @param conditions must not be {@literal null}.
+ * @return the target {@link QueryMixin}.
+ * @see QueryMixin#where(Predicate)
+ */
+ public Q on(Predicate... conditions) {
+
+ return queryMixin
+ .where(ExpressionUtils.predicate(MongodbOps.ELEM_MATCH, collection, ExpressionUtils.allOf(conditions)));
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslFetchableMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslFetchableMongodbQuery.java
new file mode 100644
index 0000000000..e9f1b09865
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslFetchableMongodbQuery.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithProjection;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.BasicQuery;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.lang.Nullable;
+import org.springframework.util.LinkedMultiValueMap;
+
+import com.mysema.commons.lang.CloseableIterator;
+import com.querydsl.core.Fetchable;
+import com.querydsl.core.JoinExpression;
+import com.querydsl.core.QueryMetadata;
+import com.querydsl.core.QueryModifiers;
+import com.querydsl.core.QueryResults;
+import com.querydsl.core.types.Expression;
+import com.querydsl.core.types.ExpressionUtils;
+import com.querydsl.core.types.Operation;
+import com.querydsl.core.types.OrderSpecifier;
+import com.querydsl.core.types.Path;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.CollectionPathBase;
+
+/**
+ * {@link Fetchable} MongoDB query with utilizing {@link MongoOperations} for command execution.
+ *
+ * @param result type
+ * @param concrete subtype
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+abstract class QuerydslFetchableMongodbQuery>
+ extends QuerydslAbstractMongodbQuery implements Fetchable {
+
+ private final Class entityClass;
+ private final String collection;
+ private final MongoOperations mongoOperations;
+ private final FindWithProjection find;
+
+ QuerydslFetchableMongodbQuery(MongodbDocumentSerializer serializer, Class extends K> entityClass, String collection,
+ MongoOperations mongoOperations) {
+
+ super(serializer);
+
+ this.entityClass = (Class) entityClass;
+ this.collection = collection;
+ this.mongoOperations = mongoOperations;
+ find = mongoOperations.query(this.entityClass).inCollection(collection);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#iterable()
+ */
+ @Override
+ public CloseableIterator iterate() {
+
+ org.springframework.data.util.CloseableIterator extends K> stream = mongoOperations.stream(createQuery(),
+ entityClass, collection);
+
+ return new CloseableIterator() {
+
+ @Override
+ public boolean hasNext() {
+ return stream.hasNext();
+ }
+
+ @Override
+ public K next() {
+ return stream.next();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove from iterator while streaming data.");
+ }
+
+ @Override
+ public void close() {
+ stream.close();
+ }
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#fetch()
+ */
+ @Override
+ public List fetch() {
+ return find.matching(createQuery()).all();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#fetchFirst()
+ */
+ @Override
+ public K fetchFirst() {
+ return find.matching(createQuery()).firstValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#fetchOne()
+ */
+ @Override
+ public K fetchOne() {
+ return find.matching(createQuery()).oneValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#fetchResults()
+ */
+ @Override
+ public QueryResults fetchResults() {
+
+ long total = fetchCount();
+ return total > 0L ? new QueryResults<>(fetch(), getQueryMixin().getMetadata().getModifiers(), total)
+ : QueryResults.emptyResults();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.Fetchable#fetchCount()
+ */
+ @Override
+ public long fetchCount() {
+ return find.matching(createQuery()).count();
+ }
+
+ /**
+ * Define a join.
+ *
+ * @param ref reference
+ * @param target join target
+ * @return new instance of {@link QuerydslJoinBuilder}.
+ */
+ public QuerydslJoinBuilder join(Path ref, Path target) {
+ return new QuerydslJoinBuilder<>(getQueryMixin(), ref, target);
+ }
+
+ /**
+ * Define a join.
+ *
+ * @param ref reference
+ * @param target join target
+ * @return new instance of {@link QuerydslJoinBuilder}.
+ */
+ public QuerydslJoinBuilder join(CollectionPathBase, T, ?> ref, Path target) {
+ return new QuerydslJoinBuilder<>(getQueryMixin(), ref, target);
+ }
+
+ /**
+ * Define a constraint for an embedded object.
+ *
+ * @param collection collection must not be {@literal null}.
+ * @param target target must not be {@literal null}.
+ * @return new instance of {@link QuerydslAnyEmbeddedBuilder}.
+ */
+ public QuerydslAnyEmbeddedBuilder anyEmbedded(Path extends Collection> collection, Path target) {
+ return new QuerydslAnyEmbeddedBuilder<>(getQueryMixin(), collection);
+ }
+
+ protected org.springframework.data.mongodb.core.query.Query createQuery() {
+
+ QueryMetadata metadata = getQueryMixin().getMetadata();
+
+ return createQuery(createFilter(metadata), metadata.getProjection(), metadata.getModifiers(),
+ metadata.getOrderBy());
+ }
+
+ protected org.springframework.data.mongodb.core.query.Query createQuery(@Nullable Predicate filter,
+ @Nullable Expression> projection, QueryModifiers modifiers, List> orderBy) {
+
+ BasicQuery basicQuery = new BasicQuery(createQuery(filter), createProjection(projection));
+
+ Integer limit = modifiers.getLimitAsInteger();
+ Integer offset = modifiers.getOffsetAsInteger();
+
+ if (limit != null) {
+ basicQuery.limit(limit);
+ }
+ if (offset != null) {
+ basicQuery.skip(offset);
+ }
+ if (orderBy.size() > 0) {
+ basicQuery.setSortObject(createSort(orderBy));
+ }
+
+ return basicQuery;
+ }
+
+ @Nullable
+ protected Predicate createFilter(QueryMetadata metadata) {
+
+ Predicate filter;
+ if (!metadata.getJoins().isEmpty()) {
+ filter = ExpressionUtils.allOf(metadata.getWhere(), createJoinFilter(metadata));
+ } else {
+ filter = metadata.getWhere();
+ }
+ return filter;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ protected Predicate createJoinFilter(QueryMetadata metadata) {
+
+ LinkedMultiValueMap, Predicate> predicates = new LinkedMultiValueMap<>();
+ List joins = metadata.getJoins();
+
+ for (int i = joins.size() - 1; i >= 0; i--) {
+
+ JoinExpression join = joins.get(i);
+ Path> source = (Path) ((Operation>) join.getTarget()).getArg(0);
+ Path> target = (Path) ((Operation>) join.getTarget()).getArg(1);
+ Collection extraFilters = predicates.get(target.getRoot());
+ Predicate filter = ExpressionUtils.allOf(join.getCondition(), allOf(extraFilters));
+
+ List extends Object> ids = getIds(target.getType(), filter);
+
+ if (ids.isEmpty()) {
+ return ExpressionUtils.predicate(QuerydslMongoOps.NO_MATCH, source);
+ }
+
+ Path> path = ExpressionUtils.path(String.class, source, "$id");
+ predicates.add(source.getRoot(), ExpressionUtils.in((Path) path, ids));
+ }
+
+ Path> source = (Path) ((Operation) joins.get(0).getTarget()).getArg(0);
+ return allOf(predicates.get(source.getRoot()));
+ }
+
+ private Predicate allOf(Collection predicates) {
+ return predicates != null ? ExpressionUtils.allOf(predicates) : null;
+ }
+
+ /**
+ * Fetch the list of ids matching a given condition.
+ *
+ * @param targetType must not be {@literal null}.
+ * @param condition must not be {@literal null}.
+ * @return empty {@link List} if none found.
+ */
+ protected List getIds(Class> targetType, Predicate condition) {
+
+ Query query = createQuery(condition, null, QueryModifiers.EMPTY, Collections.emptyList());
+ return mongoOperations.findDistinct(query, "_id", targetType, Object.class);
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslJoinBuilder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslJoinBuilder.java
new file mode 100644
index 0000000000..ac4b4bf6e4
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslJoinBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import com.querydsl.core.JoinType;
+import com.querydsl.core.support.QueryMixin;
+import com.querydsl.core.types.ExpressionUtils;
+import com.querydsl.core.types.Path;
+import com.querydsl.core.types.Predicate;
+
+/**
+ * {@code QuerydslJoinBuilder} is a builder for join constraints.
+ *
+ * Original implementation source {@link com.querydsl.mongodb.JoinBuilder} by {@literal The Querydsl Team}
+ * (http://www.querydsl.com/team ) licensed under the Apache License, Version
+ * 2.0.
+ *
+ * Modified for usage with {@link QuerydslAbstractMongodbQuery}.
+ *
+ * @param
+ * @param
+ * @author tiwe
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+public class QuerydslJoinBuilder, K, T> {
+
+ private final QueryMixin queryMixin;
+ private final Path> ref;
+ private final Path target;
+
+ QuerydslJoinBuilder(QueryMixin queryMixin, Path> ref, Path target) {
+
+ this.queryMixin = queryMixin;
+ this.ref = ref;
+ this.target = target;
+ }
+
+ /**
+ * Add the given join conditions.
+ *
+ * @param conditions must not be {@literal null}.
+ * @return the target {@link QueryMixin}.
+ * @see QueryMixin#on(Predicate)
+ */
+ @SuppressWarnings("unchecked")
+ public Q on(Predicate... conditions) {
+
+ queryMixin.addJoin(JoinType.JOIN, ExpressionUtils.as((Path) ref, target));
+ queryMixin.on(conditions);
+ return queryMixin.getSelf();
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoOps.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoOps.java
new file mode 100644
index 0000000000..cae326188a
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoOps.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.repository.support;
+
+import com.querydsl.core.types.Operator;
+
+/**
+ * Spring Data specific {@link Operator operators} for usage with Querydsl and MongoDB.
+ *
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+enum QuerydslMongoOps implements Operator {
+
+ /**
+ * {@link Operator} always evaluating to {@literal false}.
+ */
+ NO_MATCH(Boolean.class);
+
+ private final Class> type;
+
+ QuerydslMongoOps(Class> type) {
+ this.type = type;
+ }
+
+ @Override
+ public Class> getType() {
+ return type;
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
index 19bf1782b2..0ec3328509 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
@@ -39,7 +39,6 @@
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
-import com.querydsl.mongodb.AbstractMongodbQuery;
/**
* MongoDB-specific {@link QuerydslPredicateExecutor} that allows execution {@link Predicate}s in various forms.
@@ -166,7 +165,7 @@ public Page findAll(Predicate predicate, Pageable pageable) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
- AbstractMongodbQuery> query = createQueryFor(predicate);
+ SpringDataMongodbQuery query = createQueryFor(predicate);
return PageableExecutionUtils.getPage(applyPagination(query, pageable).fetch(), pageable, query::fetchCount);
}
@@ -196,47 +195,45 @@ public boolean exists(Predicate predicate) {
}
/**
- * Creates a {@link AbstractMongodbQuery} for the given {@link Predicate}.
+ * Creates a {@link SpringDataMongodbQuery} for the given {@link Predicate}.
*
* @param predicate
* @return
*/
- private AbstractMongodbQuery> createQueryFor(Predicate predicate) {
+ private SpringDataMongodbQuery createQueryFor(Predicate predicate) {
return createQuery().where(predicate);
}
/**
- * Creates a {@link AbstractMongodbQuery}.
+ * Creates a {@link SpringDataMongodbQuery}.
*
* @return
*/
- private AbstractMongodbQuery> createQuery() {
+ private SpringDataMongodbQuery createQuery() {
return new SpringDataMongodbQuery<>(mongoOperations, entityInformation.getJavaType());
}
/**
- * Applies the given {@link Pageable} to the given {@link MongodbQuery}.
+ * Applies the given {@link Pageable} to the given {@link SpringDataMongodbQuery}.
*
* @param query
* @param pageable
* @return
*/
- private AbstractMongodbQuery> applyPagination(
- AbstractMongodbQuery> query, Pageable pageable) {
+ private SpringDataMongodbQuery applyPagination(SpringDataMongodbQuery query, Pageable pageable) {
query = query.offset(pageable.getOffset()).limit(pageable.getPageSize());
return applySorting(query, pageable.getSort());
}
/**
- * Applies the given {@link Sort} to the given {@link MongodbQuery}.
+ * Applies the given {@link Sort} to the given {@link SpringDataMongodbQuery}.
*
* @param query
* @param sort
* @return
*/
- private AbstractMongodbQuery> applySorting(
- AbstractMongodbQuery> query, Sort sort) {
+ private SpringDataMongodbQuery applySorting(SpringDataMongodbQuery query, Sort sort) {
// TODO: find better solution than instanceof check
if (sort instanceof QSort) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupport.java
index da39c9e73d..5b89e85a1e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupport.java
@@ -21,7 +21,6 @@
import org.springframework.util.Assert;
import com.querydsl.core.types.EntityPath;
-import com.querydsl.mongodb.AbstractMongodbQuery;
/**
* Base class to create repository implementations based on Querydsl.
@@ -48,13 +47,13 @@ public QuerydslRepositorySupport(MongoOperations operations) {
}
/**
- * Returns a {@link MongodbQuery} for the given {@link EntityPath}. The collection being queried is derived from the
+ * Returns a {@link SpringDataMongodbQuery} for the given {@link EntityPath}. The collection being queried is derived from the
* entity metadata.
*
* @param path
* @return
*/
- protected AbstractMongodbQuery> from(final EntityPath path) {
+ protected SpringDataMongodbQuery from(final EntityPath path) {
Assert.notNull(path, "EntityPath must not be null!");
MongoPersistentEntity> entity = context.getRequiredPersistentEntity(path.getType());
@@ -62,13 +61,13 @@ protected AbstractMongodbQuery> from(final Enti
}
/**
- * Returns a {@link MongodbQuery} for the given {@link EntityPath} querying the given collection.
+ * Returns a {@link SpringDataMongodbQuery} for the given {@link EntityPath} querying the given collection.
*
* @param path must not be {@literal null}
* @param collection must not be blank or {@literal null}
* @return
*/
- protected AbstractMongodbQuery> from(final EntityPath path, String collection) {
+ protected SpringDataMongodbQuery from(final EntityPath path, String collection) {
Assert.notNull(path, "EntityPath must not be null!");
Assert.hasText(collection, "Collection name must not be null or empty!");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
index fa7185468d..a5662147ad 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
@@ -15,26 +15,17 @@
*/
package org.springframework.data.mongodb.repository.support;
-import org.bson.Document;
import org.springframework.data.mongodb.core.MongoOperations;
-import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.lang.Nullable;
-
-import com.google.common.base.Function;
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCollection;
-import com.mongodb.DBObject;
-import com.querydsl.mongodb.AbstractMongodbQuery;
/**
- * Spring Data specific {@link AbstractMongodbQuery} implementation.
+ * Spring Data specific simple {@link com.querydsl.core.Fetchable} {@link com.querydsl.core.SimpleQuery Query}
+ * implementation.
*
* @author Oliver Gierke
* @author Mark Paluch
+ * @author Christoph Strobl
*/
-public class SpringDataMongodbQuery extends AbstractMongodbQuery> {
-
- private final MongoOperations operations;
+public class SpringDataMongodbQuery extends QuerydslFetchableMongodbQuery> {
/**
* Creates a new {@link SpringDataMongodbQuery}.
@@ -56,25 +47,6 @@ public SpringDataMongodbQuery(final MongoOperations operations, final Class ex
public SpringDataMongodbQuery(final MongoOperations operations, final Class extends T> type,
String collectionName) {
- super(((MongoTemplate) operations).getMongoDbFactory().getLegacyDb().getCollection(collectionName),
- new Function() {
-
- @Override
- public T apply(@Nullable DBObject input) {
- return operations.getConverter().read(type, new Document((BasicDBObject) input));
- }
- }, new SpringDataMongodbSerializer(operations.getConverter()));
-
- this.operations = operations;
- }
-
- /*
- * (non-Javadoc)
- * @see com.querydsl.mongodb.AbstractMongodbQuery#getCollection(java.lang.Class)
- */
- @Override
- protected DBCollection getCollection(Class> type) {
- return ((MongoTemplate) operations).getMongoDbFactory().getLegacyDb()
- .getCollection(operations.getCollectionName(type));
+ super(new SpringDataMongodbSerializer(operations.getConverter()), type, collectionName, operations);
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
index 7add845d41..8a2e8f4f4f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
@@ -17,7 +17,6 @@
import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
@@ -28,16 +27,11 @@
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
-import com.mongodb.BasicDBList;
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
import com.mongodb.DBRef;
-import com.mongodb.util.JSON;
import com.querydsl.core.types.Constant;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Operation;
@@ -53,14 +47,14 @@
* @author Christoph Strobl
* @author Mark Paluch
*/
-class SpringDataMongodbSerializer extends MongodbSerializer {
+class SpringDataMongodbSerializer extends MongodbDocumentSerializer {
private static final String ID_KEY = "_id";
private static final Set PATH_TYPES;
static {
- Set pathTypes = new HashSet();
+ Set pathTypes = new HashSet<>();
pathTypes.add(PathType.VARIABLE);
pathTypes.add(PathType.PROPERTY);
@@ -72,9 +66,9 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
private final QueryMapper mapper;
/**
- * Creates a new {@link SpringDataMongodbSerializer} for the given {@link MappingContext}.
+ * Creates a new {@link SpringDataMongodbSerializer} for the given {@link MongoConverter}.
*
- * @param mappingContext must not be {@literal null}.
+ * @param converter must not be {@literal null}.
*/
public SpringDataMongodbSerializer(MongoConverter converter) {
@@ -96,7 +90,7 @@ public Object visit(Constant> expr, Void context) {
return super.visit(expr, context);
}
- return toQuerydslMongoType(expr.getConstant());
+ return converter.convertToMongoType(expr.getConstant());
}
/*
@@ -119,10 +113,10 @@ protected String getKeyForPath(Path> expr, PathMetadata metadata) {
/*
* (non-Javadoc)
- * @see com.querydsl.mongodb.MongodbSerializer#asDBObject(java.lang.String, java.lang.Object)
+ * @see org.springframework.data.mongodb.repository.support.MongodbSerializer#asDocument(java.lang.String, java.lang.Object)
*/
@Override
- protected DBObject asDBObject(@Nullable String key, @Nullable Object value) {
+ protected Document asDocument(@Nullable String key, @Nullable Object value) {
value = value instanceof Optional ? ((Optional) value).orElse(null) : value;
@@ -130,7 +124,7 @@ protected DBObject asDBObject(@Nullable String key, @Nullable Object value) {
return convertId(key, value);
}
- return super.asDBObject(key, value instanceof Pattern ? value : toQuerydslMongoType(value));
+ return super.asDocument(key, value instanceof Pattern ? value : converter.convertToMongoType(value));
}
/**
@@ -141,13 +135,11 @@ protected DBObject asDBObject(@Nullable String key, @Nullable Object value) {
* @param idValue the raw {@literal id} value.
* @return the {@literal id} representation in the required format.
*/
- private DBObject convertId(String key, Object idValue) {
+ private Document convertId(String key, Object idValue) {
Object convertedId = mapper.convertId(idValue);
- Document mappedIdValue = mapper.getMappedObject((BasicDBObject) super.asDBObject(key, convertedId),
- Optional.empty());
- return (DBObject) JSON.parse(mappedIdValue.toJson());
+ return mapper.getMappedObject(super.asDocument(key, convertedId), Optional.empty());
}
/*
@@ -215,6 +207,7 @@ protected Object convert(@Nullable Path> path, @Nullable Constant> constant)
: asReference(constant.getConstant(), path);
}
+ @Nullable
private MongoPersistentProperty getPropertyFor(Path> path) {
Path> parent = path.getMetadata().getParent();
@@ -224,7 +217,7 @@ private MongoPersistentProperty getPropertyFor(Path> path) {
}
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(parent.getType());
- return entity != null ? entity.getRequiredPersistentProperty(path.getMetadata().getName()) : null;
+ return entity != null ? entity.getPersistentProperty(path.getMetadata().getName()) : null;
}
/**
@@ -250,25 +243,4 @@ private MongoPersistentProperty getPropertyForPotentialDbRef(Path> path) {
return property;
}
-
- private Object toQuerydslMongoType(Object source) {
-
- Object target = converter.convertToMongoType(source);
-
- if (target instanceof List) {
-
- List newList = new BasicDBList();
-
- for (Object item : (List) target) {
- if (item instanceof Document) {
- newList.add(new BasicDBObject(BsonUtils.asMap((Document) item)));
- } else {
- newList.add(item);
- }
- }
- return newList;
- }
-
- return target;
- }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
index dcca0927a0..dbd64e76ff 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
@@ -40,6 +40,11 @@
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
+import org.springframework.data.mongodb.repository.Person;
+import org.springframework.data.mongodb.repository.QPerson;
+import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
+import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
+import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
@@ -386,6 +391,29 @@ public void publishesAfterConvertEventForFindQueriesUsingProjections() {
assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME);
}
+ @Test // DATAMONGO-700, DATAMONGO-1185, DATAMONGO-1848
+ public void publishesEventsForQuerydslFindQueries() {
+
+ template.dropCollection(Person.class);
+
+ template.save(new Person("Boba", "Fett", 40));
+
+ MongoRepositoryFactory factory = new MongoRepositoryFactory(template);
+ MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class);
+ QuerydslMongoPredicateExecutor executor = new QuerydslMongoPredicateExecutor<>(entityInformation, template);
+
+ executor.findOne(QPerson.person.lastname.startsWith("Fe"));
+
+ assertThat(listener.onAfterLoadEvents).hasSize(1);
+ assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo("person");
+
+ assertThat(listener.onBeforeConvertEvents).hasSize(1);
+ assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo("person");
+
+ assertThat(listener.onAfterConvertEvents).hasSize(1);
+ assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo("person");
+ }
+
private void comparePersonAndDocument(PersonPojoStringId p, PersonPojoStringId p2, org.bson.Document document) {
assertThat(p2.getId()).isEqualTo(p.getId());
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java
index 6dbede532e..36dacb0ed2 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java
@@ -18,6 +18,7 @@
import static org.assertj.core.api.Assertions.*;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.List;
import org.junit.Before;
@@ -25,15 +26,25 @@
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
+import org.springframework.dao.PermissionDeniedDataAccessException;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.repository.Address;
import org.springframework.data.mongodb.repository.Person;
+import org.springframework.data.mongodb.repository.QAddress;
import org.springframework.data.mongodb.repository.QPerson;
+import org.springframework.data.mongodb.repository.QUser;
+import org.springframework.data.mongodb.repository.User;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import com.mongodb.MongoException;
+import com.mongodb.client.MongoDatabase;
+
/**
* Integration test for {@link QuerydslMongoPredicateExecutor}.
*
@@ -47,6 +58,8 @@
public class QuerydslMongoPredicateExecutorIntegrationTests {
@Autowired MongoOperations operations;
+ @Autowired MongoDbFactory dbFactory;
+
QuerydslMongoPredicateExecutor repository;
Person dave, oliver, carter;
@@ -57,7 +70,7 @@ public void setup() {
MongoRepositoryFactory factory = new MongoRepositoryFactory(operations);
MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class);
- repository = new QuerydslMongoPredicateExecutor(entityInformation, operations);
+ repository = new QuerydslMongoPredicateExecutor<>(entityInformation, operations);
operations.dropCollection(Person.class);
@@ -99,4 +112,114 @@ public void findOneWithPredicateReturnsOptionalEmptyWhenNoDataFound() {
public void findOneWithPredicateThrowsExceptionForNonUniqueResults() {
repository.findOne(person.firstname.contains("e"));
}
+
+ @Test // DATAMONGO-1848
+ public void findUsingAndShouldWork() {
+
+ assertThat(repository.findAll(
+ person.lastname.startsWith(oliver.getLastname()).and(person.firstname.startsWith(dave.getFirstname()))))
+ .containsExactly(dave);
+ }
+
+ @Test // DATAMONGO-362, DATAMONGO-1848
+ public void springDataMongodbQueryShouldAllowJoinOnDBref() {
+
+ User user1 = new User();
+ user1.setUsername("user-1");
+
+ User user2 = new User();
+ user2.setUsername("user-2");
+
+ User user3 = new User();
+ user3.setUsername("user-3");
+
+ operations.save(user1);
+ operations.save(user2);
+ operations.save(user3);
+
+ Person person1 = new Person("Max", "The Mighty");
+ person1.setCoworker(user1);
+
+ Person person2 = new Person("Jack", "The Ripper");
+ person2.setCoworker(user2);
+
+ Person person3 = new Person("Bob", "The Builder");
+ person3.setCoworker(user3);
+
+ operations.save(person1);
+ operations.save(person2);
+ operations.save(person3);
+
+ List result = new SpringDataMongodbQuery<>(operations, Person.class).where()
+ .join(person.coworker, QUser.user).on(QUser.user.username.eq("user-2")).fetch();
+
+ assertThat(result).containsExactly(person2);
+ }
+
+ @Test // DATAMONGO-362, DATAMONGO-1848
+ public void springDataMongodbQueryShouldReturnEmptyOnJoinWithNoResults() {
+
+ User user1 = new User();
+ user1.setUsername("user-1");
+
+ User user2 = new User();
+ user2.setUsername("user-2");
+
+ operations.save(user1);
+ operations.save(user2);
+
+ Person person1 = new Person("Max", "The Mighty");
+ person1.setCoworker(user1);
+
+ Person person2 = new Person("Jack", "The Ripper");
+ person2.setCoworker(user2);
+
+ operations.save(person1);
+ operations.save(person2);
+
+ List result = new SpringDataMongodbQuery<>(operations, Person.class).where()
+ .join(person.coworker, QUser.user).on(QUser.user.username.eq("does-not-exist")).fetch();
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test // DATAMONGO-595, DATAMONGO-1848
+ public void springDataMongodbQueryShouldAllowElemMatchOnArrays() {
+
+ Address adr1 = new Address("Hauptplatz", "4020", "Linz");
+ Address adr2 = new Address("Stephansplatz", "1010", "Wien");
+ Address adr3 = new Address("Tower of London", "EC3N 4AB", "London");
+
+ Person person1 = new Person("Max", "The Mighty");
+ person1.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr1, adr2)));
+
+ Person person2 = new Person("Jack", "The Ripper");
+ person2.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr2, adr3)));
+
+ operations.save(person1);
+ operations.save(person2);
+
+ List result = new SpringDataMongodbQuery<>(operations, Person.class).where()
+ .anyEmbedded(person.shippingAddresses, QAddress.address).on(QAddress.address.city.eq("London")).fetch();
+
+ assertThat(result).containsExactly(person2);
+ }
+
+ @Test(expected = PermissionDeniedDataAccessException.class) // DATAMONGO-1434, DATAMONGO-1848
+ public void translatesExceptionsCorrectly() {
+
+ MongoOperations ops = new MongoTemplate(dbFactory) {
+
+ @Override
+ protected MongoDatabase doGetDatabase() {
+ throw new MongoException(18, "Authentication Failed");
+ }
+ };
+
+ MongoRepositoryFactory factory = new MongoRepositoryFactory(ops);
+ MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class);
+ repository = new QuerydslMongoPredicateExecutor<>(entityInformation, ops);
+
+ repository.findOne(person.firstname.contains("batman"));
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java
index acaa188454..e8fd4b7c4f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java
@@ -26,6 +26,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.MongoOperations;
@@ -139,6 +140,79 @@ public void shouldConvertStringIdThatIsAValidObjectIdIntoTheSuch() {
assertThat(query.fetchOne(), equalTo(outer));
}
+ @Test // DATAMONGO-1810, DATAMONGO-1848
+ public void shouldFetchObjectsViaStringWhenUsingInOnDbRef() {
+
+ User bart = new User();
+ DirectFieldAccessor dfa = new DirectFieldAccessor(bart);
+ dfa.setPropertyValue("id", "bart");
+
+ bart.setUsername("bart@simpson.com");
+ operations.save(bart);
+
+ User lisa = new User();
+ dfa = new DirectFieldAccessor(lisa);
+ dfa.setPropertyValue("id", "lisa");
+
+ lisa.setUsername("lisa@simposon.com");
+ operations.save(lisa);
+
+ person.setCoworker(bart);
+ operations.save(person);
+
+ QPerson p = QPerson.person;
+
+ SpringDataMongodbQuery queryUsingIdFieldWithinInClause = repoSupport.from(p)
+ .where(p.coworker.id.in(Arrays.asList(bart.getId(), lisa.getId())));
+
+ SpringDataMongodbQuery queryUsingRefObject = repoSupport.from(p).where(p.coworker.eq(bart));
+
+ assertThat(queryUsingIdFieldWithinInClause.fetchOne(), equalTo(person));
+ assertThat(queryUsingIdFieldWithinInClause.fetchOne(), equalTo(queryUsingRefObject.fetchOne()));
+ }
+
+ @Test // DATAMONGO-1810, DATAMONGO-1848
+ public void shouldFetchObjectsViaStringStoredAsObjectIdWhenUsingInOnDbRef() {
+
+ User bart = new User();
+ bart.setUsername("bart@simpson.com");
+ operations.save(bart);
+
+ User lisa = new User();
+ lisa.setUsername("lisa@simposon.com");
+ operations.save(lisa);
+
+ person.setCoworker(bart);
+ operations.save(person);
+
+ QPerson p = QPerson.person;
+
+ SpringDataMongodbQuery queryUsingIdFieldWithinInClause = repoSupport.from(p)
+ .where(p.coworker.id.in(Arrays.asList(bart.getId(), lisa.getId())));
+
+ SpringDataMongodbQuery queryUsingRefObject = repoSupport.from(p).where(p.coworker.eq(bart));
+
+ assertThat(queryUsingIdFieldWithinInClause.fetchOne(), equalTo(person));
+ assertThat(queryUsingIdFieldWithinInClause.fetchOne(), equalTo(queryUsingRefObject.fetchOne()));
+ }
+
+ @Test // DATAMONGO-1848, DATAMONGO-2010
+ public void shouldConvertStringIdThatIsAValidObjectIdWhenUsedInInPredicateIntoTheSuch() {
+
+ Outer outer = new Outer();
+ outer.id = new ObjectId().toHexString();
+ outer.inner = new Inner();
+ outer.inner.id = new ObjectId().toHexString();
+ outer.inner.value = "eat sleep workout repeat";
+
+ operations.save(outer);
+
+ QQuerydslRepositorySupportTests_Outer o = QQuerydslRepositorySupportTests_Outer.outer;
+ SpringDataMongodbQuery query = repoSupport.from(o).where(o.inner.id.in(outer.inner.id, outer.inner.id));
+
+ assertThat(query.fetchOne(), equalTo(outer));
+ }
+
@Data
@Document
public static class Outer {
@@ -152,6 +226,5 @@ public static class Inner {
@Id String id;
String value;
-
}
}
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 55faae109d..14f51c091e 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
@@ -19,6 +19,7 @@
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DocumentTestUtils.*;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,10 +43,6 @@
import org.springframework.data.mongodb.repository.QAddress;
import org.springframework.data.mongodb.repository.QPerson;
-import com.mongodb.BasicDBList;
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBObject;
-import com.mongodb.util.JSON;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.BooleanOperation;
import com.querydsl.core.types.dsl.PathBuilder;
@@ -96,9 +93,7 @@ public void convertsComplexObjectOnSerializing() {
address.street = "Foo";
address.zipCode = "01234";
- DBObject result = serializer.asDBObject("foo", address);
- assertThat(result, is(instanceOf(BasicDBObject.class)));
- BasicDBObject document = (BasicDBObject) result;
+ Document document = serializer.asDocument("foo", address);
Object value = document.get("foo");
assertThat(value, is(notNullValue()));
@@ -123,10 +118,10 @@ public void convertsIdPropertyCorrectly() {
PathBuilder builder = new PathBuilder(Address.class, "address");
StringPath idPath = builder.getString("id");
- DBObject result = (DBObject) serializer.visit((BooleanOperation) idPath.eq(id.toString()), (Void) null);
+ Document result = (Document) serializer.visit((BooleanOperation) idPath.eq(id.toString()), null);
assertThat(result.get("_id"), is(notNullValue()));
assertThat(result.get("_id"), is(instanceOf(ObjectId.class)));
- assertThat(result.get("_id"), is((Object) id));
+ assertThat(result.get("_id"), is(id));
}
@Test // DATAMONGO-761
@@ -143,10 +138,10 @@ public void looksUpKeyForNonPropertyPath() {
public void shouldConvertObjectIdEvenWhenNestedInOperatorDbObject() {
ObjectId value = new ObjectId("53bb9fd14438765b29c2d56e");
- DBObject serialized = serializer.asDBObject("_id", new Document("$ne", value.toString()));
+ Document serialized = serializer.asDocument("_id", new Document("$ne", value.toString()));
- DBObject _id = getTypedValue(new Document(serialized.toMap()), "_id", DBObject.class);
- ObjectId $ne = getTypedValue(new Document(_id.toMap()), "$ne", ObjectId.class);
+ Document _id = getTypedValue(serialized, "_id", Document.class);
+ ObjectId $ne = getTypedValue(_id, "$ne", ObjectId.class);
assertThat($ne, is(value));
}
@@ -156,14 +151,14 @@ public void shouldConvertCollectionOfObjectIdEvenWhenNestedInOperatorDocument()
ObjectId firstId = new ObjectId("53bb9fd14438765b29c2d56e");
ObjectId secondId = new ObjectId("53bb9fda4438765b29c2d56f");
- BasicDBList objectIds = new BasicDBList();
+ List objectIds = new ArrayList<>();
objectIds.add(firstId.toString());
objectIds.add(secondId.toString());
- DBObject serialized = serializer.asDBObject("_id", new Document("$in", objectIds));
+ Document serialized = serializer.asDocument("_id", new Document("$in", objectIds));
- DBObject _id = getTypedValue(new Document(serialized.toMap()), "_id", DBObject.class);
- List $in = getTypedValue(new Document(_id.toMap()), "$in", List.class);
+ Document _id = getTypedValue(serialized, "_id", Document.class);
+ List $in = getTypedValue(_id, "$in", List.class);
assertThat($in, IsIterableContainingInOrder. contains(firstId, secondId));
}
@@ -182,18 +177,19 @@ public void takesCustomConversionForEnumsIntoAccount() {
Object mappedPredicate = this.serializer.handle(QPerson.person.sex.eq(Sex.FEMALE));
- assertThat(mappedPredicate, is(instanceOf(DBObject.class)));
- assertThat(((DBObject) mappedPredicate).get("sex"), is((Object) "f"));
+ assertThat(mappedPredicate, is(instanceOf(Document.class)));
+ assertThat(((Document) mappedPredicate).get("sex"), is("f"));
}
- @Test // DATAMONGO-1943
+ @Test // DATAMONGO-1848, DATAMONGO-1943
public void shouldRemarshallListsAndDocuments() {
- BooleanExpression criteria = QPerson.person.firstname.isNotEmpty()
+ BooleanExpression criteria = QPerson.person.lastname.isNotEmpty()
.and(QPerson.person.firstname.containsIgnoreCase("foo")).not();
- assertThat(this.serializer.handle(criteria), is(equalTo(JSON.parse("{ \"$or\" : [ { \"firstname\" : { \"$ne\" : { "
- + "\"$ne\" : \"\"}}} , { \"firstname\" : { \"$not\" : { \"$regex\" : \".*\\\\Qfoo\\\\E.*\" , \"$options\" : \"i\"}}}]}"))));
+ assertThat(this.serializer.handle(criteria),
+ is(equalTo(Document.parse("{ \"$or\" : [ { \"lastname\" : { \"$not\" : { "
+ + "\"$ne\" : \"\"}}} , { \"firstname\" : { \"$not\" : { \"$regex\" : \".*\\\\Qfoo\\\\E.*\" , \"$options\" : \"i\"}}}]}"))));
}
class Address {