DATAMONGO-1848 - Use imported Querydsl support for Document API MongoDB.

Original Pull Request: #579
This commit is contained in:
Mark Paluch
2018-06-18 15:26:46 +02:00
committed by Christoph Strobl
parent b7755e71f6
commit 7d06f2b040
9 changed files with 207 additions and 89 deletions

View File

@@ -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();
/**

View File

@@ -146,7 +146,7 @@ abstract class AbstractMongodbQuery<Q extends AbstractMongodbQuery<Q>> implement
}
return obj;
}
return null;
return new Document();
}
protected Document createQuery(@Nullable Predicate predicate) {

View File

@@ -47,7 +47,8 @@ import com.querydsl.core.types.Predicate;
* @param <Q> concrete subtype
* @author Mark Paluch
*/
class FetchableMongodbQuery<K> extends AbstractMongodbQuery<FetchableMongodbQuery<K>> implements Fetchable<K> {
abstract class FetchableMongodbQuery<K, Q extends FetchableMongodbQuery<K, Q>> extends AbstractMongodbQuery<Q>
implements Fetchable<K> {
private final Class<K> entityClass;
private final String collection;

View File

@@ -39,7 +39,6 @@ import com.querydsl.core.types.Expression;
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 class QuerydslMongoPredicateExecutor<T> implements QuerydslPredicateExecu
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> query = createQueryFor(predicate);
SimpleFetchableQuery<T> query = createQueryFor(predicate);
return PageableExecutionUtils.getPage(applyPagination(query, pageable).fetch(), pageable, query::fetchCount);
}
@@ -201,7 +200,7 @@ public class QuerydslMongoPredicateExecutor<T> implements QuerydslPredicateExecu
* @param predicate
* @return
*/
private AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> createQueryFor(Predicate predicate) {
private SimpleFetchableQuery<T> createQueryFor(Predicate predicate) {
return createQuery().where(predicate);
}
@@ -210,7 +209,7 @@ public class QuerydslMongoPredicateExecutor<T> implements QuerydslPredicateExecu
*
* @return
*/
private AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> createQuery() {
private SimpleFetchableQuery<T> createQuery() {
return new SpringDataMongodbQuery<>(mongoOperations, entityInformation.getJavaType());
}
@@ -221,8 +220,7 @@ public class QuerydslMongoPredicateExecutor<T> implements QuerydslPredicateExecu
* @param pageable
* @return
*/
private AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> applyPagination(
AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> query, Pageable pageable) {
private SimpleFetchableQuery<T> applyPagination(SimpleFetchableQuery<T> query, Pageable pageable) {
query = query.offset(pageable.getOffset()).limit(pageable.getPageSize());
return applySorting(query, pageable.getSort());
@@ -235,8 +233,7 @@ public class QuerydslMongoPredicateExecutor<T> implements QuerydslPredicateExecu
* @param sort
* @return
*/
private AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> applySorting(
AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> query, Sort sort) {
private SimpleFetchableQuery<T> applySorting(SimpleFetchableQuery<T> query, Sort sort) {
// TODO: find better solution than instanceof check
if (sort instanceof QSort) {

View File

@@ -21,7 +21,6 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
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.
@@ -54,7 +53,7 @@ public abstract class QuerydslRepositorySupport {
* @param path
* @return
*/
protected <T> AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> from(final EntityPath<T> path) {
protected <T> SpringDataMongodbQuery<T> from(final EntityPath<T> path) {
Assert.notNull(path, "EntityPath must not be null!");
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(path.getType());
@@ -68,7 +67,7 @@ public abstract class QuerydslRepositorySupport {
* @param collection must not be blank or {@literal null}
* @return
*/
protected <T> AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> from(final EntityPath<T> path, String collection) {
protected <T> SpringDataMongodbQuery<T> from(final EntityPath<T> path, String collection) {
Assert.notNull(path, "EntityPath must not be null!");
Assert.hasText(collection, "Collection name must not be null or empty!");

View File

@@ -0,0 +1,27 @@
/*
* 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.Fetchable;
import com.querydsl.core.SimpleQuery;
/**
* Interface that combines {@link Fetchable} and {@link SimpleQuery}.
*
* @author Mark Paluch
* @since 2.1
*/
public interface SimpleFetchableQuery<K> extends Fetchable<K>, SimpleQuery<SimpleFetchableQuery<K>> {}

View File

@@ -15,15 +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 java.util.List;
import com.google.common.base.Function;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import org.springframework.data.mongodb.core.MongoOperations;
import com.mysema.commons.lang.CloseableIterator;
import com.querydsl.core.NonUniqueResultException;
import com.querydsl.core.QueryModifiers;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.ParamExpression;
import com.querydsl.core.types.Predicate;
import com.querydsl.mongodb.AbstractMongodbQuery;
/**
@@ -32,9 +34,9 @@ import com.querydsl.mongodb.AbstractMongodbQuery;
* @author Oliver Gierke
* @author Mark Paluch
*/
public class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
public class SpringDataMongodbQuery<T> implements SimpleFetchableQuery<T> {
private final MongoOperations operations;
private final OperationsMongodbQuery<T> query;
/**
* Creates a new {@link SpringDataMongodbQuery}.
@@ -56,25 +58,144 @@ public class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDat
public SpringDataMongodbQuery(final MongoOperations operations, final Class<? extends T> type,
String collectionName) {
super(((MongoTemplate) operations).getMongoDbFactory().getLegacyDb().getCollection(collectionName),
new Function<DBObject, T>() {
query = new OperationsMongodbQuery<>(new SpringDataMongodbSerializer(operations.getConverter()), type,
collectionName, operations);
}
@Override
public T apply(@Nullable DBObject input) {
return operations.getConverter().read(type, new Document((BasicDBObject) input));
}
}, new SpringDataMongodbSerializer(operations.getConverter()));
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#fetch()
*/
@Override
public List<T> fetch() {
return query.fetch();
}
this.operations = operations;
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#fetchFirst()
*/
@Override
public T fetchFirst() {
return query.fetchFirst();
}
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#fetchOne()
*/
@Override
public T fetchOne() throws NonUniqueResultException {
return query.fetchOne();
}
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#iterate()
*/
@Override
public CloseableIterator<T> iterate() {
return query.iterate();
}
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#fetchResults()
*/
@Override
public QueryResults<T> fetchResults() {
return query.fetchResults();
}
/*
* (non-Javadoc)
* @see com.querydsl.core.Fetchable#fetchCount()
*/
@Override
public long fetchCount() {
return query.fetchCount();
}
/*
* (non-Javadoc)
* @see com.querydsl.core.SimpleQuery#limit(long)
*/
@Override
public SpringDataMongodbQuery<T> limit(long limit) {
query.limit(limit);
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.core.SimpleQuery#offset(long)
*/
@Override
public SpringDataMongodbQuery<T> offset(long offset) {
query.offset(offset);
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.core.SimpleQuery#restrict(com.querydsl.core.QueryModifiers)
*/
@Override
public SpringDataMongodbQuery<T> restrict(QueryModifiers modifiers) {
query.restrict(modifiers);
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.mongodb.AbstractMongodbQuery#getCollection(java.lang.Class)
* @see com.querydsl.core.SimpleQuery#orderBy(com.querydsl.core.types.OrderSpecifier[])
*/
@Override
protected DBCollection getCollection(Class<?> type) {
return ((MongoTemplate) operations).getMongoDbFactory().getLegacyDb()
.getCollection(operations.getCollectionName(type));
public SpringDataMongodbQuery<T> orderBy(OrderSpecifier<?>... o) {
query.orderBy(o);
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.core.SimpleQuery#set(com.querydsl.core.types.ParamExpression, java.lang.Object)
*/
@Override
public <V> SpringDataMongodbQuery<T> set(ParamExpression<V> param, V value) {
query.set(param, value);
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.core.SimpleQuery#distinct()
*/
@Override
public SpringDataMongodbQuery<T> distinct() {
query.distinct();
return this;
}
/*
* (non-Javadoc)
* @see com.querydsl.core.FilteredClause#where(com.querydsl.core.types.Predicate[])
*/
@Override
public SpringDataMongodbQuery<T> where(Predicate... o) {
query.where(o);
return this;
}
/**
* Concrete implementation of {@link FetchableMongodbQuery}.
*
* @param <T>
*/
static class OperationsMongodbQuery<T> extends FetchableMongodbQuery<T, OperationsMongodbQuery<T>> {
public OperationsMongodbQuery(MongodbDocumentSerializer serializer, Class<? extends T> entityClass,
String collection, MongoOperations mongoOperations) {
super(serializer, entityClass, collection, mongoOperations);
}
}
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.repository.support;
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.MongoConverter;
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,7 +47,7 @@ import com.querydsl.mongodb.MongodbSerializer;
* @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<PathType> PATH_TYPES;
@@ -96,7 +90,7 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
return super.visit(expr, context);
}
return toQuerydslMongoType(expr.getConstant());
return converter.convertToMongoType(expr.getConstant());
}
/*
@@ -119,10 +113,10 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
/*
* (non-Javadoc)
* @see com.querydsl.mongodb.MongodbSerializer#asDBObject(java.lang.String, java.lang.Object)
* @see com.querydsl.mongodb.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 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
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,12 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
* @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),
return mapper.getMappedObject(super.asDocument(key, convertedId),
Optional.empty());
return (DBObject) JSON.parse(mappedIdValue.toJson());
}
/*
@@ -250,25 +243,4 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
return property;
}
private Object toQuerydslMongoType(Object source) {
Object target = converter.convertToMongoType(source);
if (target instanceof List) {
List<Object> 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;
}
}

View File

@@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.*;
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;
@@ -26,6 +27,7 @@ import org.bson.Document;
import org.bson.types.ObjectId;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -42,10 +44,6 @@ import org.springframework.data.mongodb.repository.Person.Sex;
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 +94,7 @@ public class SpringDataMongodbSerializerUnitTests {
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 +119,10 @@ public class SpringDataMongodbSerializerUnitTests {
PathBuilder<Address> builder = new PathBuilder<Address>(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 +139,10 @@ public class SpringDataMongodbSerializerUnitTests {
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 +152,14 @@ public class SpringDataMongodbSerializerUnitTests {
ObjectId firstId = new ObjectId("53bb9fd14438765b29c2d56e");
ObjectId secondId = new ObjectId("53bb9fda4438765b29c2d56f");
BasicDBList objectIds = new BasicDBList();
List<Object> 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<Object> $in = getTypedValue(new Document(_id.toMap()), "$in", List.class);
Document _id = getTypedValue(serialized, "_id", Document.class);
List<Object> $in = getTypedValue(_id, "$in", List.class);
assertThat($in, IsIterableContainingInOrder.<Object> contains(firstId, secondId));
}
@@ -182,17 +178,19 @@ public class SpringDataMongodbSerializerUnitTests {
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
@Ignore("FIXME mp911de")
public void shouldRemarshallListsAndDocuments() {
BooleanExpression criteria = QPerson.person.firstname.isNotEmpty()
.and(QPerson.person.firstname.containsIgnoreCase("foo")).not();
assertThat(this.serializer.handle(criteria), is(equalTo(JSON.parse("{ \"$or\" : [ { \"firstname\" : { \"$ne\" : { "
assertThat(this.serializer.handle(criteria),
is(equalTo(Document.parse("{ \"$or\" : [ { \"firstname\" : { \"$not\" : { "
+ "\"$ne\" : \"\"}}} , { \"firstname\" : { \"$not\" : { \"$regex\" : \".*\\\\Qfoo\\\\E.*\" , \"$options\" : \"i\"}}}]}"))));
}