Compare commits

...

19 Commits

Author SHA1 Message Date
Spring Buildmaster
eae463622c DATAMONGO-1079 - Release version 1.6.1.RELEASE (Evans SR1). 2014-10-30 14:36:13 +01:00
Oliver Gierke
6ba8144bca DATAMONGO-1079 - Prepare 1.6.1.RELEASE (Evans SR1). 2014-10-30 12:33:08 +01:00
Oliver Gierke
9d1c1a9fc5 DATAMONGO-1079 - Updated changelog. 2014-10-30 11:57:35 +01:00
Oliver Gierke
f552ca8073 DATAMONGO-1080 - AbstractMongoQuery now refrains from eagerly post-processing the query execution results.
To properly support general post processing of query execution results (in QueryExecutorMethodInterceptor) we need to remove the eager post-processing of query execution results in AbstractMongoQuery.

Removed the usage of the local ConversionService all together.
2014-10-30 11:36:02 +01:00
Thomas Darimont
d7bd82c643 DATAMONGO-1076 - Avoid resolving lazy-loading proxy for DBRefs during finalize.
We now handle intercepted finalize method invocations by not resolving the proxy. Previously the LazyLoadingProxy tried to resolve the proxy during finalization which could lead to unnecessary database accesses.

Original pull request: #234.
2014-10-29 10:16:34 +01:00
Christoph Strobl
078cca83e3 DATAMONGO-1077 - Fix Update removing $ operator for DBRef.
We now retain the positional parameter "$" when mapping field names for associations.

Orignal pull request: #235.
2014-10-28 14:30:08 +01:00
Christoph Strobl
93ae6815bd DATAMONGO-1072 - Fix annotated query placeholders not replaced correctly.
We now also check field names for potential placeholder matches to ensure those are registered for binding parameters.

Original pull request: #233.
2014-10-22 13:56:59 +02:00
Christoph Strobl
13dcb8cda1 DATAMONGO-1068 - Fix getCritieriaObject returns empty DBO when no key defined.
We now check for the presence of a Critieria key.

Original pull request: #232.
2014-10-21 12:06:51 +02:00
Oliver Gierke
a4497bcf8a DATAMONGO-1070 - Fixed a few glitches in DBRef binding for repository query methods.
The QueryMapping for derived repository queries pointing to the identifier of the referenced document. We now reduce the query field's key from reference.id to reference so that the generated DBRef is applied correctly and also take care that the id's are potentially converted to ObjectIds. This is mainly achieved by using the AssociationConverter pulled up from UpdateMapper in ObjectMapper.getMappedKey().

MongoQueryCreator now refrains from translating the field keys as that will fail the QueryMapper to correctly detect id properties.

Fixed DBRef handling for StringBasedMongoQuery which previously didn't parse the DBRef instance created after JSON parsing for placeholders.
2014-10-15 10:14:12 +02:00
Christoph Strobl
6a82c47a4d DATAMONGO-1063 - Fix application of Querydsl'S any().in() throwing Exception.
We now only convert paths that point to either a property or variable.

Original pull request: #230.
2014-10-10 11:35:57 +02:00
Christoph Strobl
8f8f5b7ce4 DATAMONGO-1053 - Type check is now only performed on explicit language properties.
We now only perform a type check on via @Language explicitly defined language properties. Prior to this change non-String properties named language caused errors on entity validation.

Original pull request: #228.
2014-10-10 11:31:39 +02:00
Oliver Gierke
c683813a7a DATAMONGO-1057 - Polishing.
Slightly tweaked the changes in SlicedExecution to simplify the implementation. We now apply the given pageable but tweak the limit the query uses to peek into the next page.

Original pull request: #226.
2014-10-08 07:06:35 +02:00
Christoph Strobl
dbe983c3cb DATAMONGO-1057 - Fix SliceExecution skipping elements.
We now directly set the offset to use instead of reading it from the used pageable. This asserts that every single element is read from the store.
Prior to this change the altered pageSize lead to an unintended increase of the number of elements to skip.

Original pull request: #226.
2014-10-08 07:06:35 +02:00
Oliver Gierke
8e11fe84df DATAMONGO-1062 - Polishing.
Removed exploded static imports. Updated copyright header.

Original pull request: #229.
2014-10-07 16:29:48 +02:00
Christoph Strobl
9eb2856840 DATAMONGO-1058 - DBRef should respect explicit field name.
We now use property.getFieldName() for mapping DbRefs. This assures we also capture explicitly defined names set via @Field.

Original pull request: #227.
2014-10-01 10:05:52 +02:00
Thomas Darimont
828b379f1f DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
The test rejectsAddressConfigWithoutASingleParsableServerAddress fails because the supposedly non-existing hostname "bar" "now" resolves to a real host-address.

The addresses "gugu.nonexistant.example.org, gaga.nonexistant.example.org" shouldn't be resolvable TM.

Original pull request: #229.
2014-10-01 09:44:48 +02:00
Christoph Strobl
161fd8c09d DATAMONGO-1049 - Check for explicitly declared language field.
We now check for an explicitly declared language field for setting language_override within a text index. Therefore the attribute (even if named with the reserved keyword language) has to be explicitly marked with @Language. Prior to this change having:

@Language String lang;
String language;

would have caused trouble when trying to resolve index structures as one cannot set language override to more than one property.

