From f9e50916e1a791cf5db2d68c9c491bad874a9507 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 3 Feb 2011 18:45:07 +0000 Subject: [PATCH] Refactored repository implementation to use QuerySpec API. Use QuerySpec instead of QueryBuilder now. Refactored query method query building to populate CriteriaSpec and work with root QuerySpec object until the API is polished. Some test should break right now as we have to get the skip and sorting stuff into the API. --- .../mongodb/repository/MongoCursorUtils.java | 157 ------------------ .../mongodb/repository/MongoQuery.java | 37 +++-- .../mongodb/repository/MongoQueryCreator.java | 123 ++++++++------ .../mongodb/repository/QueryUtils.java | 78 +++++++++ .../repository/SimpleMongoRepository.java | 33 ++-- .../MongoQueryCreatorUnitTests.java | 17 +- 6 files changed, 196 insertions(+), 249 deletions(-) delete mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoCursorUtils.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoCursorUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoCursorUtils.java deleted file mode 100644 index f3f3898c6..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoCursorUtils.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2010-2011 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.document.mongodb.repository; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.data.document.mongodb.CursorPreparer; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.domain.Sort.Order; - -import com.mongodb.BasicDBObject; -import com.mongodb.DBCursor; - - -/** - * Collection of utility methods to apply sorting and pagination to a - * {@link DBCursor}. - * - * @author Oliver Gierke - */ -abstract class MongoCursorUtils { - - private MongoCursorUtils() { - - } - - /** - * Creates a {@link CursorPreparer} applying the given {@link Pageable} to - * the cursor. - * - * @param pageable - * @return - */ - public static CursorPreparer withPagination(Pageable pageable) { - - return new PaginationCursorPreparer(pageable); - } - - - /** - * Creates a {@link CursorPreparer} to apply the given {@link Sort} to the - * cursor. - * - * @param sort - * @return - */ - public static CursorPreparer withSorting(Sort sort) { - - return new SortingCursorPreparer(sort); - } - - /** - * Applies the given {@link Pageable} to the given {@link DBCursor}. - * - * @author Oliver Gierke - */ - private static class PaginationCursorPreparer implements CursorPreparer { - - private final Pageable pageable; - private final SortingCursorPreparer sortingPreparer; - - - /** - * Creates a new {@link PaginationCursorPreparer}. - * - * @param pageable - */ - public PaginationCursorPreparer(Pageable pageable) { - - this.pageable = pageable; - this.sortingPreparer = - new SortingCursorPreparer(pageable.getSort()); - } - - - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.document.mongodb.CursorPreparer#prepare( - * com.mongodb.DBCursor) - */ - public void prepare(DBCursor cursor) { - - if (pageable == null) { - return; - } - - int toSkip = pageable.getOffset(); - int first = pageable.getPageSize(); - - cursor.limit(first).skip(toSkip); - sortingPreparer.prepare(cursor); - } - } - - /** - * Applies the given {@link Sort} to the given {@link DBCursor}. - * - * @author Oliver Gierke - */ - private static class SortingCursorPreparer implements CursorPreparer { - - private final Sort sort; - - - /** - * Creates a new {@link SortingCursorPreparer}. - * - * @param sort - */ - public SortingCursorPreparer(Sort sort) { - - this.sort = sort; - } - - - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.document.mongodb.CursorPreparer#prepare( - * com.mongodb.DBCursor) - */ - public void prepare(DBCursor cursor) { - - if (sort == null) { - return; - } - - Map sorts = new HashMap(); - - for (Order order : sort) { - sorts.put(order.getProperty(), - Direction.ASC.equals(order.getDirection()) ? 1 : -1); - } - - cursor.sort(new BasicDBObject(sorts)); - } - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQuery.java index 12ab095d6..a195587eb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQuery.java @@ -15,12 +15,13 @@ */ package org.springframework.data.document.mongodb.repository; -import static org.springframework.data.document.mongodb.repository.MongoCursorUtils.*; +import static org.springframework.data.document.mongodb.repository.QueryUtils.*; import java.util.List; import org.springframework.data.document.mongodb.CollectionCallback; import org.springframework.data.document.mongodb.MongoTemplate; +import org.springframework.data.document.mongodb.builder.QuerySpec; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.QueryMethod; @@ -75,28 +76,31 @@ public class MongoQuery implements RepositoryQuery { SimpleParameterAccessor accessor = new SimpleParameterAccessor(method.getParameters(), parameters); - MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, template.getConverter()); - DBObject query = creator.createQuery(); + QuerySpec spec = new QuerySpec(); + + MongoQueryCreator creator = + new MongoQueryCreator(spec, tree, accessor, + template.getConverter()); + creator.createQuery(); if (method.isCollectionQuery()) { - return new CollectionExecution().execute(query); + return new CollectionExecution().execute(spec); } else if (method.isPageQuery()) { return new PagedExecution(creator, accessor.getPageable()) - .execute(query); + .execute(spec); } else { - return new SingleEntityExecution().execute(query); + return new SingleEntityExecution().execute(spec); } } private abstract class Execution { - abstract Object execute(DBObject query); + abstract Object execute(QuerySpec query); - protected List readCollection(DBObject query) { + protected List readCollection(QuerySpec query) { - return template.find(template.getDefaultCollectionName(), - query, method.getDomainClass()); + return template.query(query.build(), method.getDomainClass()); } } @@ -115,7 +119,7 @@ public class MongoQuery implements RepositoryQuery { * #execute(com.mongodb.DBObject) */ @Override - public Object execute(DBObject query) { + public Object execute(QuerySpec query) { return readCollection(query); } @@ -155,12 +159,13 @@ public class MongoQuery implements RepositoryQuery { */ @Override @SuppressWarnings({ "rawtypes", "unchecked" }) - Object execute(DBObject query) { + Object execute(QuerySpec query) { + + int count = getCollectionCursor(query.getQueryObject()).count(); - int count = getCollectionCursor(creator.createQuery()).count(); List result = - template.find(query, method.getDomainClass(), - withPagination(pageable)); + template.query(applyPagination(query, pageable), + method.getDomainClass()); return new PageImpl(result, pageable, count); } @@ -193,7 +198,7 @@ public class MongoQuery implements RepositoryQuery { * #execute(com.mongodb.DBObject) */ @Override - Object execute(DBObject query) { + Object execute(QuerySpec query) { List result = readCollection(query); return result.isEmpty() ? null : result.get(0); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java index 36e8f03db..36f54eeac 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java @@ -15,11 +15,16 @@ */ package org.springframework.data.document.mongodb.repository; +import java.util.Arrays; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.document.mongodb.MongoConverter; +import org.springframework.data.document.mongodb.builder.Criteria; +import org.springframework.data.document.mongodb.builder.CriteriaSpec; +import org.springframework.data.document.mongodb.builder.Query; +import org.springframework.data.document.mongodb.builder.QuerySpec; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.SimpleParameterAccessor; import org.springframework.data.repository.query.SimpleParameterAccessor.BindableParameterIterator; @@ -30,7 +35,6 @@ import org.springframework.data.repository.query.parser.PartTree; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; -import com.mongodb.QueryBuilder; /** @@ -38,10 +42,12 @@ import com.mongodb.QueryBuilder; * * @author Oliver Gierke */ -class MongoQueryCreator extends AbstractQueryCreator { - - private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class); - private final MongoConverter converter; +class MongoQueryCreator extends AbstractQueryCreator { + + private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class); + private final MongoConverter converter; + private final QuerySpec querySpec; + /** * Creates a new {@link MongoQueryCreator} from the given {@link PartTree} @@ -50,9 +56,11 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param tree * @param accessor */ - public MongoQueryCreator(PartTree tree, SimpleParameterAccessor accessor, MongoConverter converter) { + public MongoQueryCreator(QuerySpec querySpec, PartTree tree, + SimpleParameterAccessor accessor, MongoConverter converter) { super(tree, accessor); + this.querySpec = querySpec; this.converter = converter; } @@ -67,10 +75,10 @@ class MongoQueryCreator extends AbstractQueryCreator { * .data.repository.query.SimpleParameterAccessor.BindableParameterIterator) */ @Override - protected QueryBuilder create(Part part, BindableParameterIterator iterator) { + protected CriteriaSpec create(Part part, BindableParameterIterator iterator) { - return from(part.getType(), QueryBuilder.start(part.getProperty().toDotPath()), - iterator); + return from(part.getType(), + querySpec.find(part.getProperty().toDotPath()), iterator); } @@ -84,10 +92,11 @@ class MongoQueryCreator extends AbstractQueryCreator { * .data.repository.query.SimpleParameterAccessor.BindableParameterIterator) */ @Override - protected QueryBuilder and(Part part, QueryBuilder base, + protected CriteriaSpec and(Part part, CriteriaSpec base, BindableParameterIterator iterator) { - return from(part.getType(), base.and(part.getProperty().toDotPath()), iterator); + return from(part.getType(), base.and(part.getProperty().toDotPath()), + iterator); } @@ -95,13 +104,13 @@ class MongoQueryCreator extends AbstractQueryCreator { * (non-Javadoc) * * @see - * org.springframework.data.document.mongodb.repository.AbstractQueryCreator - * #reduceCriterias(java.lang.Object, java.lang.Object) + * org.springframework.data.repository.query.parser.AbstractQueryCreator + * #or(java.lang.Object, java.lang.Object) */ @Override - protected QueryBuilder or(QueryBuilder base, QueryBuilder criteria) { + protected CriteriaSpec or(CriteriaSpec base, CriteriaSpec criteria) { - base.or(criteria.get()); + base.or(Arrays.asList(criteria.build())); return base; } @@ -110,19 +119,19 @@ class MongoQueryCreator extends AbstractQueryCreator { * (non-Javadoc) * * @see - * org.springframework.data.document.mongodb.repository.AbstractQueryCreator - * #finalize(java.lang.Object) + * org.springframework.data.repository.query.parser.AbstractQueryCreator + * #complete(java.lang.Object, org.springframework.data.domain.Sort) */ @Override - protected DBObject complete(QueryBuilder criteria, Sort sort) { + protected Void complete(CriteriaSpec criteria, Sort sort) { - DBObject query = criteria.get(); - - if (LOG.isDebugEnabled()) { - LOG.debug("Created query " + query); - } - - return query; + Query query = criteria.build(); + + if (LOG.isDebugEnabled()) { + LOG.debug("Created query " + query); + } + + return null; } @@ -134,38 +143,39 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param parameters * @return */ - private QueryBuilder from(Type type, QueryBuilder criteria, + private CriteriaSpec from(Type type, CriteriaSpec criteria, BindableParameterIterator parameters) { switch (type) { case GREATER_THAN: - return criteria.greaterThan(getConvertedParameter(parameters)); + return criteria.gt(getConvertedParameter(parameters)); case LESS_THAN: - return criteria.lessThan(getConvertedParameter(parameters)); + return criteria.lt(getConvertedParameter(parameters)); case BETWEEN: - return criteria.greaterThan(getConvertedParameter(parameters)).lessThan(getConvertedParameter(parameters)); + return criteria.gt(getConvertedParameter(parameters)).lt( + getConvertedParameter(parameters)); case IS_NOT_NULL: - return criteria.notEquals(null); + return criteria.not().is(null); case IS_NULL: return criteria.is(null); case LIKE: String value = parameters.next().toString(); - return criteria.regex(toLikeRegex(value)); + return criteria.is(toLikeRegex(value)); case SIMPLE_PROPERTY: return criteria.is(getConvertedParameter(parameters)); case NEGATING_SIMPLE_PROPERTY: - return criteria.notEquals(getConvertedParameter(parameters)); + return criteria.not().is(getConvertedParameter(parameters)); } throw new IllegalArgumentException("Unsupported keyword!"); } - - + + private Object getConvertedParameter(BindableParameterIterator parameters) { - - DBObject result = new BasicDBObject(); - converter.write(new ValueHolder(parameters.next()), result); - return result.get("value"); + + DBObject result = new BasicDBObject(); + converter.write(new ValueHolder(parameters.next()), result); + return result.get("value"); } @@ -175,22 +185,27 @@ class MongoQueryCreator extends AbstractQueryCreator { return Pattern.compile(regex); } - /** - * Simple value holder class to allow conversion and accessing the converted value in a deterministic way. - * - * @author Oliver Gierke - */ + /** + * Simple value holder class to allow conversion and accessing the converted + * value in a deterministic way. + * + * @author Oliver Gierke + */ private static class ValueHolder { - - private Object value; - - public ValueHolder(Object value) { - this.value = value; - } - - @SuppressWarnings("unused") - public Object getValue() { - return value; - } + + private Object value; + + + public ValueHolder(Object value) { + + this.value = value; + } + + + @SuppressWarnings("unused") + public Object getValue() { + + return value; + } } } \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java new file mode 100644 index 000000000..d2411d152 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2011 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.document.mongodb.repository; + +import org.springframework.data.document.mongodb.builder.QuerySpec; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import com.mongodb.DBCursor; + + +/** + * Collection of utility methods to apply sorting and pagination to a + * {@link DBCursor}. + * + * @author Oliver Gierke + */ +abstract class QueryUtils { + + private QueryUtils() { + + } + + + /** + * Applies the given {@link Pageable} to the given {@link QuerySpec}. Will + * do nothing if {@link Pageable} is {@literal null}. + * + * @param spec + * @param pageable + * @return + */ + public static QuerySpec applyPagination(QuerySpec spec, Pageable pageable) { + + if (pageable == null) { + return spec; + } + + spec.limit(pageable.getPageSize()); + // spec.skip(pageable.getOffset()); + + return applySorting(spec, pageable.getSort()); + } + + + /** + * Applies the given {@link Sort} to the {@link QuerySpec}. Will do nothing + * if {@link Sort} is {@literal null}. + * + * @param spec + * @param sort + * @return + */ + public static QuerySpec applySorting(QuerySpec spec, Sort sort) { + + if (sort == null) { + return spec; + } + + // TODO apply sorting + // spec. + + return spec; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java index cbf73598b..4dd7c4cf3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java @@ -15,14 +15,14 @@ */ package org.springframework.data.document.mongodb.repository; -import static org.springframework.data.document.mongodb.repository.MongoCursorUtils.*; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import org.bson.types.ObjectId; import org.springframework.data.document.mongodb.MongoConverter; import org.springframework.data.document.mongodb.MongoTemplate; +import org.springframework.data.document.mongodb.builder.QuerySpec; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -32,7 +32,6 @@ import org.springframework.data.repository.support.IsNewAware; import org.springframework.data.repository.support.RepositorySupport; import org.springframework.util.Assert; -import com.mongodb.BasicDBObject; import com.mongodb.QueryBuilder; @@ -55,8 +54,7 @@ public class SimpleMongoRepository extends * @param domainClass * @param template */ - public SimpleMongoRepository(Class domainClass, - MongoTemplate template) { + public SimpleMongoRepository(Class domainClass, MongoTemplate template) { super(domainClass); @@ -105,12 +103,16 @@ public class SimpleMongoRepository extends * ) */ public T findById(ID id) { - - MongoConverter converter = template.getConverter(); - - List result = template.find(QueryBuilder.start("_id").is(converter.convertObjectId(id)).get(), - getDomainClass()); - return result.isEmpty() ? null : result.get(0); + + MongoConverter converter = template.getConverter(); + + ObjectId objectId = converter.convertObjectId(id); + + List result = + template.query( + new QuerySpec().find("_id").is(objectId).build(), + getDomainClass()); + return result.isEmpty() ? null : result.get(0); } @@ -200,10 +202,11 @@ public class SimpleMongoRepository extends public Page findAll(final Pageable pageable) { Long count = count(); + QuerySpec spec = new QuerySpec(); List list = - template.find(new BasicDBObject(), getDomainClass(), - withPagination(pageable)); + template.query(QueryUtils.applyPagination(spec, pageable), + getDomainClass()); return new PageImpl(list, pageable, count); } @@ -218,8 +221,8 @@ public class SimpleMongoRepository extends */ public List findAll(final Sort sort) { - return template.find(new BasicDBObject(), getDomainClass(), - withSorting(sort)); + QuerySpec query = QueryUtils.applySorting(new QuerySpec(), sort); + return template.query(query, getDomainClass()); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoQueryCreatorUnitTests.java index 942d0ebe4..9a10cf72b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoQueryCreatorUnitTests.java @@ -25,6 +25,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.document.mongodb.MongoConverter; import org.springframework.data.document.mongodb.Person; +import org.springframework.data.document.mongodb.builder.QuerySpec; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.SimpleParameterAccessor; import org.springframework.data.repository.query.parser.PartTree; @@ -40,7 +41,7 @@ public class MongoQueryCreatorUnitTests { Method findByFirstname; Method findByFirstnameAndFriend; - + @Mock MongoConverter converter; @@ -63,16 +64,18 @@ public class MongoQueryCreatorUnitTests { PartTree tree = new PartTree("findByFirstName", Person.class); MongoQueryCreator creator = - new MongoQueryCreator(tree, new SimpleParameterAccessor( - new Parameters(findByFirstname), - new Object[] { "Oliver" }), converter); + new MongoQueryCreator(new QuerySpec(), tree, + new SimpleParameterAccessor(new Parameters( + findByFirstname), new Object[] { "Oliver" }), + converter); creator.createQuery(); creator = - new MongoQueryCreator(new PartTree("findByFirstNameAndFriend", - Person.class), new SimpleParameterAccessor( - new Parameters(findByFirstnameAndFriend), new Object[] { + new MongoQueryCreator(new QuerySpec(), new PartTree( + "findByFirstNameAndFriend", Person.class), + new SimpleParameterAccessor(new Parameters( + findByFirstnameAndFriend), new Object[] { "Oliver", new Person() }), converter); creator.createQuery(); }