Original pull request: #224.
2014-09-25 12:43:46 +02:00
Oliver Gierke
dc037dfef6 DATAMONGO-1046 - After release cleanups. 2014-09-05 14:17:02 +02:00
Spring Buildmaster
c41653f9da DATAMONGO-1046 - Prepare next development iteration. 2014-09-05 14:09:48 +02:00
36 changed files with 796 additions and 190 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.5.0.RELEASE</version>
<version>1.5.1.RELEASE</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
@@ -29,7 +29,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>1.9.0.RELEASE</springdata.commons>
<springdata.commons>1.9.1.RELEASE</springdata.commons>
<mongo>2.12.3</mongo>
<mongo.osgi>2.12.3</mongo.osgi>
</properties>
@@ -116,7 +116,7 @@
<id>mongo-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</repositories>
</profile>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.6.0.RELEASE</version>
<version>1.6.1.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -178,7 +178,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
Serializable {
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD;
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD;
private final DbRefResolverCallback callback;
private final MongoPersistentProperty property;
@@ -192,6 +192,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
try {
INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("getTarget");
TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef");
FINALIZE_METHOD = Object.class.getDeclaredMethod("finalize");
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -255,6 +256,11 @@ public class DefaultDbRefResolver implements DbRefResolver {
if (ReflectionUtils.isHashCodeMethod(method)) {
return proxyHashCode(proxy);
}
// DATAMONGO-1076 - finalize methods should not trigger proxy initialization
if (FINALIZE_METHOD.equals(method)) {
return null;
}
}
Object target = ensureResolved();

View File

@@ -284,7 +284,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
public void doWithAssociation(Association<MongoPersistentProperty> association) {
final MongoPersistentProperty property = association.getInverse();
Object value = dbo.get(property.getName());
Object value = dbo.get(property.getFieldName());
if (value == null) {
return;

View File

@@ -334,7 +334,8 @@ public class QueryMapper {
}
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
return entity.hasIdProperty() && entity.getIdProperty().getActualType().isAssignableFrom(type);
return entity.hasIdProperty()
&& (type.equals(DBRef.class) || entity.getIdProperty().getActualType().isAssignableFrom(type));
}
/**
@@ -382,10 +383,16 @@ public class QueryMapper {
*/
protected Object convertAssociation(Object source, MongoPersistentProperty property) {
if (property == null || source == null || source instanceof DBRef || source instanceof DBObject) {
if (property == null || source == null || source instanceof DBObject) {
return source;
}
if (source instanceof DBRef) {
DBRef ref = (DBRef) source;
return new DBRef(ref.getDB(), ref.getRef(), convertId(ref.getId()));
}
if (source instanceof Iterable) {
BasicDBList result = new BasicDBList();
for (Object element : (Iterable<?>) source) {
@@ -785,7 +792,7 @@ public class QueryMapper {
*/
@Override
public String getMappedKey() {
return path == null ? name : path.toDotPath(getPropertyConverter());
return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter());
}
protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
@@ -837,5 +844,56 @@ public class QueryMapper {
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return PropertyToFieldNameConverter.INSTANCE;
}
/**
* Return the {@link Converter} to use for creating the mapped key of an association. Default implementation is
* {@link AssociationConverter}.
*
* @return
* @since 1.7
*/
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new AssociationConverter(getAssociation());
}
}
/**
* Converter to skip all properties after an association property was rendered.
*
* @author Oliver Gierke
*/
protected static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
private final MongoPersistentProperty property;
private boolean associationFound;
/**
* Creates a new {@link AssociationConverter} for the given {@link Association}.
*
* @param association must not be {@literal null}.
*/
public AssociationConverter(Association<MongoPersistentProperty> association) {
Assert.notNull(association, "Association must not be null!");
this.property = association.getInverse();
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public String convert(MongoPersistentProperty source) {
if (associationFound) {
return null;
}
if (property.equals(source)) {
associationFound = true;
}
return source.getFieldName();
}
}
}

View File

@@ -194,47 +194,48 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return isAssociation() ? new AssociationConverter(getAssociation()) : new UpdatePropertyConverter(key);
return new UpdatePropertyConverter(key);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getAssociationConverter()
*/
@Override
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new UpdateAssociationConverter(getAssociation(), key);
}
/**
* Converter to skip all properties after an association property was rendered.
* Special mapper handling positional parameter {@literal $} within property names.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.7
*/
private static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
private static class UpdateKeyMapper {
private final MongoPersistentProperty property;
private boolean associationFound;
private final Iterator<String> iterator;
protected UpdateKeyMapper(String rawKey) {
Assert.hasText(rawKey, "Key must not be null or empty!");
this.iterator = Arrays.asList(rawKey.split("\\.")).iterator();
this.iterator.next();
}
/**
* Creates a new {@link AssociationConverter} for the given {@link Association}.
* Maps the property name while retaining potential positional operator {@literal $}.
*
* @param association must not be {@literal null}.
* @param property
* @return
*/
public AssociationConverter(Association<MongoPersistentProperty> association) {
protected String mapPropertyName(MongoPersistentProperty property) {
Assert.notNull(association, "Association must not be null!");
this.property = association.getInverse();
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public String convert(MongoPersistentProperty source) {
if (associationFound) {
return null;
}
if (property.equals(source)) {
associationFound = true;
}
return source.getFieldName();
}
}
/**
@@ -242,10 +243,11 @@ public class UpdateMapper extends QueryMapper {
* contained in the source update key.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
private static class UpdatePropertyConverter implements Converter<MongoPersistentProperty, String> {
private final Iterator<String> iterator;
private final UpdateKeyMapper mapper;
/**
* Creates a new {@link UpdatePropertyConverter} with the given update key.
@@ -256,8 +258,7 @@ public class UpdateMapper extends QueryMapper {
Assert.hasText(updateKey, "Update key must not be null or empty!");
this.iterator = Arrays.asList(updateKey.split("\\.")).iterator();
this.iterator.next();
this.mapper = new UpdateKeyMapper(updateKey);
}
/*
@@ -266,9 +267,37 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
public String convert(MongoPersistentProperty property) {
return mapper.mapPropertyName(property);
}
}
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
/**
* {@link Converter} retaining positional parameter {@literal $} for {@link Association}s.
*
* @author Christoph Strobl
*/
protected static class UpdateAssociationConverter extends AssociationConverter {
private final UpdateKeyMapper mapper;
/**
* Creates a new {@link AssociationConverter} for the given {@link Association}.
*
* @param association must not be {@literal null}.
*/
public UpdateAssociationConverter(Association<MongoPersistentProperty> association, String key) {
super(association);
this.mapper = new UpdateKeyMapper(key);
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public String convert(MongoPersistentProperty source) {
return super.convert(source) == null ? null : mapper.mapPropertyName(source);
}
}
}

View File

@@ -220,7 +220,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
private void appendTextIndexInformation(final String dotPath,
final TextIndexDefinitionBuilder indexDefinitionBuilder, MongoPersistentEntity<?> entity,
final TextIndexDefinitionBuilder indexDefinitionBuilder, final MongoPersistentEntity<?> entity,
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
@@ -230,7 +230,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
guard.protect(persistentProperty, dotPath);
if (persistentProperty.isLanguageProperty()) {
if (persistentProperty.isExplicitLanguageProperty() && !StringUtils.hasText(dotPath)) {
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
}
@@ -257,6 +257,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
} catch (CyclicPropertyReferenceException e) {
LOGGER.warn(e.getMessage(), e);
} catch (InvalidDataAccessApiUsageException e) {
LOGGER.warn(
String.format("Potentially invald index structure discovered. Breaking operation for %s.",
entity.getName()), e);
}
} else if (includeOptions.isForce() || indexed != null) {
indexDefinitionBuilder.onField(propertyDotPath, weight);

View File

@@ -284,7 +284,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
private void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
if (persistentProperty.isLanguageProperty()) {
if (persistentProperty.isExplicitLanguageProperty()) {
assertPropertyType(persistentProperty, String.class);
}
}

View File

@@ -190,9 +190,18 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
*/
@Override
public boolean isLanguageProperty() {
return getFieldName().equals(LANGUAGE_FIELD_NAME) || isAnnotationPresent(Language.class);
return getFieldName().equals(LANGUAGE_FIELD_NAME) || isExplicitLanguageProperty();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isExplicitLanguageProperty()
*/
@Override
public boolean isExplicitLanguageProperty() {
return isAnnotationPresent(Language.class);
};
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isTextScoreProperty()

View File

@@ -61,14 +61,22 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
boolean isExplicitIdProperty();
/**
* Returns whether the property indicates the documents language either by having a {@link #getFieldName()} equal to
* {@literal language} or being annotated with {@link Language}.
* Returns true whether the property indicates the documents language either by having a {@link #getFieldName()} equal
* to {@literal language} or being annotated with {@link Language}.
*
* @return
* @since 1.6
*/
boolean isLanguageProperty();
/**
* Returns true when property being annotated with {@link Language}.
*
* @return
* @since 1.6.1
*/
boolean isExplicitLanguageProperty();
/**
* Returns whether the property holds the documents score calculated by text search. <br/>
* It's marked with {@link TextScore}.

View File

@@ -31,7 +31,9 @@ import org.springframework.data.geo.Shape;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -515,8 +517,11 @@ public class Criteria implements CriteriaDefinition {
* @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getCriteriaObject()
*/
public DBObject getCriteriaObject() {
if (this.criteriaChain.size() == 1) {
return criteriaChain.get(0).getSingleCriteriaObject();
} else if (CollectionUtils.isEmpty(this.criteriaChain) && !CollectionUtils.isEmpty(this.criteria)) {
return getSingleCriteriaObject();
} else {
DBObject criteriaObject = new BasicDBObject();
for (Criteria c : this.criteriaChain) {
@@ -550,6 +555,13 @@ public class Criteria implements CriteriaDefinition {
}
}
if (!StringUtils.hasText(this.key)) {
if (not) {
return new BasicDBObject("$not", dbo);
}
return dbo;
}
DBObject queryCriteria = new BasicDBObject();
if (!NOT_SET.equals(isValue)) {

View File

@@ -21,7 +21,6 @@ import java.util.List;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
@@ -88,39 +87,25 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
applyQueryMetaAttributesWhenPresent(query);
Object result = null;
if (isDeleteQuery()) {
result = new DeleteExecution().execute(query);
return new DeleteExecution().execute(query);
} else if (method.isGeoNearQuery() && method.isPageQuery()) {
MongoParameterAccessor countAccessor = new MongoParametersParameterAccessor(method, parameters);
Query countQuery = createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), countAccessor));
result = new GeoNearExecution(accessor).execute(query, countQuery);
return new GeoNearExecution(accessor).execute(query, countQuery);
} else if (method.isGeoNearQuery()) {
result = new GeoNearExecution(accessor).execute(query);
return new GeoNearExecution(accessor).execute(query);
} else if (method.isSliceQuery()) {
result = new SlicedExecution(accessor.getPageable()).execute(query);
return new SlicedExecution(accessor.getPageable()).execute(query);
} else if (method.isCollectionQuery()) {
result = new CollectionExecution(accessor.getPageable()).execute(query);
return new CollectionExecution(accessor.getPageable()).execute(query);
} else if (method.isPageQuery()) {
result = new PagedExecution(accessor.getPageable()).execute(query);
return new PagedExecution(accessor.getPageable()).execute(query);
} else {
result = new SingleEntityExecution(isCountQuery()).execute(query);
return new SingleEntityExecution(isCountQuery()).execute(query);
}
if (result == null) {
return result;
}
Class<?> expectedReturnType = method.getReturnType().getType();
if (expectedReturnType.isAssignableFrom(result.getClass())) {
return result;
}
return CONVERSION_SERVICE.convert(result, expectedReturnType);
}
private Query applyQueryMetaAttributesWhenPresent(Query query) {
@@ -211,6 +196,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* {@link Execution} for {@link Slice} query methods.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.5
*/
@@ -232,9 +218,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
MongoEntityMetadata<?> metadata = method.getEntityInformation();
int pageSize = pageable.getPageSize();
Pageable slicePageable = new PageRequest(pageable.getPageNumber(), pageSize + 1, pageable.getSort());
List result = operations.find(query.with(slicePageable), metadata.getJavaType(), metadata.getCollectionName());
// Apply Pageable but tweak limit to peek into next page
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
List result = operations.find(modifiedQuery, metadata.getJavaType(), metadata.getCollectionName());
boolean hasNext = result.size() > pageSize;

View File

@@ -102,9 +102,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
MongoPersistentProperty property = path.getLeafProperty();
Criteria criteria = from(part, property,
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
Criteria criteria = from(part, property, where(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
return criteria;
}
@@ -123,9 +121,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
MongoPersistentProperty property = path.getLeafProperty();
return from(part, property,
base.and(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
return from(part, property, base.and(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
}
/*

View File

@@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.StringUtils;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.mongodb.util.JSON;
/**
@@ -199,6 +200,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* {@link Collections#emptyList()}.
*
* @param input
* @param conversionService must not be {@literal null}.
* @return
*/
public List<ParameterBinding> parseParameterBindingsFrom(String input) {
@@ -229,14 +231,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
if (value instanceof String) {
String string = ((String) value).trim();
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
while (valueMatcher.find()) {
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
boolean quoted = (string.startsWith("'") && string.endsWith("'"))
|| (string.startsWith("\"") && string.endsWith("\""));
bindings.add(new ParameterBinding(paramIndex, quoted));
}
potentiallyAddBinding(string, bindings);
} else if (value instanceof Pattern) {
@@ -255,15 +250,37 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
bindings.add(new ParameterBinding(paramIndex, quoted));
}
} else if (value instanceof DBRef) {
DBRef dbref = (DBRef) value;
potentiallyAddBinding(dbref.getRef(), bindings);
potentiallyAddBinding(dbref.getId().toString(), bindings);
} else if (value instanceof DBObject) {
DBObject dbo = (DBObject) value;
for (String field : dbo.keySet()) {
collectParameterReferencesIntoBindings(bindings, field);
collectParameterReferencesIntoBindings(bindings, dbo.get(field));
}
}
}
private void potentiallyAddBinding(String source, List<ParameterBinding> bindings) {
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(source);
while (valueMatcher.find()) {
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
boolean quoted = (source.startsWith("'") && source.endsWith("'"))
|| (source.startsWith("\"") && source.endsWith("\""));
bindings.add(new ParameterBinding(paramIndex, quoted));
}
}
}
/**

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.repository.support;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.springframework.data.mapping.context.MappingContext;
@@ -41,7 +44,17 @@ import com.mysema.query.types.PathType;
*/
class SpringDataMongodbSerializer extends MongodbSerializer {
private final String ID_KEY = "_id";
private static final String ID_KEY = "_id";
private static final Set<PathType> PATH_TYPES;
static {
Set<PathType> pathTypes = new HashSet<PathType>();
pathTypes.add(PathType.VARIABLE);
pathTypes.add(PathType.PROPERTY);
PATH_TYPES = Collections.unmodifiableSet(pathTypes);
}
private final MongoConverter converter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
@@ -138,7 +151,7 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
Path<?> parent = path.getMetadata().getParent();
if (parent == null) {
if (parent == null || !PATH_TYPES.contains(path.getMetadata().getPathType())) {
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 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.
@@ -23,6 +23,7 @@ import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -49,11 +50,17 @@ public class ServerAddressPropertyEditorUnitTests {
/**
* @see DATAMONGO-454
* @see DATAMONGO-1062
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsAddressConfigWithoutASingleParsableServerAddress() {
public void rejectsAddressConfigWithoutASingleParsableAndResolvableServerAddress() {
editor.setAsText("foo, bar");
String unknownHost1 = "gugu.nonexistant.example.org";
String unknownHost2 = "gaga.nonexistant.example.org";
assertUnresolveableHostnames(unknownHost1, unknownHost2);
editor.setAsText(unknownHost1 + "," + unknownHost2);
}
/**
@@ -193,4 +200,16 @@ public class ServerAddressPropertyEditorUnitTests {
assertThat(addresses, hasItem(new ServerAddress(InetAddress.getByName(hostAddress), port)));
}
}
private void assertUnresolveableHostnames(String... hostnames) {
for (String hostname : hostnames) {
try {
InetAddress.getByName(hostname);
Assert.fail("Supposedly unresolveable hostname '" + hostname + "' can be resolved.");
} catch (UnknownHostException expected) {
// ok
}
}
}
}

View File

@@ -2791,6 +2791,7 @@ public class MongoTemplateTests {
@Id public String id;
@Field("db_ref_list")/** @see DATAMONGO-1058 */
@org.springframework.data.mongodb.core.mapping.DBRef//
public List<Sample> dbRefAnnotatedList;

View File

@@ -541,6 +541,26 @@ public class DbRefMappingMongoConverterUnitTests {
assertProxyIsResolved(proxy, false);
}
/**
* @see DATAMONGO-1076
*/
@Test
public void shouldNotTriggerResolvingOfLazyLoadedProxyWhenFinalizeMethodIsInvoked() throws Exception {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(WithObjectMethodOverrideLazyDbRefs.class);
MongoPersistentProperty property = entity.getPersistentProperty("dbRefToConcreteTypeWithPropertyAccess");
String idValue = new ObjectId().toString();
DBRef dbRef = converter.toDBRef(new LazyDbRefTargetPropertyAccess(idValue), property);
WithObjectMethodOverrideLazyDbRefs result = converter.read(WithObjectMethodOverrideLazyDbRefs.class,
new BasicDBObject("dbRefToPlainObject", dbRef));
ReflectionTestUtils.invokeMethod(result.dbRefToPlainObject, "finalize");
assertProxyIsResolved(result.dbRefToPlainObject, false);
}
private Object transport(Object result) {
return SerializationUtils.deserialize(SerializationUtils.serialize(result));
}

View File

@@ -50,6 +50,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -1853,6 +1854,21 @@ public class MappingMongoConverterUnitTests {
converter.read(Item.class, source);
}
/**
* @see DATAMONGO-1058
*/
@Test
public void readShouldRespectExplicitFieldNameForDbRef() {
BasicDBObject source = new BasicDBObject();
source.append("explict-name-for-db-ref", new DBRef(mock(DB.class), "foo", "1"));
converter.read(ClassWithExplicitlyNamedDBRefProperty.class, source);
verify(resolver, times(1)).resolveDbRef(Mockito.any(MongoPersistentProperty.class), Mockito.any(DBRef.class),
Mockito.any(DbRefResolverCallback.class), Mockito.any(DbRefProxyHandler.class));
}
static class GenericType<T> {
T content;
}
@@ -2102,4 +2118,16 @@ public class MappingMongoConverterUnitTests {
@TextScore Float score;
}
class ClassWithExplicitlyNamedDBRefProperty {
@Field("explict-name-for-db-ref")//
@org.springframework.data.mongodb.core.mapping.DBRef//
ClassWithIntId dbRefProperty;
public ClassWithIntId getDbRefProperty() {
return dbRefProperty;
}
}
}

View File

@@ -658,6 +658,22 @@ public class QueryMapperUnitTests {
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("_id", 1).get()));
}
/**
* @see DATAMONGO-1070
*/
@Test
public void mapsIdReferenceToDBRefCorrectly() {
ObjectId id = new ObjectId();
DBObject query = new BasicDBObject("reference.id", new com.mongodb.DBRef(null, "reference", id.toString()));
DBObject result = mapper.getMappedObject(query, context.getPersistentEntity(WithDBRef.class));
assertThat(result.containsField("reference"), is(true));
com.mongodb.DBRef reference = getTypedValue(result, "reference", com.mongodb.DBRef.class);
assertThat(reference.getId(), is(instanceOf(ObjectId.class)));
}
@Document
public class Foo {
@Id private ObjectId id;

View File

@@ -508,6 +508,23 @@ public class UpdateMapperUnitTests {
assertThat(list, equalTo(new BasicDBObjectBuilder().add("_id", "1").get()));
}
/**
* @see DATAMONGO-1077
*/
@Test
public void shouldNotRemovePositionalParameter() {
Update update = new Update();
update.unset("dbRefAnnotatedList.$");
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(DocumentWithDBRefCollection.class));
DBObject $unset = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$unset");
assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get()));
}
@org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface")
static interface DocumentWithReferenceToInterface {

View File

@@ -15,10 +15,7 @@
*/
package org.springframework.data.mongodb.core.index;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import static org.hamcrest.collection.IsEmptyCollection.*;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@@ -26,8 +23,6 @@ import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import org.hamcrest.collection.IsEmptyCollection;
import org.hamcrest.core.IsEqual;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -461,7 +456,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
indexDefinitions.get(0));
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
assertThat(weights.get("nested.foo"), is((Object) 5F));
}
/**
@@ -476,8 +471,8 @@ public class MongoPersistentEntityIndexResolverUnitTests {
"textIndexOnNestedWithMostSpecificValueRoot", indexDefinitions.get(0));
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
assertThat(weights.get("nested.bar"), IsEqual.<Object> equalTo(10F));
assertThat(weights.get("nested.foo"), is((Object) 5F));
assertThat(weights.get("nested.bar"), is((Object) 10F));
}
/**
@@ -487,17 +482,57 @@ public class MongoPersistentEntityIndexResolverUnitTests {
public void shouldSetDefaultLanguageCorrectly() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithDefaultLanguage.class);
assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), IsEqual.<Object> equalTo("spanish"));
assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), is((Object) "spanish"));
}
/**
* @see DATAMONGO-937
* @see DATAMONGO-937, DATAMONGO-1049
*/
@Test
public void shouldResolveTextIndexLanguageOverrideCorrectly() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverrideOnNestedElementRoot.class);
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), IsEqual.<Object> equalTo("lang"));
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverride.class);
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang"));
}
/**
* @see DATAMONGO-1049
*/
@Test
public void shouldIgnoreTextIndexLanguageOverrideOnNestedElements() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverrideOnNestedElement.class);
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is(nullValue()));
}
/**
* @see DATAMONGO-1049
*/
@Test
public void shouldNotCreateIndexDefinitionWhenOnlyLanguageButNoTextIndexPresent() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNoTextIndexPropertyButReservedFieldLanguage.class);
assertThat(indexDefinitions, is(empty()));
}
/**
* @see DATAMONGO-1049
*/
@Test
public void shouldNotCreateIndexDefinitionWhenOnlyAnnotatedLanguageButNoTextIndexPresent() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated.class);
assertThat(indexDefinitions, is(empty()));
}
/**
* @see DATAMONGO-1049
*/
@Test
public void shouldPreferExplicitlyAnnotatedLanguageProperty() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithOverlappingLanguageProps.class);
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang"));
}
@Document
@@ -527,14 +562,12 @@ public class MongoPersistentEntityIndexResolverUnitTests {
static class TextIndexOnNested {
String foo;
}
@Document
static class TextIndexOnNestedWithWeightRoot {
@TextIndexed(weight = 5) TextIndexOnNested nested;
}
@Document
@@ -554,18 +587,39 @@ public class MongoPersistentEntityIndexResolverUnitTests {
}
@Document
static class DocumentWithLanguageOverrideOnNestedElementRoot {
static class DocumentWithLanguageOverrideOnNestedElement {
DocumentWithLanguageOverrideOnNestedElement nested;
DocumentWithLanguageOverride nested;
}
static class DocumentWithLanguageOverrideOnNestedElement {
@Document
static class DocumentWithLanguageOverride {
@TextIndexed String foo;
@Language String lang;
}
@Document
static class DocumentWithNoTextIndexPropertyButReservedFieldLanguage {
String language;
}
@Document
static class DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated {
@Field("language") String lang;
}
@Document
static class DocumentWithOverlappingLanguageProps {
@TextIndexed String foo;
String language;
@Language String lang;
}
}
public static class MixedIndexResolutionTests {
@@ -670,7 +724,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
public void shouldDetectSelfCycleViaCollectionTypeCorrectly() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(SelfCyclingViaCollectionType.class);
assertThat(indexDefinitions, IsEmptyCollection.empty());
assertThat(indexDefinitions, empty());
}
/**
@@ -680,7 +734,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() {
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MultipleObjectsOfSameType.class);
assertThat(indexDefinitions, IsEmptyCollection.empty());
assertThat(indexDefinitions, empty());
}
/**

View File

@@ -24,6 +24,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.util.ClassTypeInformation;
/**
@@ -36,6 +37,7 @@ import org.springframework.data.util.ClassTypeInformation;
public class BasicMongoPersistentEntityUnitTests {
@Mock ApplicationContext context;
@Mock MongoPersistentProperty propertyMock;
@Test
public void subclassInheritsAtDocumentAnnotation() {
@@ -80,6 +82,61 @@ public class BasicMongoPersistentEntityUnitTests {
assertThat(entity.getLanguage(), is("spanish"));
}
/**
* @see DATAMONGO-1053
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test(expected = MappingException.class)
public void verifyShouldThrowExceptionForInvalidTypeOfExplicitLanguageProperty() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
when(propertyMock.isExplicitLanguageProperty()).thenReturn(true);
when(propertyMock.getActualType()).thenReturn((Class) Number.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
}
/**
* @see DATAMONGO-1053
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void verifyShouldPassForStringAsExplicitLanguageProperty() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
when(propertyMock.isExplicitLanguageProperty()).thenReturn(true);
when(propertyMock.getActualType()).thenReturn((Class) String.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
verify(propertyMock, times(1)).isExplicitLanguageProperty();
verify(propertyMock, times(1)).getActualType();
}
/**
* @see DATAMONGO-1053
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void verifyShouldIgnoreNonExplicitLanguageProperty() {
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
ClassTypeInformation.from(AnyDocument.class));
when(propertyMock.isExplicitLanguageProperty()).thenReturn(false);
when(propertyMock.getActualType()).thenReturn((Class) Number.class);
entity.addPersistentProperty(propertyMock);
entity.verify();
verify(propertyMock, times(1)).isExplicitLanguageProperty();
verify(propertyMock, never()).getActualType();
}
@Document(collection = "contacts")
class Contact {
@@ -111,4 +168,8 @@ public class BasicMongoPersistentEntityUnitTests {
static class DocumentWithLanguage {
}
static class AnyDocument {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 the original author or authors.
* Copyright 2010-2014 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.
@@ -22,12 +22,14 @@ import org.junit.Test;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
/**
* @author Oliver Gierke
* @author Thomas Darimont
*/
/**
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
*/
public class CriteriaTests {
@Test
@@ -72,50 +74,94 @@ public class CriteriaTests {
assertThat(left, is(not(right)));
assertThat(right, is(not(left)));
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateAndOperation() {
new Criteria() //
.not() //
.andOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateOrOperation() {
new Criteria() //
.not() //
.orOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateNorOperation() {
new Criteria() //
.not() //
.norOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test
public void shouldNegateFollowingSimpleExpression() {
Criteria c = Criteria.where("age").not().gt(18).and("status").is("student");
DBObject co = c.getCriteriaObject();
assertThat(co, is(notNullValue()));
assertThat(co.toString(), is("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateAndOperation() {
new Criteria() //
.not() //
.andOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateOrOperation() {
new Criteria() //
.not() //
.orOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTryingToNegateNorOperation() {
new Criteria() //
.not() //
.norOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
}
/**
* @see DATAMONGO-507
*/
@Test
public void shouldNegateFollowingSimpleExpression() {
Criteria c = Criteria.where("age").not().gt(18).and("status").is("student");
DBObject co = c.getCriteriaObject();
assertThat(co, is(notNullValue()));
assertThat(co.toString(), is("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
}
/**
* @see DATAMONGO-1068
*/
@Test
public void getCriteriaObjectShouldReturnEmptyDBOWhenNoCriteriaSpecified() {
DBObject dbo = new Criteria().getCriteriaObject();
assertThat(dbo, equalTo(new BasicDBObjectBuilder().get()));
}
/**
* @see DATAMONGO-1068
*/
@Test
public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresent() {
DBObject dbo = new Criteria().lt("foo").getCriteriaObject();
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$lt", "foo").get()));
}
/**
* @see DATAMONGO-1068
*/
@Test
public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresentButMultipleCriteriasPresent() {
DBObject dbo = new Criteria().lt("foo").gt("bar").getCriteriaObject();
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$lt", "foo").add("$gt", "bar").get()));
}
/**
* @see DATAMONGO-1068
*/
@Test
public void getCriteriaObjectShouldRespectNotWhenNoKeyPresent() {
DBObject dbo = new Criteria().lt("foo").not().getCriteriaObject();
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$not", new BasicDBObject("$lt", "foo")).get()));
}
}

View File

@@ -19,6 +19,7 @@ import static java.util.Arrays.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -1023,6 +1024,40 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result, is(notNullValue()));
assertThat(result.firstname, is("Carter"));
assertThat(result.lastname, is("Beauford"));
}
/**
* @see DATAMONGO-1057
*/
@Test
public void sliceShouldTraverseElementsWithoutSkippingOnes() {
repository.deleteAll();
List<Person> persons = new ArrayList<Person>(100);
for (int i = 0; i < 100; i++) {
// format firstname to assert sorting retains proper order
persons.add(new Person(String.format("%03d", i), "ln" + 1, 100));
}
repository.save(persons);
Slice<Person> slice = repository.findByAgeGreaterThan(50, new PageRequest(0, 20, Direction.ASC, "firstname"));
assertThat(slice, contains(persons.subList(0, 20).toArray()));
slice = repository.findByAgeGreaterThan(50, slice.nextPageable());
assertThat(slice, contains(persons.subList(20, 40).toArray()));
}
/**
* @see DATAMONGO-1072
*/
@Test
public void shouldBindPlaceholdersUsedAsKeysCorrectly() {
List<Person> persons = repository.findByKeyValue("firstname", alicia.getFirstname());
assertThat(persons, hasSize(1));
assertThat(persons, hasItem(alicia));
}
}

View File

@@ -46,6 +46,8 @@ public class Person extends Contact {
@SuppressWarnings("unused") private Sex sex;
Date createdAt;
List<String> skills;
@GeoSpatialIndexed private Point location;
private Address address;
@@ -271,6 +273,14 @@ public class Person extends Contact {
this.creator = creator;
}
public void setSkills(List<String> skills) {
this.skills = skills;
}
public List<String> getSkills() {
return skills;
}
/*
* (non-Javadoc)
*

View File

@@ -317,4 +317,7 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
* @see DATAMONGO-1030
*/
PersonSummary findSummaryByLastname(String lastname);
@Query("{ ?0 : ?1 }")
List<Person> findByKeyValue(String key, String value);
}

View File

@@ -17,12 +17,14 @@ package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.bson.types.ObjectId;
import org.hamcrest.core.Is;
@@ -32,10 +34,13 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.Person;
@@ -50,6 +55,8 @@ import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.core.RepositoryMetadata;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
/**
@@ -211,6 +218,88 @@ public class AbstractMongoQueryUnitTests {
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
}
/**
* @see DATAMONGO-1057
*/
@Test
public void slicedExecutionShouldRetainNrOfElementsToSkip() {
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
Pageable page1 = new PageRequest(0, 10);
Pageable page2 = page1.next();
query.execute(new Object[] { "fake", page1 });
query.execute(new Object[] { "fake", page2 });
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getAllValues().get(0).getSkip(), is(0));
assertThat(captor.getAllValues().get(1).getSkip(), is(10));
}
/**
* @see DATAMONGO-1057
*/
@Test
public void slicedExecutionShouldIncrementLimitByOne() {
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
Pageable page1 = new PageRequest(0, 10);
Pageable page2 = page1.next();
query.execute(new Object[] { "fake", page1 });
query.execute(new Object[] { "fake", page2 });
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
assertThat(captor.getAllValues().get(0).getLimit(), is(11));
assertThat(captor.getAllValues().get(1).getLimit(), is(11));
}
/**
* @see DATAMONGO-1057
*/
@Test
public void slicedExecutionShouldRetainSort() {
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
Pageable page1 = new PageRequest(0, 10, Sort.Direction.DESC, "bar");
Pageable page2 = page1.next();
query.execute(new Object[] { "fake", page1 });
query.execute(new Object[] { "fake", page2 });
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
verify(this.mongoOperationsMock, times(2))
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
DBObject expectedSortObject = new BasicDBObjectBuilder().add("bar", -1).get();
assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject));
assertThat(captor.getAllValues().get(1).getSortObject(), is(expectedSortObject));
}
/**
* @see DATAMONGO-1080
*/
@Test
public void doesNotTryToPostProcessQueryResultIntoWrapperType() {
Person reference = new Person();
when(mongoOperationsMock.findOne(Mockito.any(Query.class), eq(Person.class), eq("persons"))).//
thenReturn(reference);
AbstractMongoQuery query = createQueryForMethod("findByLastname", String.class);
assertThat(query.execute(new Object[] { "lastname" }), is((Object) reference));
}
private MongoQueryFake createQueryForMethod(String methodName, Class<?>... paramTypes) {
try {
@@ -272,5 +361,9 @@ public class AbstractMongoQueryUnitTests {
@org.springframework.data.mongodb.repository.Query("{}")
Page<Person> findByAnnotatedQuery(String firstnanme, Pageable pageable);
/** @see DATAMONGO-1057 */
Slice<Person> findByLastname(String lastname, Pageable page);
Optional<Person> findByLastname(String lastname);
}
}

View File

@@ -167,19 +167,6 @@ public class MongoQueryCreatorUnitTests {
assertThat(creator.createQuery(), is(reference));
}
/**
* @see DATAMONGO-291
*/
@Test
public void honoursMappingInformationForPropertyPaths() {
PartTree partTree = new PartTree("findByUsername", User.class);
MongoQueryCreator creator = new MongoQueryCreator(partTree, getAccessor(converter, "Oliver"), context);
Query reference = query(where("foo").is("Oliver"));
assertThat(creator.createQuery(), is(reference));
}
/**
* @see DATAMONGO-338
*/
@@ -268,7 +255,7 @@ public class MongoQueryCreatorUnitTests {
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("foo").regex("^Matt"))));
assertThat(query, is(query(where("username").regex("^Matt"))));
}
/**
@@ -281,7 +268,7 @@ public class MongoQueryCreatorUnitTests {
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("foo").regex("ews$"))));
assertThat(query, is(query(where("username").regex("ews$"))));
}
/**
@@ -294,7 +281,7 @@ public class MongoQueryCreatorUnitTests {
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("foo").regex(".*thew.*"))));
assertThat(query, is(query(where("username").regex(".*thew.*"))));
}
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {

View File

@@ -29,6 +29,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
@@ -42,7 +43,9 @@ import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.core.RepositoryMetadata;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
* Unit tests for {@link StringBasedMongoQuery}.
@@ -255,6 +258,37 @@ public class StringBasedMongoQueryUnitTests {
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1070
*/
@Test
public void parsesDbRefDeclarationsCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithManuallyDefinedDbRef", String.class);
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "myid");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
DBRef dbRef = DBObjectTestUtils.getTypedValue(query.getQueryObject(), "reference", DBRef.class);
assertThat(dbRef.getId(), is((Object) "myid"));
assertThat(dbRef.getRef(), is("reference"));
}
/**
* @see DATAMONGO-1072
*/
@Test
public void shouldParseJsonKeyReplacementCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithPlaceholderInKeyOfJsonStructure", String.class,
String.class);
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "key", "value");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
assertThat(query.getQueryObject(), is(new BasicDBObjectBuilder().add("key", "value").get()));
}
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -291,7 +325,14 @@ public class StringBasedMongoQueryUnitTests {
@Query("{'title': { $regex : '^?0', $options : 'i'}}")
List<DBObject> findByTitleBeginsWithExplicitQuoting(String title);
@Query(value = "{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
@Query("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
List<DBObject> findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4);
@Query("{ 'reference' : { $ref : 'reference', $id : ?0 }}")
Object methodWithManuallyDefinedDbRef(String id);
@Query("{ ?0 : ?1}")
Object methodWithPlaceholderInKeyOfJsonStructure(String keyReplacement, String valueReplacement);
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.repository.support;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,8 +42,7 @@ import com.mysema.query.mongodb.MongodbQuery;
@ContextConfiguration("classpath:infrastructure.xml")
public class QuerydslRepositorySupportUnitTests {
@Autowired
MongoOperations operations;
@Autowired MongoOperations operations;
Person person;
@Before
@@ -54,9 +55,26 @@ public class QuerydslRepositorySupportUnitTests {
@Test
public void providesMongoQuery() {
QPerson p = QPerson.person;
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {
};
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {};
MongodbQuery<Person> query = support.from(p).where(p.lastname.eq("Matthews"));
assertThat(query.uniqueResult(), is(person));
}
/**
* @see DATAMONGO-1063
*/
@Test
public void shouldAllowAny() {
person.setSkills(Arrays.asList("vocalist", "songwriter", "guitarist"));
operations.save(person);
QPerson p = QPerson.person;
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {};
MongodbQuery<Person> query = support.from(p).where(p.skills.any().in("guitarist"));
assertThat(query.uniqueResult(), is(person));
}
}

View File

@@ -1,6 +1,23 @@
Spring Data MongoDB Changelog
=============================
Changes in version 1.6.1.RELEASE (2014-10-30)
---------------------------------------------
* DATAMONGO-1080 - AbstractMongoQuery must not eagerly post-process results.
* DATAMONGO-1079 - Release 1.6.1.
* DATAMONGO-1077 - Update removes positional operator $ in key when used on DBRef property.
* DATAMONGO-1076 - Finalizer hit db on lazy dbrefs.
* DATAMONGO-1072 - Query placeholders in keys no longer correctly substituted.
* DATAMONGO-1070 - Query annotation with $oid leads to a parse error.
* DATAMONGO-1068 - elemMatch of Class Criteria fails to build special cirteria.
* DATAMONGO-1063 - IllegalStateException using any().in().
* DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
* DATAMONGO-1058 - Using @Field("foo") with @Dbref breaking behavior.
* DATAMONGO-1057 - AbstractMongoQuery.SlicedExecution#execute() skips every nth element.
* DATAMONGO-1053 - In 1.6, any field in a mapped object named "language" will fail to map if it is a type other than String.
* DATAMONGO-1049 - Reserved field name 'language' causes trouble.
Changes in version 1.6.0.RELEASE (2014-09-05)
---------------------------------------------
* DATAMONGO-1046 - Release 1.6 GA.

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 1.6 GA
Spring Data MongoDB 1.6.1
Copyright (c) [2010-2014] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").