Compare commits
45 Commits
3.1.4
...
1.5.4.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7c3ef2aa8 | ||
|
|
3320e49c0b | ||
|
|
286efca52d | ||
|
|
92926befc9 | ||
|
|
703f24ae1c | ||
|
|
febe703954 | ||
|
|
440d16ebc6 | ||
|
|
11c2e90736 | ||
|
|
816a567f29 | ||
|
|
e35486759b | ||
|
|
b80c81f861 | ||
|
|
005d21c0b6 | ||
|
|
3c8b7a54d6 | ||
|
|
b2d59f2539 | ||
|
|
1cc2830e95 | ||
|
|
9a4d6f6fb7 | ||
|
|
e96db8b69b | ||
|
|
13ce75779f | ||
|
|
cc785ecf4f | ||
|
|
41fe1809da | ||
|
|
fd5d39f4d9 | ||
|
|
71d97ff53a | ||
|
|
f2f3faef08 | ||
|
|
b1a488098b | ||
|
|
fd18a8b82c | ||
|
|
761d2748c5 | ||
|
|
e2f3966763 | ||
|
|
2b3e5461c2 | ||
|
|
ca4db459c4 | ||
|
|
e0d0a5dc31 | ||
|
|
18761a39c4 | ||
|
|
ad25751dbb | ||
|
|
879ca6d149 | ||
|
|
8a40cf421c | ||
|
|
fbbb7b6bf7 | ||
|
|
0596403081 | ||
|
|
d9eb18df4d | ||
|
|
132e4a9839 | ||
|
|
4acf8caac1 | ||
|
|
9de3c88e6b | ||
|
|
6115c9562b | ||
|
|
963a222616 | ||
|
|
7a2de49ac1 | ||
|
|
8380806d7a | ||
|
|
e60479e47a |
@@ -11,7 +11,7 @@ For a comprehensive treatment of all the Spring Data MongoDB features, please re
|
||||
* the [User Guide](http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/)
|
||||
* the [JavaDocs](http://docs.spring.io/spring-data/mongodb/docs/current/api/) have extensive comments in them as well.
|
||||
* the home page of [Spring Data MongoDB](http://projects.spring.io/spring-data-mongodb) contains links to articles and other resources.
|
||||
* for more detailed questions, use the [forum](http://forum.spring.io/forum/spring-projects/data/nosql).
|
||||
* for more detailed questions, use [Spring Data Mongodb on Stackoverflow](http://stackoverflow.com/questions/tagged/spring-data-mongodb).
|
||||
|
||||
If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://projects.spring.io/).
|
||||
|
||||
@@ -139,7 +139,7 @@ public class MyService {
|
||||
|
||||
Here are some ways for you to get involved in the community:
|
||||
|
||||
* Get involved with the Spring community on the Spring Community Forums. Please help out on the [forum](http://forum.spring.io/forum/spring-projects/data/nosql) by responding to questions and joining the debate.
|
||||
* Get involved with the Spring community on Stackoverflow and help out on the [spring-data-mongodb](http://stackoverflow.com/questions/tagged/spring-data-mongodb) tag by responding to questions and joining the debate.
|
||||
* Create [JIRA](https://jira.springframework.org/browse/DATADOC) tickets for bugs and new features and comment and vote on the ones that you are interested in.
|
||||
* Github is for social coding: if you want to write code, we encourage contributions through pull requests from [forks of this repository](http://help.github.com/forking/). If you want to contribute code this way, please reference a JIRA ticket as well covering the specific issue you are addressing.
|
||||
* Watch for upcoming articles on Spring by [subscribing](http://spring.io/blog) to spring.io.
|
||||
|
||||
42
pom.xml
42
pom.xml
@@ -2,10 +2,10 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.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.4.0.RELEASE</version>
|
||||
<version>1.4.4.RELEASE</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.8.0.RELEASE</springdata.commons>
|
||||
<springdata.commons>1.8.4.RELEASE</springdata.commons>
|
||||
<mongo>2.12.1</mongo>
|
||||
<mongo.osgi>2.12.1</mongo.osgi>
|
||||
</properties>
|
||||
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>ogierke</id>
|
||||
@@ -104,10 +105,35 @@
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.12.0</mongo>
|
||||
<mongo>2.12.3-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mongo-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo-3-next</id>
|
||||
<properties>
|
||||
<mongo>3.0.0-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mongo-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
@@ -119,14 +145,14 @@
|
||||
<version>${mongo}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-release</id>
|
||||
<url>http://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -7,7 +7,7 @@ Import-Package:
|
||||
Export-Template:
|
||||
org.springframework.data.mongodb.crossstore.*;version="${project.version}"
|
||||
Import-Template:
|
||||
com.mongodb.*;version="${mongo:[=.=.=,+1.0.0)}",
|
||||
com.mongodb.*;version="${mongo.osgi:[=.=.=,+1.0.0)}",
|
||||
javax.persistence.*;version="${jpa:[=.=.=,+1.0.0)}",
|
||||
org.aspectj.*;version="${aspectj:[1.0.0, 2.0.0)}",
|
||||
org.bson.*;version="0",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
sun.reflect;version="0";resolution:=optional
|
||||
Import-Template:
|
||||
com.mongodb.*;version="${mongo:[=.=.=,+1.0.0)}",
|
||||
com.mongodb.*;version="${mongo.osgi:[=.=.=,+1.0.0)}",
|
||||
org.apache.log4j.*;version="${log4j:[=.=.=,+1.0.0)}"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
|
||||
<name>Spring Data MongoDB - Core</name>
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>1.5.4.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
@@ -77,7 +77,7 @@
|
||||
<version>1.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- CDI -->
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
@@ -86,21 +86,21 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>el-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans.test</groupId>
|
||||
<artifactId>cditest-owb</artifactId>
|
||||
<version>${webbeans}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
@@ -115,7 +115,7 @@
|
||||
<version>${validation}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.objenesis</groupId>
|
||||
<artifactId>objenesis</artifactId>
|
||||
@@ -129,23 +129,23 @@
|
||||
<version>4.2.0.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${slf4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -189,9 +189,14 @@
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
|
||||
</systemPropertyVariables>
|
||||
<properties>
|
||||
<property>
|
||||
<name>listener</name>
|
||||
<value>org.springframework.data.mongodb.test.util.CleanMongoDBJunitRunListener</value>
|
||||
</property>
|
||||
</properties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core;
|
||||
import static org.springframework.data.domain.Sort.Direction.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -41,6 +43,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private static final Double ONE = Double.valueOf(1);
|
||||
private static final Double MINUS_ONE = Double.valueOf(-1);
|
||||
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final String collectionName;
|
||||
@@ -140,7 +143,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
Object value = keyDbObject.get(key);
|
||||
|
||||
if ("2d".equals(value)) {
|
||||
if (TWO_D_IDENTIFIERS.contains(value)) {
|
||||
indexFields.add(IndexField.geo(key));
|
||||
} else {
|
||||
|
||||
|
||||
@@ -23,11 +23,15 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.UncategorizedMongoDbException;
|
||||
|
||||
import com.mongodb.MongoCursorNotFoundException;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.MongoException.CursorNotFound;
|
||||
import com.mongodb.MongoException.DuplicateKey;
|
||||
import com.mongodb.MongoException.Network;
|
||||
import com.mongodb.MongoInternalException;
|
||||
import com.mongodb.MongoServerSelectionException;
|
||||
import com.mongodb.MongoSocketException;
|
||||
import com.mongodb.MongoTimeoutException;
|
||||
|
||||
/**
|
||||
* Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate
|
||||
@@ -47,21 +51,23 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
|
||||
|
||||
// Check for well-known MongoException subclasses.
|
||||
|
||||
// All other MongoExceptions
|
||||
if (ex instanceof DuplicateKey) {
|
||||
if (ex instanceof DuplicateKey || ex instanceof DuplicateKeyException) {
|
||||
return new DuplicateKeyException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof Network) {
|
||||
if (ex instanceof Network || ex instanceof MongoSocketException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof CursorNotFound) {
|
||||
if (ex instanceof CursorNotFound || ex instanceof MongoCursorNotFoundException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// Driver 2.12 throws this to indicate connection problems. String comparison to avoid hard dependency
|
||||
if (ex.getClass().getName().equals("com.mongodb.MongoServerSelectionException")) {
|
||||
if (ex instanceof MongoServerSelectionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof MongoTimeoutException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
@@ -69,6 +75,7 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// All other MongoExceptions
|
||||
if (ex instanceof MongoException) {
|
||||
|
||||
int code = ((MongoException) ex).getCode();
|
||||
|
||||
@@ -1070,17 +1070,22 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Entry} containing the {@link MongoPersistentProperty} defining the {@literal id} as
|
||||
* {@link Entry#getKey()} and the {@link Id}s property value as its {@link Entry#getValue()}.
|
||||
* Returns {@link Entry} containing the field name of the id property as {@link Entry#getKey()} and the {@link Id}s
|
||||
* property value as its {@link Entry#getValue()}.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private Map.Entry<MongoPersistentProperty, Object> extractIdPropertyAndValue(Object object) {
|
||||
private Entry<String, Object> extractIdPropertyAndValue(Object object) {
|
||||
|
||||
Assert.notNull(object, "Id cannot be extracted from 'null'.");
|
||||
|
||||
Class<?> objectType = object.getClass();
|
||||
|
||||
if (object instanceof DBObject) {
|
||||
return Collections.singletonMap(ID_FIELD, ((DBObject) object).get(ID_FIELD)).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(objectType);
|
||||
MongoPersistentProperty idProp = entity == null ? null : entity.getIdProperty();
|
||||
|
||||
@@ -1090,7 +1095,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
Object idValue = BeanWrapper.create(object, mongoConverter.getConversionService())
|
||||
.getProperty(idProp, Object.class);
|
||||
return Collections.singletonMap(idProp, idValue).entrySet().iterator().next();
|
||||
return Collections.singletonMap(idProp.getFieldName(), idValue).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1101,8 +1106,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
*/
|
||||
private Query getIdQueryFor(Object object) {
|
||||
|
||||
Map.Entry<MongoPersistentProperty, Object> id = extractIdPropertyAndValue(object);
|
||||
return new Query(where(id.getKey().getFieldName()).is(id.getValue()));
|
||||
Entry<String, Object> id = extractIdPropertyAndValue(object);
|
||||
return new Query(where(id.getKey()).is(id.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1116,7 +1121,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
Assert.notEmpty(objects, "Cannot create Query for empty collection.");
|
||||
|
||||
Iterator<?> it = objects.iterator();
|
||||
Map.Entry<MongoPersistentProperty, Object> firstEntry = extractIdPropertyAndValue(it.next());
|
||||
Entry<String, Object> firstEntry = extractIdPropertyAndValue(it.next());
|
||||
|
||||
ArrayList<Object> ids = new ArrayList<Object>(objects.size());
|
||||
ids.add(firstEntry.getValue());
|
||||
@@ -1125,7 +1130,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
ids.add(extractIdPropertyAndValue(it.next()).getValue());
|
||||
}
|
||||
|
||||
return new Query(where(firstEntry.getKey().getFieldName()).in(ids));
|
||||
return new Query(where(firstEntry.getKey()).in(ids));
|
||||
}
|
||||
|
||||
private void assertUpdateableIdIfNotSet(Object entity) {
|
||||
@@ -1455,13 +1460,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
"Can not use skip or field specification with map reduce operations");
|
||||
}
|
||||
if (query.getQueryObject() != null) {
|
||||
copyMapReduceOptions.put("query", query.getQueryObject());
|
||||
copyMapReduceOptions.put("query", queryMapper.getMappedObject(query.getQueryObject(), null));
|
||||
}
|
||||
if (query.getLimit() > 0) {
|
||||
copyMapReduceOptions.put("limit", query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
copyMapReduceOptions.put("sort", query.getSortObject());
|
||||
copyMapReduceOptions.put("sort", queryMapper.getMappedObject(query.getSortObject(), null));
|
||||
}
|
||||
}
|
||||
return copyMapReduceOptions;
|
||||
@@ -2179,7 +2184,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
cursorToUse = cursorToUse.limit(query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
cursorToUse = cursorToUse.sort(getMappedSortObject(query, type));
|
||||
DBObject sortDbo = type != null ? getMappedSortObject(query, type) : query.getSortObject();
|
||||
cursorToUse = cursorToUse.sort(sortDbo);
|
||||
}
|
||||
if (StringUtils.hasText(query.getHint())) {
|
||||
cursorToUse = cursorToUse.hint(query.getHint());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -28,7 +28,7 @@ import com.mongodb.DBObject;
|
||||
* @author Oliver Gierke
|
||||
* @since 1.3
|
||||
*/
|
||||
class LimitOperation implements AggregationOperation {
|
||||
public class LimitOperation implements AggregationOperation {
|
||||
|
||||
private final long maxElements;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -32,17 +33,29 @@ import com.mongodb.DBObject;
|
||||
*/
|
||||
public class MatchOperation implements AggregationOperation {
|
||||
|
||||
private final Criteria criteria;
|
||||
private final CriteriaDefinition criteriaDefinition;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MatchOperation} for the given {@link Criteria}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @deprecated Use {@link MatchOperation#MatchOperation(CriteriaDefinition)} instead. This constructor is scheduled
|
||||
* for removal in the next versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public MatchOperation(Criteria criteria) {
|
||||
this((CriteriaDefinition) criteria);
|
||||
}
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
this.criteria = criteria;
|
||||
/**
|
||||
* Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
*/
|
||||
public MatchOperation(CriteriaDefinition criteriaDefinition) {
|
||||
|
||||
Assert.notNull(criteriaDefinition, "Criteria must not be null!");
|
||||
this.criteriaDefinition = criteriaDefinition;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -51,6 +64,6 @@ public class MatchOperation implements AggregationOperation {
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$match", context.getMappedObject(criteria.getCriteriaObject()));
|
||||
return new BasicDBObject("$match", context.getMappedObject(criteriaDefinition.getCriteriaObject()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
@@ -75,6 +76,8 @@ import com.mongodb.DBRef;
|
||||
*/
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
|
||||
|
||||
private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s! Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions. Parent object was: %4$s";
|
||||
|
||||
protected static final Logger LOGGER = LoggerFactory.getLogger(MappingMongoConverter.class);
|
||||
|
||||
protected final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
@@ -213,6 +216,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return (S) readMap(typeToUse, dbo, parent);
|
||||
}
|
||||
|
||||
if (dbo instanceof BasicDBList) {
|
||||
throw new MappingException(String.format(INCOMPATIBLE_TYPES, dbo, BasicDBList.class, typeToUse.getType(), parent));
|
||||
}
|
||||
|
||||
// Retrieve persistent entity info
|
||||
MongoPersistentEntity<S> persistentEntity = (MongoPersistentEntity<S>) mappingContext
|
||||
.getPersistentEntity(typeToUse);
|
||||
@@ -253,8 +260,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
Object obj = getValueInternal(prop, dbo, evaluator, result);
|
||||
wrapper.setProperty(prop, obj);
|
||||
wrapper.setProperty(prop, getValueInternal(prop, dbo, evaluator, result));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -263,18 +269,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
|
||||
Object value = dbo.get(property.getName());
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
|
||||
Object obj = dbRefResolver.resolveDbRef(property, dbref, new DbRefResolverCallback() {
|
||||
wrapper.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, new DbRefResolverCallback() {
|
||||
|
||||
@Override
|
||||
public Object resolve(MongoPersistentProperty property) {
|
||||
return getValueInternal(property, dbo, evaluator, parent);
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.setProperty(property, obj);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -314,14 +322,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
boolean handledByCustomConverter = conversions.getCustomWriteTarget(obj.getClass(), DBObject.class) != null;
|
||||
TypeInformation<? extends Object> type = ClassTypeInformation.from(obj.getClass());
|
||||
Class<?> entityType = obj.getClass();
|
||||
boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, DBObject.class) != null;
|
||||
TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType);
|
||||
|
||||
if (!handledByCustomConverter && !(dbo instanceof BasicDBList)) {
|
||||
typeMapper.writeType(type, dbo);
|
||||
}
|
||||
|
||||
writeInternal(obj, dbo, type);
|
||||
Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).initialize() : obj;
|
||||
|
||||
writeInternal(target, dbo, type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +348,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> customTarget = conversions.getCustomWriteTarget(obj.getClass(), DBObject.class);
|
||||
Class<?> entityType = obj.getClass();
|
||||
Class<?> customTarget = conversions.getCustomWriteTarget(entityType, DBObject.class);
|
||||
|
||||
if (customTarget != null) {
|
||||
DBObject result = conversionService.convert(obj, DBObject.class);
|
||||
@@ -345,17 +357,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
if (Map.class.isAssignableFrom(obj.getClass())) {
|
||||
if (Map.class.isAssignableFrom(entityType)) {
|
||||
writeMapInternal((Map<Object, Object>) obj, dbo, ClassTypeInformation.MAP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Collection.class.isAssignableFrom(obj.getClass())) {
|
||||
if (Collection.class.isAssignableFrom(entityType)) {
|
||||
writeCollectionInternal((Collection<?>) obj, ClassTypeInformation.LIST, (BasicDBList) dbo);
|
||||
return;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(obj.getClass());
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
|
||||
writeInternal(obj, dbo, entity);
|
||||
addCustomTypeKeyIfNecessary(typeHint, obj, dbo);
|
||||
}
|
||||
@@ -682,10 +694,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
TypeInformation<?> actualType = type != null ? type.getActualType() : null;
|
||||
Class<?> reference = actualType == null ? Object.class : actualType.getType();
|
||||
Class<?> valueType = ClassUtils.getUserClass(value.getClass());
|
||||
|
||||
boolean notTheSameClass = !value.getClass().equals(reference);
|
||||
boolean notTheSameClass = !valueType.equals(reference);
|
||||
if (notTheSameClass) {
|
||||
typeMapper.writeType(value.getClass(), dbObject);
|
||||
typeMapper.writeType(valueType, dbObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,11 +804,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
idMapper.convertId(id));
|
||||
}
|
||||
|
||||
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator eval,
|
||||
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator evaluator,
|
||||
Object parent) {
|
||||
|
||||
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(dbo, spELContext, parent);
|
||||
return provider.getPropertyValue(prop);
|
||||
return new MongoDbPropertyValueProvider(dbo, evaluator, parent).getPropertyValue(prop);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1012,17 +1024,27 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link PropertyValueProvider} to evaluate a SpEL expression if present on the property or simply accesses the field
|
||||
* of the configured source {@link DBObject}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private class MongoDbPropertyValueProvider implements PropertyValueProvider<MongoPersistentProperty> {
|
||||
|
||||
private final DBObjectAccessor source;
|
||||
private final SpELExpressionEvaluator evaluator;
|
||||
private final Object parent;
|
||||
|
||||
public MongoDbPropertyValueProvider(DBObject source, SpELContext factory, Object parent) {
|
||||
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent);
|
||||
}
|
||||
|
||||
public MongoDbPropertyValueProvider(DBObject source, DefaultSpELExpressionEvaluator evaluator, Object parent) {
|
||||
/**
|
||||
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
|
||||
* parent object.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param evaluator must not be {@literal null}.
|
||||
* @param parent can be {@literal null}.
|
||||
*/
|
||||
public MongoDbPropertyValueProvider(DBObject source, SpELExpressionEvaluator evaluator, Object parent) {
|
||||
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(evaluator);
|
||||
|
||||
@@ -73,7 +73,43 @@ public @interface CompoundIndex {
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
* The name of the index to be created.
|
||||
* The name of the index to be created. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* @CompoundIndex(name = "compound_index", def = "{'h1': 1, 'h2': 1}")
|
||||
* class Hybrid {
|
||||
* String h1, h2;
|
||||
* }
|
||||
*
|
||||
* @CompoundIndex(name = "compound_index", def = "{'n1': 1, 'n2': 1}")
|
||||
* class Nested {
|
||||
* String n1, n2;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: 1, hybrid.h2: 1 } , { name: "hybrid.compound_index" } )
|
||||
* db.root.ensureIndex( { nested.n1: 1, nested.n2: 1 } , { name: "nested.compound_index" } )
|
||||
* db.hybrid.ensureIndex( { h1: 1, h2: 1 } , { name: "compound_index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -107,8 +143,11 @@ public @interface CompoundIndex {
|
||||
/**
|
||||
* Configures the number of seconds after which the collection should expire. Defaults to -1 for no expiry.
|
||||
*
|
||||
* @deprecated TTL cannot be defined for {@link CompoundIndex} having more than one field as key. Will be removed in
|
||||
* 1.6.
|
||||
* @see http://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
int expireAfterSeconds() default -1;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,41 @@ import java.lang.annotation.Target;
|
||||
public @interface GeoSpatialIndexed {
|
||||
|
||||
/**
|
||||
* Name of the property in the document that contains the [x, y] or radial coordinates to index.
|
||||
* Index name. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* class Hybrid {
|
||||
* @GeoSpatialIndexed(name="index") Point h1;
|
||||
* }
|
||||
*
|
||||
* class Nested {
|
||||
* @GeoSpatialIndexed(name="index") Point n1;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: "2d" } , { name: "hybrid.index" } )
|
||||
* db.root.ensureIndex( { nested.n1: "2d" } , { name: "nested.index" } )
|
||||
* db.hybrid.ensureIndex( { h1: "2d" } , { name: "index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -58,7 +58,41 @@ public @interface Indexed {
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
* Index name.
|
||||
* Index name. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* class Hybrid {
|
||||
* @Indexed(name="index") String h1;
|
||||
* }
|
||||
*
|
||||
* class Nested {
|
||||
* @Indexed(name="index") String n1;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: 1 } , { name: "hybrid.index" } )
|
||||
* db.root.ensureIndex( { nested.n1: 1 } , { name: "nested.index" } )
|
||||
* db.hybrid.ensureIndex( { h1: 1} , { name: "index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -21,12 +21,9 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
@@ -94,7 +91,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
Assert.notNull(document, "Given entity is not collection root.");
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root.getType()));
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root));
|
||||
|
||||
final CycleGuard guard = new CycleGuard();
|
||||
|
||||
@@ -103,15 +100,19 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
indexInformation.addAll(resolveIndexForClass(persistentProperty.getActualType(),
|
||||
persistentProperty.getFieldName(), root.getCollection(), guard));
|
||||
}
|
||||
try {
|
||||
if (persistentProperty.isEntity()) {
|
||||
indexInformation.addAll(resolveIndexForClass(persistentProperty.getActualType(),
|
||||
persistentProperty.getFieldName(), root.getCollection(), guard));
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(
|
||||
persistentProperty.getFieldName(), root.getCollection(), persistentProperty);
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexInformation.add(indexDefinitionHolder);
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(
|
||||
persistentProperty.getFieldName(), root.getCollection(), persistentProperty);
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexInformation.add(indexDefinitionHolder);
|
||||
}
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -131,10 +132,11 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass(final Class<?> type, final String path,
|
||||
final String collection, final CycleGuard guard) {
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, type));
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, entity));
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
@@ -176,14 +178,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
private List<IndexDefinitionHolder> potentiallyCreateCompoundIndexDefinitions(String dotPath, String collection,
|
||||
Class<?> type) {
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (AnnotationUtils.findAnnotation(type, CompoundIndexes.class) == null
|
||||
&& AnnotationUtils.findAnnotation(type, CompoundIndex.class) == null) {
|
||||
if (entity.findAnnotation(CompoundIndexes.class) == null && entity.findAnnotation(CompoundIndex.class) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return createCompoundIndexDefinitions(dotPath, collection, type);
|
||||
return createCompoundIndexDefinitions(dotPath, collection, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,34 +196,35 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @return
|
||||
*/
|
||||
protected List<IndexDefinitionHolder> createCompoundIndexDefinitions(String dotPath, String fallbackCollection,
|
||||
Class<?> type) {
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
CompoundIndexes indexes = AnnotationUtils.findAnnotation(type, CompoundIndexes.class);
|
||||
CompoundIndexes indexes = entity.findAnnotation(CompoundIndexes.class);
|
||||
|
||||
if (indexes != null) {
|
||||
for (CompoundIndex index : indexes.value()) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index));
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
}
|
||||
|
||||
CompoundIndex index = AnnotationUtils.findAnnotation(type, CompoundIndex.class);
|
||||
CompoundIndex index = entity.findAnnotation(CompoundIndex.class);
|
||||
|
||||
if (index != null) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index));
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
|
||||
return indexDefinitions;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected IndexDefinitionHolder createCompoundIndexDefinition(String dotPath, String fallbackCollection,
|
||||
CompoundIndex index) {
|
||||
CompoundIndex index, MongoPersistentEntity<?> entity) {
|
||||
|
||||
CompoundIndexDefinition indexDefinition = new CompoundIndexDefinition(resolveCompoundIndexKeyFromStringDefinition(
|
||||
dotPath, index.def()));
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(index.name());
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, null));
|
||||
}
|
||||
|
||||
if (index.unique()) {
|
||||
@@ -237,8 +239,14 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
indexDefinition.background();
|
||||
}
|
||||
|
||||
if (index.expireAfterSeconds() >= 0) {
|
||||
indexDefinition.expire(index.expireAfterSeconds(), TimeUnit.SECONDS);
|
||||
int ttl = index.expireAfterSeconds();
|
||||
|
||||
if (ttl >= 0) {
|
||||
if (indexDefinition.getIndexKeys().keySet().size() > 1) {
|
||||
LOGGER.warn("TTL is not supported for compound index with more than one key. TTL={} will be ignored.", ttl);
|
||||
} else {
|
||||
indexDefinition.expire(ttl, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
String collection = StringUtils.hasText(index.collection()) ? index.collection() : fallbackCollection;
|
||||
@@ -287,7 +295,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
IndexDirection.ASCENDING.equals(index.direction()) ? Sort.Direction.ASC : Sort.Direction.DESC);
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : dotPath);
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persitentProperty));
|
||||
}
|
||||
|
||||
if (index.unique()) {
|
||||
@@ -329,7 +337,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
indexDefinition.withMin(index.min()).withMax(index.max());
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : persistentProperty.getName());
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persistentProperty));
|
||||
}
|
||||
|
||||
indexDefinition.typed(index.type()).withBucketSize(index.bucketSize()).withAdditionalField(index.additionalField());
|
||||
@@ -337,13 +345,30 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
|
||||
}
|
||||
|
||||
private String pathAwareIndexName(String indexName, String dotPath, MongoPersistentProperty property) {
|
||||
|
||||
String nameToUse = StringUtils.hasText(indexName) ? indexName : "";
|
||||
|
||||
if (!StringUtils.hasText(dotPath) || (property != null && dotPath.equals(property.getFieldName()))) {
|
||||
return StringUtils.hasText(nameToUse) ? nameToUse : dotPath;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(dotPath)) {
|
||||
|
||||
nameToUse = StringUtils.hasText(nameToUse) ? (property != null ? dotPath.replace("." + property.getFieldName(),
|
||||
"") : dotPath) + "." + nameToUse : dotPath;
|
||||
}
|
||||
return nameToUse;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
|
||||
* to detect potential cycles within the references.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
private static class CycleGuard {
|
||||
static class CycleGuard {
|
||||
|
||||
private final Map<String, List<Path>> propertyTypeMap;
|
||||
|
||||
@@ -355,6 +380,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @param property The property to inspect
|
||||
* @param path The path under which the property can be reached.
|
||||
* @throws CyclicPropertyReferenceException in case a potential cycle is detected.
|
||||
* @see Path#cycles(MongoPersistentProperty, String)
|
||||
*/
|
||||
void protect(MongoPersistentProperty property, String path) throws CyclicPropertyReferenceException {
|
||||
|
||||
@@ -365,7 +391,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
for (Path existingPath : paths) {
|
||||
|
||||
if (existingPath.cycles(property)) {
|
||||
if (existingPath.cycles(property, path)) {
|
||||
paths.add(new Path(property, path));
|
||||
throw new CyclicPropertyReferenceException(property.getFieldName(), property.getOwner().getType(),
|
||||
existingPath.getPath());
|
||||
@@ -373,7 +399,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
paths.add(new Path(property, path));
|
||||
|
||||
} else {
|
||||
|
||||
ArrayList<Path> paths = new ArrayList<Path>();
|
||||
@@ -386,7 +411,30 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return property.getOwner().getType().getSimpleName() + ":" + property.getFieldName();
|
||||
}
|
||||
|
||||
private static class Path {
|
||||
/**
|
||||
* Path defines the property and its full path from the document root. <br />
|
||||
* A {@link Path} with {@literal spring.data.mongodb} would be created for the property {@code Three.mongodb}.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class One {
|
||||
* Two spring;
|
||||
* }
|
||||
*
|
||||
* class Two {
|
||||
* Three data;
|
||||
* }
|
||||
*
|
||||
* class Three {
|
||||
* String mongodb;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
static class Path {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private final String path;
|
||||
@@ -401,17 +449,23 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return path;
|
||||
}
|
||||
|
||||
boolean cycles(MongoPersistentProperty property) {
|
||||
/**
|
||||
* Checks whether the given property is owned by the same entity and if it has been already visited by a subset of
|
||||
* the current path. Given {@literal foo.bar.bar} cycles if {@literal foo.bar} has already been visited and
|
||||
* {@code class Bar} contains a property of type {@code Bar}. The previously mentioned path would not cycle if
|
||||
* {@code class Bar} contained a property of type {@code SomeEntity} named {@literal bar}.
|
||||
*
|
||||
* @param property
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
boolean cycles(MongoPersistentProperty property, String path) {
|
||||
|
||||
Pattern pattern = Pattern.compile("\\p{Punct}?" + Pattern.quote(property.getFieldName()) + "(\\p{Punct}|\\w)?");
|
||||
Matcher matcher = pattern.matcher(path);
|
||||
|
||||
int count = 0;
|
||||
while (matcher.find()) {
|
||||
count++;
|
||||
if (!property.getOwner().equals(this.property.getOwner())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return count >= 1 && property.getOwner().getType().equals(this.property.getOwner().getType());
|
||||
return path.contains(this.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,8 +495,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return String.format("Found cycle for field '%s' in type '%s' for path '%s'", propertyName, type.getSimpleName(),
|
||||
dotPath);
|
||||
return String.format("Found cycle for field '%s' in type '%s' for path '%s'", propertyName,
|
||||
type != null ? type.getSimpleName() : "unknown", dotPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -40,6 +41,7 @@ import com.mongodb.DBObject;
|
||||
* @author Oliver Gierke
|
||||
* @author Becca Gaspard
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class Update {
|
||||
|
||||
@@ -279,6 +281,7 @@ public class Update {
|
||||
}
|
||||
|
||||
public DBObject getUpdateObject() {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
for (String k : modifierOps.keySet()) {
|
||||
dbo.put(k, modifierOps.get(k));
|
||||
@@ -335,14 +338,52 @@ public class Update {
|
||||
return StringUtils.startsWithIgnoreCase(key, "$");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getUpdateObject().hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Update that = (Update) obj;
|
||||
return this.getUpdateObject().equals(that.getUpdateObject());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return SerializationUtils.serializeToJsonSafely(getUpdateObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifiers holds a distinct collection of {@link Modifier}
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public static class Modifiers {
|
||||
|
||||
private HashMap<String, Modifier> modifiers;
|
||||
private Map<String, Modifier> modifiers;
|
||||
|
||||
public Modifiers() {
|
||||
this.modifiers = new LinkedHashMap<String, Modifier>(1);
|
||||
@@ -355,6 +396,33 @@ public class Update {
|
||||
public void addModifier(Modifier modifier) {
|
||||
this.modifiers.put(modifier.getKey(), modifier);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nullSafeHashCode(modifiers);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Modifiers that = (Modifiers) obj;
|
||||
|
||||
return this.modifiers.equals(that.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,6 +447,7 @@ public class Update {
|
||||
* Implementation of {@link Modifier} representing {@code $each}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class Each implements Modifier {
|
||||
|
||||
@@ -399,6 +468,7 @@ public class Update {
|
||||
}
|
||||
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
@@ -406,21 +476,57 @@ public class Update {
|
||||
return convertedValues;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Update.Modifier#getKey()
|
||||
*/
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "$each";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Update.Modifier#getValue()
|
||||
*/
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nullSafeHashCode(values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (that == null || getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nullSafeEquals(values, ((Each) that).values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for creating {@code $push} modifiers
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class PushOperatorBuilder {
|
||||
|
||||
@@ -453,6 +559,50 @@ public class Update {
|
||||
public Update value(Object value) {
|
||||
return Update.this.push(key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
|
||||
result += 31 * result + getOuterType().hashCode();
|
||||
result += 31 * result + nullSafeHashCode(key);
|
||||
result += 31 * result + nullSafeHashCode(modifiers);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PushOperatorBuilder that = (PushOperatorBuilder) obj;
|
||||
|
||||
if (!getOuterType().equals(that.getOuterType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nullSafeEquals(this.key, that.key) && nullSafeEquals(this.modifiers, that.modifiers);
|
||||
}
|
||||
|
||||
private Update getOuterType() {
|
||||
return Update.this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,7 +638,5 @@ public class Update {
|
||||
public Update value(Object value) {
|
||||
return Update.this.addToSet(this.key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
MongoEntityMetadata<?> metadata = method.getEntityInformation();
|
||||
return countProjection ? operations.count(query, metadata.getJavaType()) : operations.findOne(query,
|
||||
metadata.getJavaType());
|
||||
metadata.getJavaType(), metadata.getCollectionName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
return operations.findAllAndRemove(query, metadata.getJavaType());
|
||||
}
|
||||
|
||||
WriteResult writeResult = operations.remove(query, metadata.getCollectionName());
|
||||
WriteResult writeResult = operations.remove(query, metadata.getJavaType(), metadata.getCollectionName());
|
||||
return writeResult != null ? writeResult.getN() : 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.util.JSONParseException;
|
||||
|
||||
/**
|
||||
* {@link RepositoryQuery} implementation for Mongo.
|
||||
@@ -67,7 +71,24 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
|
||||
return creator.createQuery();
|
||||
Query query = creator.createQuery();
|
||||
|
||||
String fieldSpec = this.getQueryMethod().getFieldSpecification();
|
||||
|
||||
if (!StringUtils.hasText(fieldSpec)) {
|
||||
return query;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
|
||||
result.setSortObject(query.getSortObject());
|
||||
return result;
|
||||
|
||||
} catch (JSONParseException o_O) {
|
||||
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod(),
|
||||
o_O));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -23,7 +26,9 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
@@ -31,17 +36,20 @@ import com.mongodb.util.JSON;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
|
||||
private static final String COUND_AND_DELETE = "Manually defined query for %s cannot be both a count and delete query at the same time!";
|
||||
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StringBasedMongoQuery.class);
|
||||
private static final ParameterBindingParser PARSER = ParameterBindingParser.INSTANCE;
|
||||
|
||||
private final String query;
|
||||
private final String fieldSpec;
|
||||
private final boolean isCountQuery;
|
||||
private final boolean isDeleteQuery;
|
||||
private final List<ParameterBinding> queryParameterBindings;
|
||||
private final List<ParameterBinding> fieldSpecParameterBindings;
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod} and {@link MongoOperations}.
|
||||
@@ -65,7 +73,11 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
super(method, mongoOperations);
|
||||
|
||||
this.query = query;
|
||||
this.queryParameterBindings = PARSER.parseParameterBindingsFrom(query);
|
||||
|
||||
this.fieldSpec = method.getFieldSpecification();
|
||||
this.fieldSpecParameterBindings = PARSER.parseParameterBindingsFrom(method.getFieldSpecification());
|
||||
|
||||
this.isCountQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().count() : false;
|
||||
this.isDeleteQuery = method.hasAnnotatedQuery() ? method.getQueryAnnotation().delete() : false;
|
||||
|
||||
@@ -81,12 +93,12 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
String queryString = replacePlaceholders(query, accessor);
|
||||
String queryString = replacePlaceholders(query, accessor, queryParameterBindings);
|
||||
|
||||
Query query = null;
|
||||
|
||||
if (fieldSpec != null) {
|
||||
String fieldString = replacePlaceholders(fieldSpec, accessor);
|
||||
String fieldString = replacePlaceholders(fieldSpec, accessor, fieldSpecParameterBindings);
|
||||
query = new BasicQuery(queryString, fieldString);
|
||||
} else {
|
||||
query = new BasicQuery(queryString);
|
||||
@@ -119,21 +131,173 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
return this.isDeleteQuery;
|
||||
}
|
||||
|
||||
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor) {
|
||||
/**
|
||||
* Replaced the parameter place-holders with the actual parameter values from the given {@link ParameterBinding}s.
|
||||
*
|
||||
* @param input
|
||||
* @param accessor
|
||||
* @param bindings
|
||||
* @return
|
||||
*/
|
||||
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor, List<ParameterBinding> bindings) {
|
||||
|
||||
Matcher matcher = PLACEHOLDER.matcher(input);
|
||||
String result = input;
|
||||
|
||||
while (matcher.find()) {
|
||||
String group = matcher.group();
|
||||
int index = Integer.parseInt(matcher.group(1));
|
||||
result = result.replace(group, getParameterWithIndex(accessor, index));
|
||||
if (bindings.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
return result;
|
||||
StringBuilder result = new StringBuilder(input);
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
|
||||
String parameter = binding.getParameter();
|
||||
int idx = result.indexOf(parameter);
|
||||
|
||||
if (idx != -1) {
|
||||
result.replace(idx, idx + parameter.length(), getParameterValueForBinding(accessor, binding));
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String getParameterWithIndex(ConvertingParameterAccessor accessor, int index) {
|
||||
return JSON.serialize(accessor.getBindableValue(index));
|
||||
/**
|
||||
* Returns the serialized value to be used for the given {@link ParameterBinding}.
|
||||
*
|
||||
* @param accessor
|
||||
* @param binding
|
||||
* @return
|
||||
*/
|
||||
private String getParameterValueForBinding(ConvertingParameterAccessor accessor, ParameterBinding binding) {
|
||||
|
||||
Object value = accessor.getBindableValue(binding.getParameterIndex());
|
||||
|
||||
if (value instanceof String && binding.isQuoted()) {
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
return JSON.serialize(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* A parser that extracts the parameter bindings from a given query string.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static enum ParameterBindingParser {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final String PARAMETER_PREFIX = "_param_";
|
||||
private static final String PARSEABLE_PARAMETER = "\"" + PARAMETER_PREFIX + "$1\"";
|
||||
private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+)");
|
||||
private static final Pattern PARSEABLE_BINDING_PATTERN = Pattern.compile("\"?" + PARAMETER_PREFIX + "(\\d+)\"?");
|
||||
|
||||
private final static int PARAMETER_INDEX_GROUP = 1;
|
||||
|
||||
/**
|
||||
* Returns a list of {@link ParameterBinding}s found in the given {@code input} or an
|
||||
* {@link Collections#emptyList()}.
|
||||
*
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
public List<ParameterBinding> parseParameterBindingsFrom(String input) {
|
||||
|
||||
if (!StringUtils.hasText(input)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ParameterBinding> bindings = new ArrayList<ParameterBinding>();
|
||||
|
||||
String parseableInput = makeParameterReferencesParseable(input);
|
||||
|
||||
collectParameterReferencesIntoBindings(bindings, JSON.parse(parseableInput));
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
private String makeParameterReferencesParseable(String input) {
|
||||
|
||||
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(input);
|
||||
String parseableInput = matcher.replaceAll(PARSEABLE_PARAMETER);
|
||||
|
||||
return parseableInput;
|
||||
}
|
||||
|
||||
private void collectParameterReferencesIntoBindings(List<ParameterBinding> bindings, Object value) {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
} else if (value instanceof Pattern) {
|
||||
|
||||
String string = ((Pattern) value).toString().trim();
|
||||
|
||||
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
|
||||
while (valueMatcher.find()) {
|
||||
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
|
||||
|
||||
/*
|
||||
* The pattern is used as a direct parameter replacement, e.g. 'field': ?1,
|
||||
* therefore we treat it as not quoted to remain backwards compatible.
|
||||
*/
|
||||
boolean quoted = !string.equals(PARAMETER_PREFIX + paramIndex);
|
||||
|
||||
bindings.add(new ParameterBinding(paramIndex, quoted));
|
||||
}
|
||||
|
||||
} else if (value instanceof DBObject) {
|
||||
|
||||
DBObject dbo = (DBObject) value;
|
||||
|
||||
for (String field : dbo.keySet()) {
|
||||
collectParameterReferencesIntoBindings(bindings, dbo.get(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic parameter binding with name or position information.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class ParameterBinding {
|
||||
|
||||
private final int parameterIndex;
|
||||
private final boolean quoted;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParameterBinding} with the given {@code parameterIndex} and {@code quoted} information.
|
||||
*
|
||||
* @param parameterIndex
|
||||
* @param quoted whether or not the parameter is already quoted.
|
||||
*/
|
||||
public ParameterBinding(int parameterIndex, boolean quoted) {
|
||||
|
||||
this.parameterIndex = parameterIndex;
|
||||
this.quoted = quoted;
|
||||
}
|
||||
|
||||
public boolean isQuoted() {
|
||||
return quoted;
|
||||
}
|
||||
|
||||
public int getParameterIndex() {
|
||||
return parameterIndex;
|
||||
}
|
||||
|
||||
public String getParameter() {
|
||||
return "?" + parameterIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-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.
|
||||
@@ -25,7 +25,10 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.mysema.query.mongodb.MongodbSerializer;
|
||||
import com.mysema.query.types.Constant;
|
||||
import com.mysema.query.types.Operation;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.PathMetadata;
|
||||
import com.mysema.query.types.PathType;
|
||||
@@ -34,9 +37,12 @@ import com.mysema.query.types.PathType;
|
||||
* Custom {@link MongodbSerializer} to take mapping information into account when building keys for constraints.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
|
||||
private final String ID_KEY = "_id";
|
||||
|
||||
private final MongoConverter converter;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final QueryMapper mapper;
|
||||
@@ -44,7 +50,7 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
/**
|
||||
* Creates a new {@link SpringDataMongodbSerializer} for the given {@link MappingContext}.
|
||||
*
|
||||
* @param mappingContext
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public SpringDataMongodbSerializer(MongoConverter converter) {
|
||||
|
||||
@@ -80,10 +86,63 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
@Override
|
||||
protected DBObject asDBObject(String key, Object value) {
|
||||
|
||||
if ("_id".equals(key)) {
|
||||
return super.asDBObject(key, mapper.convertId(value));
|
||||
if (ID_KEY.equals(key)) {
|
||||
return mapper.getMappedObject(super.asDBObject(key, value), null);
|
||||
}
|
||||
|
||||
return super.asDBObject(key, value instanceof Pattern ? value : converter.convertToMongoType(value));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.mysema.query.mongodb.MongodbSerializer#isReference(com.mysema.query.types.Path)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isReference(Path<?> path) {
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(path);
|
||||
return property == null ? false : property.isAssociation();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.mysema.query.mongodb.MongodbSerializer#asReference(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected DBRef asReference(Object constant) {
|
||||
return converter.toDBRef(constant, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.mysema.query.mongodb.MongodbSerializer#asReference(com.mysema.query.types.Operation, int)
|
||||
*/
|
||||
@Override
|
||||
protected DBRef asReference(Operation<?> expr, int constIndex) {
|
||||
|
||||
for (Object arg : expr.getArgs()) {
|
||||
|
||||
if (arg instanceof Path) {
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor((Path<?>) arg);
|
||||
Object constant = ((Constant<?>) expr.getArg(constIndex)).getConstant();
|
||||
|
||||
return converter.toDBRef(constant, property);
|
||||
}
|
||||
}
|
||||
|
||||
return super.asReference(expr, constIndex);
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(Path<?> path) {
|
||||
|
||||
Path<?> parent = path.getMetadata().getParent();
|
||||
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(parent.getType());
|
||||
return entity != null ? entity.getPersistentProperty(path.getMetadata().getName()) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.core;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DefaultIndexOperations}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class DefaultIndexOperationsIntegrationTests {
|
||||
|
||||
static final DBObject GEO_SPHERE_2D = new BasicDBObject("loaction", "2dsphere");
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
DefaultIndexOperations indexOps;
|
||||
DBCollection collection;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class);
|
||||
|
||||
this.collection = this.template.getDb().getCollection(collectionName);
|
||||
this.collection.dropIndexes();
|
||||
|
||||
this.indexOps = new DefaultIndexOperations(template, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1008
|
||||
*/
|
||||
@Test
|
||||
public void getIndexInfoShouldBeAbleToRead2dsphereIndex() {
|
||||
|
||||
collection.createIndex(GEO_SPHERE_2D);
|
||||
|
||||
IndexInfo info = findAndReturnIndexInfo(GEO_SPHERE_2D);
|
||||
assertThat(info.getIndexFields().get(0).isGeo(), is(true));
|
||||
}
|
||||
|
||||
private IndexInfo findAndReturnIndexInfo(DBObject keys) {
|
||||
return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static IndexInfo findAndReturnIndexInfo(Iterable<IndexInfo> candidates, DBObject keys) {
|
||||
return findAndReturnIndexInfo(candidates, DBCollection.genIndexName(keys));
|
||||
}
|
||||
|
||||
private static IndexInfo findAndReturnIndexInfo(Iterable<IndexInfo> candidates, String name) {
|
||||
|
||||
for (IndexInfo info : candidates) {
|
||||
if (ObjectUtils.nullSafeEquals(name, info.getName())) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
throw new AssertionError(String.format("Index with %s was not found", name));
|
||||
}
|
||||
|
||||
static class DefaultIndexOperationsIntegrationTestsSample {}
|
||||
}
|
||||
@@ -2511,6 +2511,37 @@ public class MongoTemplateTests {
|
||||
assertThat(template.getDb().getCollection("sample").find(new BasicDBObject("field", "data")).count(), is(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowSavingOfLazyLoadedDbRefs() {
|
||||
|
||||
template.dropCollection(SomeTemplate.class);
|
||||
template.dropCollection(SomeMessage.class);
|
||||
template.dropCollection(SomeContent.class);
|
||||
|
||||
SomeContent content = new SomeContent();
|
||||
content.id = "content-1";
|
||||
content.text = "spring";
|
||||
template.save(content);
|
||||
|
||||
SomeTemplate tmpl = new SomeTemplate();
|
||||
tmpl.id = "template-1";
|
||||
tmpl.content = content; // @DBRef(lazy=true) tmpl.content
|
||||
|
||||
template.save(tmpl);
|
||||
|
||||
SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class);
|
||||
|
||||
SomeContent loadedContent = savedTmpl.getContent();
|
||||
loadedContent.setText("data");
|
||||
template.save(loadedContent);
|
||||
|
||||
assertThat(template.findById(content.id, SomeContent.class).getText(), is("data"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-880
|
||||
*/
|
||||
@@ -2693,6 +2724,22 @@ public class MongoTemplateTests {
|
||||
assertThat(result.getContent().getName(), is(content.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-970
|
||||
*/
|
||||
@Test
|
||||
public void insertsAndRemovesBasicDbObjectCorrectly() {
|
||||
|
||||
BasicDBObject object = new BasicDBObject("key", "value");
|
||||
template.insert(object, "collection");
|
||||
|
||||
assertThat(object.get("_id"), is(notNullValue()));
|
||||
assertThat(template.findAll(DBObject.class, "collection"), hasSize(1));
|
||||
|
||||
template.remove(object, "collection");
|
||||
assertThat(template.findAll(DBObject.class, "collection"), hasSize(0));
|
||||
}
|
||||
|
||||
static class DoucmentWithNamedIdField {
|
||||
|
||||
@Id String someIdKey;
|
||||
@@ -2940,6 +2987,11 @@ public class MongoTemplateTests {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
@@ -50,11 +51,13 @@ import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
@@ -329,6 +332,26 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(collection, never()).remove(Mockito.any(DBObject.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-948
|
||||
*/
|
||||
@Test
|
||||
public void sortShouldBeTakenAsIsWhenExecutingQueryWithoutSpecificTypeInformation() {
|
||||
|
||||
Query query = Query.query(Criteria.where("foo").is("bar")).with(new Sort("foo"));
|
||||
template.executeQuery(query, "collection1", new DocumentCallbackHandler() {
|
||||
|
||||
@Override
|
||||
public void processDocument(DBObject dbObject) throws MongoException, DataAccessException {
|
||||
// nothing to do - just a test
|
||||
}
|
||||
});
|
||||
|
||||
ArgumentCaptor<DBObject> captor = ArgumentCaptor.forClass(DBObject.class);
|
||||
verify(cursor, times(1)).sort(captor.capture());
|
||||
assertThat(captor.getValue(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
|
||||
}
|
||||
|
||||
class AutogenerateableId {
|
||||
|
||||
@Id BigInteger id;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2011-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.
|
||||
* 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.core.convert;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
@@ -15,6 +30,7 @@ import org.bson.types.Binary;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
@@ -26,6 +42,7 @@ import com.mongodb.DBRef;
|
||||
* Unit tests for {@link CustomConversions}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @auhtor Christoph Strobl
|
||||
*/
|
||||
public class CustomConversionsUnitTests {
|
||||
|
||||
@@ -197,6 +214,35 @@ public class CustomConversionsUnitTests {
|
||||
assertThat(conversionService.convert(new DateTime(), Date.class), is(new Date(0)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
*/
|
||||
@Test
|
||||
public void shouldSelectPropertCustomWriteTargetForCglibProxiedType() {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(FormatToStringConverter.INSTANCE));
|
||||
assertThat(conversions.getCustomWriteTarget(createProxyTypeFor(Format.class)), is(typeCompatibleWith(String.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
*/
|
||||
@Test
|
||||
public void shouldSelectPropertCustomReadTargetForCglibProxiedType() {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(CustomObjectToStringConverter.INSTANCE));
|
||||
assertThat(conversions.hasCustomReadTarget(createProxyTypeFor(Object.class), String.class), is(true));
|
||||
}
|
||||
|
||||
private static Class<?> createProxyTypeFor(Class<?> type) {
|
||||
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setProxyTargetClass(true);
|
||||
factory.setTargetClass(type);
|
||||
|
||||
return factory.getProxy().getClass();
|
||||
}
|
||||
|
||||
enum FormatToStringConverter implements Converter<Format, String> {
|
||||
INSTANCE;
|
||||
|
||||
@@ -251,4 +297,15 @@ public class CustomConversionsUnitTests {
|
||||
return new Date(0);
|
||||
}
|
||||
}
|
||||
|
||||
enum CustomObjectToStringConverter implements Converter<Object, String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Object source) {
|
||||
return source != null ? source.toString() : null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +429,27 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-987
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotGenerateLazyLoadingProxyForNullValues() {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs();
|
||||
lazyDbRefs.id = "42";
|
||||
converter.write(lazyDbRefs, dbo);
|
||||
|
||||
ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, dbo);
|
||||
|
||||
assertThat(result.id, is(lazyDbRefs.id));
|
||||
assertThat(result.dbRefToInterface, is(nullValue()));
|
||||
assertThat(result.dbRefToConcreteCollection, is(nullValue()));
|
||||
assertThat(result.dbRefToConcreteType, is(nullValue()));
|
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor, is(nullValue()));
|
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor, is(nullValue()));
|
||||
}
|
||||
|
||||
private Object transport(Object result) {
|
||||
return SerializationUtils.deserialize(SerializationUtils.serialize(result));
|
||||
}
|
||||
|
||||
@@ -45,10 +45,13 @@ import org.hamcrest.Matchers;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -98,6 +101,8 @@ public class MappingMongoConverterUnitTests {
|
||||
@Mock ApplicationContext context;
|
||||
@Mock DbRefResolver resolver;
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
@@ -1807,6 +1812,60 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(result.shape, is((Shape) sphere));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
*/
|
||||
@Test
|
||||
public void shouldWriteCglibProxiedClassTypeInformationCorrectly() {
|
||||
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setTargetClass(GenericType.class);
|
||||
factory.setProxyTargetClass(true);
|
||||
|
||||
GenericType<?> proxied = (GenericType<?>) factory.getProxy();
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
converter.write(proxied, dbo);
|
||||
|
||||
assertThat(dbo.get("_class"), is((Object) GenericType.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseTargetObjectOfLazyLoadingProxyWhenWriting() {
|
||||
|
||||
LazyLoadingProxy mock = mock(LazyLoadingProxy.class);
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
converter.write(mock, dbo);
|
||||
|
||||
verify(mock, times(1)).initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1034
|
||||
*/
|
||||
@Test
|
||||
public void rejectsBasicDbListToBeConvertedIntoComplexType() {
|
||||
|
||||
BasicDBList inner = new BasicDBList();
|
||||
inner.add("key");
|
||||
inner.add("value");
|
||||
|
||||
BasicDBList outer = new BasicDBList();
|
||||
outer.add(inner);
|
||||
outer.add(inner);
|
||||
|
||||
BasicDBObject source = new BasicDBObject("attributes", outer);
|
||||
|
||||
exception.expect(MappingException.class);
|
||||
exception.expectMessage(Item.class.getName());
|
||||
exception.expectMessage(BasicDBList.class.getName());
|
||||
|
||||
converter.read(Item.class, source);
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
|
||||
@@ -163,8 +163,8 @@ public class MongoPersistentEntityIndexCreatorUnitTests {
|
||||
new MongoPersistentEntityIndexCreator(mappingContext, factory);
|
||||
|
||||
assertThat(keysCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("company.address.location", "2d").get()));
|
||||
assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "location").add("min", -180)
|
||||
.add("max", 180).add("bits", 26).get()));
|
||||
assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "company.address.location")
|
||||
.add("min", -180).add("max", 180).add("bits", 26).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,10 +20,13 @@ import static org.hamcrest.collection.IsEmptyCollection.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.hamcrest.core.IsInstanceOf.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.collection.IsEmptyCollection;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
@@ -38,6 +41,9 @@ import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntityTestDummy.MongoPersistentEntityDummyBuilder;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
|
||||
@@ -289,9 +295,8 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CompoundIndexOnLevelZero.class);
|
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition();
|
||||
assertThat(indexDefinition.getIndexOptions(),
|
||||
equalTo(new BasicDBObjectBuilder().add("name", "compound_index").add("unique", true).add("dropDups", true)
|
||||
.add("sparse", true).add("background", true).add("expireAfterSeconds", 10L).get()));
|
||||
assertThat(indexDefinition.getIndexOptions(), equalTo(new BasicDBObjectBuilder().add("name", "compound_index")
|
||||
.add("unique", true).add("dropDups", true).add("sparse", true).add("background", true).get()));
|
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).add("bar", -1).get()));
|
||||
}
|
||||
|
||||
@@ -304,9 +309,8 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexDefinedOnSuperClass.class);
|
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition();
|
||||
assertThat(indexDefinition.getIndexOptions(),
|
||||
equalTo(new BasicDBObjectBuilder().add("name", "compound_index").add("unique", true).add("dropDups", true)
|
||||
.add("sparse", true).add("background", true).add("expireAfterSeconds", 10L).get()));
|
||||
assertThat(indexDefinition.getIndexOptions(), equalTo(new BasicDBObjectBuilder().add("name", "compound_index")
|
||||
.add("unique", true).add("dropDups", true).add("sparse", true).add("background", true).get()));
|
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).add("bar", -1).get()));
|
||||
}
|
||||
|
||||
@@ -322,7 +326,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertThat(
|
||||
indexDefinition.getIndexOptions(),
|
||||
equalTo(new BasicDBObjectBuilder().add("unique", true).add("dropDups", true).add("sparse", true)
|
||||
.add("background", true).add("expireAfterSeconds", 10L).get()));
|
||||
.add("background", true).get()));
|
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).add("bar", -1).get()));
|
||||
}
|
||||
|
||||
@@ -364,6 +368,22 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-963
|
||||
*/
|
||||
@Test
|
||||
public void compoundIndexShouldIncludeTTLWhenConsistingOfOnlyOneKey() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CompoundIndexWithOnlyOneKeyAndTTL.class);
|
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition();
|
||||
assertThat(
|
||||
indexDefinition.getIndexOptions(),
|
||||
equalTo(new BasicDBObjectBuilder().add("unique", true).add("dropDups", true).add("sparse", true)
|
||||
.add("background", true).add("expireAfterSeconds", 10L).get()));
|
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
|
||||
}
|
||||
|
||||
@Document(collection = "CompoundIndexOnLevelOne")
|
||||
static class CompoundIndexOnLevelOne {
|
||||
|
||||
@@ -400,6 +420,13 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
static class ComountIndexWithAutogeneratedName {
|
||||
|
||||
}
|
||||
|
||||
@Document(collection = "CompoundIndexWithOnlyOneKeyAndTTL")
|
||||
@CompoundIndex(def = "{'foo': 1}", background = true, dropDups = true, expireAfterSeconds = 10, sparse = true,
|
||||
unique = true)
|
||||
static class CompoundIndexWithOnlyOneKeyAndTTL {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class MixedIndexResolutionTests {
|
||||
@@ -469,9 +496,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CycleOnLevelOne.class);
|
||||
assertIndexPathAndCollection("reference.indexedProperty", "cycleOnLevelOne", indexDefinitions.get(0));
|
||||
assertIndexPathAndCollection("reference.cyclicReference.reference.indexedProperty", "cycleOnLevelOne",
|
||||
indexDefinitions.get(1));
|
||||
assertThat(indexDefinitions, hasSize(2));
|
||||
assertThat(indexDefinitions, hasSize(1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,6 +513,123 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertThat(indexDefinitions, hasSize(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-949
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotDetectCycleInSimilarlyNamedProperties() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(SimilarityHolingBean.class);
|
||||
assertIndexPathAndCollection("norm", "similarityHolingBean", indexDefinitions.get(0));
|
||||
assertThat(indexDefinitions, hasSize(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
public void shouldDetectSelfCycleViaCollectionTypeCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(SelfCyclingViaCollectionType.class);
|
||||
assertThat(indexDefinitions, IsEmptyCollection.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MultipleObjectsOfSameType.class);
|
||||
assertThat(indexDefinitions, IsEmptyCollection.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
public void shouldCatchCyclicReferenceExceptionOnRoot() {
|
||||
|
||||
Document documentDummy = new Document() {
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Document.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collection() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class);
|
||||
when(propertyMock.isEntity()).thenReturn(true);
|
||||
when(propertyMock.getActualType()).thenThrow(
|
||||
new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar"));
|
||||
|
||||
MongoPersistentEntity<SelfCyclingViaCollectionType> dummy = MongoPersistentEntityDummyBuilder
|
||||
.forClass(SelfCyclingViaCollectionType.class).withCollection("foo").and(propertyMock)
|
||||
.and(documentDummy).build();
|
||||
|
||||
new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class))
|
||||
.resolveIndexForEntity(dummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedCompoundIndexFixedOnCollection() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedCompoundIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameForNestedTypesWithNamedCompoundIndexDefinition() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedCompoundIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedIndexFixedOnCollection() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedIndex.property_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameForNestedTypesWithNamedIndexDefinition() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedIndex.property_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameOnRootLevel() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("property_index"));
|
||||
}
|
||||
|
||||
@Document
|
||||
static class MixedIndexRoot {
|
||||
|
||||
@@ -554,6 +696,80 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
@Indexed String foo;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class SimilarityHolingBean {
|
||||
|
||||
@Indexed @Field("norm") String normalProperty;
|
||||
@Field("similarityL") private List<SimilaritySibling> listOfSimilarilyNamedEntities = null;
|
||||
}
|
||||
|
||||
static class SimilaritySibling {
|
||||
@Field("similarity") private String similarThoughNotEqualNamedProperty;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class MultipleObjectsOfSameType {
|
||||
|
||||
SelfCyclingViaCollectionType cycleOne;
|
||||
|
||||
SelfCyclingViaCollectionType cycleTwo;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class SelfCyclingViaCollectionType {
|
||||
|
||||
List<SelfCyclingViaCollectionType> cyclic;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
@CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }")
|
||||
static class DocumentWithNamedCompoundIndex {
|
||||
|
||||
String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNamedIndex {
|
||||
|
||||
@Indexed(name = "property_index") String property;
|
||||
}
|
||||
|
||||
static class TypeWithNamedIndex {
|
||||
|
||||
@Indexed(name = "property_index") String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedDocumentHavingNamedCompoundIndex {
|
||||
|
||||
DocumentWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex;
|
||||
}
|
||||
|
||||
@CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }")
|
||||
static class TypeWithNamedCompoundIndex {
|
||||
String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedTypeHavingNamedCompoundIndex {
|
||||
|
||||
TypeWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedDocumentHavingNamedIndex {
|
||||
|
||||
DocumentWithNamedIndex propertyOfTypeHavingNamedIndex;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedTypeHavingNamedIndex {
|
||||
|
||||
TypeWithNamedIndex propertyOfTypeHavingNamedIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static List<IndexDefinitionHolder> prepareMappingContextAndResolveIndexForType(Class<?> type) {
|
||||
@@ -586,4 +802,5 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
}
|
||||
assertThat(holder.getCollection(), equalTo(expectedCollection));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.core.index;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.CycleGuard.Path;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Path}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PathUnitTests {
|
||||
|
||||
@Mock MongoPersistentEntity<?> entityMock;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setUp() {
|
||||
when(entityMock.getType()).thenReturn((Class) Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
public void shouldIdentifyCycleForOwnerOfSameTypeAndMatchingPath() {
|
||||
|
||||
MongoPersistentProperty property = createPersistentPropertyMock(entityMock, "foo");
|
||||
assertThat(new Path(property, "foo.bar").cycles(property, "foo.bar.bar"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void shouldAllowMatchingPathForDifferentOwners() {
|
||||
|
||||
MongoPersistentProperty existing = createPersistentPropertyMock(entityMock, "foo");
|
||||
|
||||
MongoPersistentEntity entityOfDifferentType = Mockito.mock(MongoPersistentEntity.class);
|
||||
when(entityOfDifferentType.getType()).thenReturn(String.class);
|
||||
MongoPersistentProperty toBeVerified = createPersistentPropertyMock(entityOfDifferentType, "foo");
|
||||
|
||||
assertThat(new Path(existing, "foo.bar").cycles(toBeVerified, "foo.bar.bar"), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowEqaulPropertiesOnDifferentPaths() {
|
||||
|
||||
MongoPersistentProperty property = createPersistentPropertyMock(entityMock, "foo");
|
||||
assertThat(new Path(property, "foo.bar").cycles(property, "foo2.bar.bar"), is(false));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private MongoPersistentProperty createPersistentPropertyMock(MongoPersistentEntity owner, String fieldname) {
|
||||
|
||||
MongoPersistentProperty property = Mockito.mock(MongoPersistentProperty.class);
|
||||
when(property.getOwner()).thenReturn(owner);
|
||||
when(property.getFieldName()).thenReturn(fieldname);
|
||||
return property;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.core.mapping;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.SimpleAssociationHandler;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Trivial dummy implementation of {@link MongoPersistentEntity} to be used in tests.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
*/
|
||||
public class MongoPersistentEntityTestDummy<T> implements MongoPersistentEntity<T> {
|
||||
|
||||
private Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>();
|
||||
private Collection<MongoPersistentProperty> properties = new ArrayList<MongoPersistentProperty>();
|
||||
private String collection;
|
||||
private String name;
|
||||
private Class<T> type;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConstructorArgument(PersistentProperty<?> property) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty(PersistentProperty<?> property) {
|
||||
return property != null ? property.isIdProperty() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionProperty(PersistentProperty<?> property) {
|
||||
return property != null ? property.isIdProperty() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getIdProperty() {
|
||||
return getPersistentProperty(Id.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getVersionProperty() {
|
||||
return getPersistentProperty(Version.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getPersistentProperty(String name) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
if (p.getName().equals(name)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
if (p.isAnnotationPresent(annotationType)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVersionProperty() {
|
||||
return getVersionProperty() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTypeAlias() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(PropertyHandler<MongoPersistentProperty> handler) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
handler.doWithPersistentProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(SimplePropertyHandler handler) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
handler.doWithPersistentProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(AssociationHandler<MongoPersistentProperty> handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(SimpleAssociationHandler handler) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
|
||||
return (A) this.annotations.get(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollection() {
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple builder to create {@link MongoPersistentEntityTestDummy} with defined properties.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
*/
|
||||
public static class MongoPersistentEntityDummyBuilder<T> {
|
||||
|
||||
private MongoPersistentEntityTestDummy<T> instance;
|
||||
|
||||
private MongoPersistentEntityDummyBuilder(Class<T> type) {
|
||||
this.instance = new MongoPersistentEntityTestDummy<T>();
|
||||
this.instance.type = type;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static <T> MongoPersistentEntityDummyBuilder<T> forClass(Class<T> type) {
|
||||
return new MongoPersistentEntityDummyBuilder(type);
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> withName(String name) {
|
||||
this.instance.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> and(MongoPersistentProperty property) {
|
||||
this.instance.properties.add(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> withCollection(String collection) {
|
||||
this.instance.collection = collection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> and(Annotation annotation) {
|
||||
this.instance.annotations.put(annotation.annotationType(), annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityTestDummy<T> build() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-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.
|
||||
@@ -32,6 +32,7 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
@@ -50,6 +51,7 @@ import com.mongodb.Mongo;
|
||||
* Integration test for {@link MongoTemplate}'s Map-Reduce operations
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -276,6 +278,31 @@ public class MapReduceTests {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-938
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldUseQueryMapper() {
|
||||
|
||||
DBCollection c = mongoTemplate.getDb().getCollection("jmrWithGeo");
|
||||
|
||||
c.save(new BasicDBObject("x", new String[] { "a", "b" }).append("loc", new double[] { 0, 0 }));
|
||||
c.save(new BasicDBObject("x", new String[] { "b", "c" }).append("loc", new double[] { 0, 0 }));
|
||||
c.save(new BasicDBObject("x", new String[] { "c", "d" }).append("loc", new double[] { 0, 0 }));
|
||||
|
||||
Query query = new Query(where("x").ne(new String[] { "a", "b" }).and("loc")
|
||||
.within(new Box(new double[] { 0, 0 }, new double[] { 1, 1 })));
|
||||
|
||||
MapReduceResults<ValueObject> results = template.mapReduce(query, "jmrWithGeo", mapFunction, reduceFunction,
|
||||
ValueObject.class);
|
||||
|
||||
Map<String, Float> m = copyToMap(results);
|
||||
assertEquals(3, m.size());
|
||||
assertEquals(1, m.get("b").intValue());
|
||||
assertEquals(2, m.get("c").intValue());
|
||||
assertEquals(1, m.get("d").intValue());
|
||||
}
|
||||
|
||||
private void performMapReduce(boolean inline, boolean withQuery) {
|
||||
createMapReduceData();
|
||||
MapReduceResults<ValueObject> results;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,7 @@ import org.junit.Test;
|
||||
* @author Thomas Risberg
|
||||
* @author Becca Gaspard
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class UpdateTests {
|
||||
|
||||
@@ -284,4 +286,71 @@ public class UpdateTests {
|
||||
public void testCreatingUpdateWithNullKeyThrowsException() {
|
||||
Update.update(null, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-953
|
||||
*/
|
||||
@Test
|
||||
public void testEquality() {
|
||||
|
||||
Update actualUpdate = new Update() //
|
||||
.inc("size", 1) //
|
||||
.set("nl", null) //
|
||||
.set("directory", "/Users/Test/Desktop") //
|
||||
.push("authors", Collections.singletonMap("name", "Sven")) //
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
Update expectedUpdate = new Update() //
|
||||
.inc("size", 1) //
|
||||
.set("nl", null) //
|
||||
.set("directory", "/Users/Test/Desktop") //
|
||||
.push("authors", Collections.singletonMap("name", "Sven")) //
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
assertThat(actualUpdate, is(equalTo(actualUpdate)));
|
||||
assertThat(actualUpdate.hashCode(), is(equalTo(actualUpdate.hashCode())));
|
||||
assertThat(actualUpdate, is(equalTo(expectedUpdate)));
|
||||
assertThat(actualUpdate.hashCode(), is(equalTo(expectedUpdate.hashCode())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-953
|
||||
*/
|
||||
@Test
|
||||
public void testToString() {
|
||||
|
||||
Update actualUpdate = new Update() //
|
||||
.inc("size", 1) //
|
||||
.set("nl", null) //
|
||||
.set("directory", "/Users/Test/Desktop") //
|
||||
.push("authors", Collections.singletonMap("name", "Sven")) //
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
Update expectedUpdate = new Update() //
|
||||
.inc("size", 1) //
|
||||
.set("nl", null) //
|
||||
.set("directory", "/Users/Test/Desktop") //
|
||||
.push("authors", Collections.singletonMap("name", "Sven")) //
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
assertThat(actualUpdate.toString(), is(equalTo(expectedUpdate.toString())));
|
||||
assertThat(actualUpdate.toString(), is("{ \"$inc\" : { \"size\" : 1} ," //
|
||||
+ " \"$set\" : { \"nl\" : null , \"directory\" : \"/Users/Test/Desktop\" , \"foo\" : \"bar\"} , " //
|
||||
+ "\"$push\" : { \"authors\" : { \"name\" : \"Sven\"}} " //
|
||||
+ ", \"$pop\" : { \"authors\" : -1}}")); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1002
|
||||
*/
|
||||
@Test
|
||||
public void toStringWorksForUpdateWithComplexObject() {
|
||||
|
||||
Update update = new Update().addToSet("key", new DateTime());
|
||||
assertThat(update.toString(), is(notNullValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -880,4 +881,86 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
|
||||
assertThat(result.getContent(), hasSize(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Test
|
||||
public void findByCustomQueryFirstnamesInListAndLastname() {
|
||||
|
||||
repository.save(new Person("foo", "bar"));
|
||||
repository.save(new Person("bar", "bar"));
|
||||
repository.save(new Person("fuu", "bar"));
|
||||
repository.save(new Person("notfound", "bar"));
|
||||
|
||||
Page<Person> result = repository.findByCustomQueryFirstnamesAndLastname(Arrays.asList("bar", "foo", "fuu"), "bar",
|
||||
new PageRequest(0, 2));
|
||||
|
||||
assertThat(result.getContent(), hasSize(2));
|
||||
assertThat(result.getTotalPages(), is(2));
|
||||
assertThat(result.getTotalElements(), is(3L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Test
|
||||
public void findByCustomQueryLastnameAndStreetInList() {
|
||||
|
||||
repository.save(new Person("foo", "bar").withAddress(new Address("street1", "1", "SB")));
|
||||
repository.save(new Person("bar", "bar").withAddress(new Address("street2", "1", "SB")));
|
||||
repository.save(new Person("fuu", "bar").withAddress(new Address("street1", "2", "RGB")));
|
||||
repository.save(new Person("notfound", "notfound"));
|
||||
|
||||
Page<Person> result = repository.findByCustomQueryLastnameAndAddressStreetInList("bar",
|
||||
Arrays.asList("street1", "street2"), new PageRequest(0, 2));
|
||||
|
||||
assertThat(result.getContent(), hasSize(2));
|
||||
assertThat(result.getTotalPages(), is(2));
|
||||
assertThat(result.getTotalElements(), is(3L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignored for now as this requires Querydsl 3.4.1 to succeed.
|
||||
*
|
||||
* @see DATAMONGO-972
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void shouldExecuteFindOnDbRefCorrectly() {
|
||||
|
||||
operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class);
|
||||
|
||||
User user = new User();
|
||||
user.setUsername("Valerie Matthews");
|
||||
|
||||
operations.save(user);
|
||||
|
||||
dave.setCreator(user);
|
||||
operations.save(dave);
|
||||
|
||||
assertThat(repository.findOne(QPerson.person.creator.eq(user)), is(dave));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-969
|
||||
*/
|
||||
@Test
|
||||
public void shouldFindPersonsWhenUsingQueryDslPerdicatedOnIdProperty() {
|
||||
assertThat(repository.findAll(person.id.in(Arrays.asList(dave.id, carter.id))), containsInAnyOrder(dave, carter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1030
|
||||
*/
|
||||
@Test
|
||||
public void executesSingleEntityQueryWithProjectionCorrectly() {
|
||||
|
||||
PersonSummary result = repository.findSummaryByLastname("Beauford");
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.firstname, is("Carter"));
|
||||
assertThat(result.lastname, is("Beauford"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,16 @@ public class Person extends Contact {
|
||||
return this.getId().equals(that.getId());
|
||||
}
|
||||
|
||||
public Person withAddress(Address address) {
|
||||
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setCreator(User creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -290,4 +290,21 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
Page<Person> findByAddressIn(List<Address> address, Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Query("{firstname:{$in:?0}, lastname:?1}")
|
||||
Page<Person> findByCustomQueryFirstnamesAndLastname(List<String> firstnames, String lastname, Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Query("{lastname:?0, address.street:{$in:?1}}")
|
||||
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames, Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1030
|
||||
*/
|
||||
PersonSummary findSummaryByLastname(String lastname);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PersonSummary {
|
||||
|
||||
String firstname;
|
||||
String lastname;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ import com.mongodb.WriteResult;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AbstracMongoQueryUnitTests {
|
||||
public class AbstractMongoQueryUnitTests {
|
||||
|
||||
@Mock RepositoryMetadata metadataMock;
|
||||
@Mock MongoOperations mongoOperationsMock;
|
||||
@@ -88,7 +88,8 @@ public class AbstracMongoQueryUnitTests {
|
||||
|
||||
createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq("persons"));
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq(Person.class),
|
||||
Matchers.eq("persons"));
|
||||
verify(this.mongoOperationsMock, times(0)).find(Matchers.any(Query.class), Matchers.any(Class.class),
|
||||
Matchers.anyString());
|
||||
}
|
||||
@@ -122,19 +123,21 @@ public class AbstracMongoQueryUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-566
|
||||
* @see DATAMONGO-978
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() {
|
||||
|
||||
when(writeResultMock.getN()).thenReturn(100);
|
||||
when(this.mongoOperationsMock.remove(Matchers.any(Query.class), Matchers.eq("persons")))
|
||||
when(this.mongoOperationsMock.remove(Matchers.any(Query.class), Matchers.eq(Person.class), Matchers.eq("persons")))
|
||||
.thenReturn(writeResultMock);
|
||||
|
||||
MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class);
|
||||
query.setDeleteQuery(true);
|
||||
|
||||
assertThat(query.execute(new Object[] { "fake" }), is((Object) 100L));
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq("persons"));
|
||||
verify(this.mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), Matchers.eq(Person.class),
|
||||
Matchers.eq("persons"));
|
||||
}
|
||||
|
||||
private MongoQueryFake createQueryForMethod(String methodName, Class<?>... paramTypes) {
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PartTreeMongoQuery}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PartTreeMongoQueryUnitTests {
|
||||
|
||||
@Mock RepositoryMetadata metadataMock;
|
||||
@Mock MongoOperations mongoOperationsMock;
|
||||
|
||||
MongoMappingContext mappingContext;
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() {
|
||||
|
||||
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
|
||||
when(metadataMock.getReturnedDomainClass(Matchers.any(Method.class))).thenReturn((Class) Person.class);
|
||||
mappingContext = new MongoMappingContext();
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
|
||||
MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
|
||||
when(mongoOperationsMock.getConverter()).thenReturn(converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void rejectsInvalidFieldSpecification() {
|
||||
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectMessage("findByLastname");
|
||||
|
||||
deriveQueryFromMethod("findByLastname", new Object[] { "foo" });
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void singleFieldJsonIncludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstname",
|
||||
new Object[] { "foo" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void multiFieldJsonIncludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstnameAndLastname",
|
||||
new Object[] { "foo", "bar" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).add("lastname", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void multiFieldJsonExcludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findPersonByFirstnameAndLastname",
|
||||
new Object[] { "foo", "bar" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 0).add("lastname", 0).get()));
|
||||
}
|
||||
|
||||
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object[] args) {
|
||||
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
types[i] = args[i].getClass();
|
||||
}
|
||||
|
||||
PartTreeMongoQuery partTreeQuery = createQueryForMethod(method, types);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(partTreeQuery.getQueryMethod(), args);
|
||||
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mongoOperationsMock.getConverter(), accessor));
|
||||
}
|
||||
|
||||
private PartTreeMongoQuery createQueryForMethod(String methodName, Class<?>... paramTypes) {
|
||||
|
||||
try {
|
||||
|
||||
Method method = Repo.class.getMethod(methodName, paramTypes);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadataMock, mappingContext);
|
||||
|
||||
return new PartTreeMongoQuery(queryMethod, mongoOperationsMock);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
interface Repo extends MongoRepository<Person, Long> {
|
||||
|
||||
@Query(fields = "firstname")
|
||||
Person findByLastname(String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 1 }")
|
||||
Person findByFirstname(String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 1, 'lastname' : 1 }")
|
||||
Person findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 0, 'lastname' : 0 }")
|
||||
Person findPersonByFirstnameAndLastname(String firstname, String lastname);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@ import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -46,6 +49,7 @@ import com.mongodb.DBObject;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StringBasedMongoQueryUnitTests {
|
||||
@@ -158,6 +162,99 @@ public class StringBasedMongoQueryUnitTests {
|
||||
createQueryForMethod("invalidMethod", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
|
||||
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1) });
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", DBObject.class,
|
||||
Map.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
|
||||
assertThat(query.getQueryObject(),
|
||||
is(new BasicQuery("{ \"firstname\": \"first\", \"lastname\": \"last\"}").getQueryObject()));
|
||||
assertThat(query.getFieldsObject(), is(new BasicQuery(null, "{ \"lastname\": 1}").getFieldsObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportRespectExistingQuotingInFindByTitleBeginsWithExplicitQuoting() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { "fun" });
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByTitleBeginsWithExplicitQuoting", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
|
||||
assertThat(query.getQueryObject(), is(new BasicQuery("{title: {$regex: '^fun', $options: 'i'}}").getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-995, DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void shouldParseQueryWithParametersInExpression() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { 1, 2, 3, 4 });
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class,
|
||||
int.class, int.class, int.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
|
||||
assertThat(query.getQueryObject(), is(new BasicQuery(
|
||||
"{$where: 'return this.date.getUTCMonth() == 3 && this.date.getUTCDay() == 4;'}").getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-995, DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-995, DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-995, DATAMONGO-420
|
||||
*/
|
||||
@Test
|
||||
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
@@ -170,6 +267,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Query("{ 'lastname' : ?0 }")
|
||||
Person findByLastname(String lastname);
|
||||
|
||||
@Query("{ 'lastname' : '?0' }")
|
||||
Person findByLastnameQuoted(String lastname);
|
||||
|
||||
@Query("{ 'address' : ?0 }")
|
||||
Person findByAddress(Address address);
|
||||
|
||||
@@ -184,5 +284,14 @@ public class StringBasedMongoQueryUnitTests {
|
||||
|
||||
@Query(value = "{ 'lastname' : ?0 }", delete = true, count = true)
|
||||
void invalidMethod(String lastname);
|
||||
|
||||
@Query(value = "?0", fields = "?1")
|
||||
DBObject findByParameterizedCriteriaAndFields(DBObject criteria, Map<String, Integer> fields);
|
||||
|
||||
@Query("{'title': { $regex : '^?0', $options : 'i'}}")
|
||||
List<DBObject> findByTitleBeginsWithExplicitQuoting(String title);
|
||||
|
||||
@Query(value = "{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
|
||||
List<DBObject> findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-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.
|
||||
@@ -17,8 +17,10 @@ package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -32,6 +34,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
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.mysema.query.types.expr.BooleanOperation;
|
||||
@@ -43,6 +46,7 @@ import com.mysema.query.types.path.StringPath;
|
||||
* Unit tests for {@link SpringDataMongodbSerializer}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SpringDataMongodbSerializerUnitTests {
|
||||
@@ -132,6 +136,41 @@ public class SpringDataMongodbSerializerUnitTests {
|
||||
assertThat(path, is("0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-969
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertObjectIdEvenWhenNestedInOperatorDbObject() {
|
||||
|
||||
ObjectId value = new ObjectId("53bb9fd14438765b29c2d56e");
|
||||
DBObject serialized = serializer.asDBObject("_id", new BasicDBObject("$ne", value.toString()));
|
||||
|
||||
DBObject _id = getAsDBObject(serialized, "_id");
|
||||
ObjectId $ne = getTypedValue(_id, "$ne", ObjectId.class);
|
||||
assertThat($ne, is(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-969
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertCollectionOfObjectIdEvenWhenNestedInOperatorDbObject() {
|
||||
|
||||
ObjectId firstId = new ObjectId("53bb9fd14438765b29c2d56e");
|
||||
ObjectId secondId = new ObjectId("53bb9fda4438765b29c2d56f");
|
||||
|
||||
BasicDBList objectIds = new BasicDBList();
|
||||
objectIds.add(firstId.toString());
|
||||
objectIds.add(secondId.toString());
|
||||
|
||||
DBObject serialized = serializer.asDBObject("_id", new BasicDBObject("$in", objectIds));
|
||||
|
||||
DBObject _id = getAsDBObject(serialized, "_id");
|
||||
Object[] $in = getTypedValue(_id, "$in", Object[].class);
|
||||
|
||||
assertThat($in, Matchers.<Object> arrayContaining(firstId, secondId));
|
||||
}
|
||||
|
||||
class Address {
|
||||
String id;
|
||||
String street;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.test.util;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class CleanMongoDB implements TestRule {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CleanMongoDB.class);
|
||||
|
||||
public enum Types {
|
||||
DATABASE, COLLECTION, INDEX;
|
||||
}
|
||||
|
||||
private Set<String> preserveDatabases = new HashSet<String>() {
|
||||
|
||||
private static final long serialVersionUID = -8698807376808700046L;
|
||||
|
||||
{
|
||||
add("admin");
|
||||
add("local");
|
||||
}
|
||||
};
|
||||
|
||||
private Set<String> dbNames = new HashSet<String>();
|
||||
private Set<String> collectionNames = new HashSet<String>();
|
||||
private Set<Types> types = new HashSet<CleanMongoDB.Types>();
|
||||
private MongoClient client;
|
||||
|
||||
public CleanMongoDB() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public CleanMongoDB(String host, int port) throws UnknownHostException {
|
||||
this(new MongoClient(host, port));
|
||||
}
|
||||
|
||||
public CleanMongoDB(MongoClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public static CleanMongoDB everything() {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Types.DATABASE);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
public static CleanMongoDB databases(String... dbNames) {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Types.DATABASE);
|
||||
cleanMongoDB.collectionNames.addAll(Arrays.asList(dbNames));
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
public static CleanMongoDB indexes() {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Types.INDEX);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
public CleanMongoDB clean(Types... types) {
|
||||
|
||||
this.types.addAll(Arrays.asList(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Statement apply() {
|
||||
return apply(null, null);
|
||||
}
|
||||
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new MongoCleanStatement(base);
|
||||
}
|
||||
|
||||
private class MongoCleanStatement extends Statement {
|
||||
|
||||
private final Statement base;
|
||||
|
||||
public MongoCleanStatement(Statement base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
|
||||
if (base != null) {
|
||||
base.evaluate();
|
||||
}
|
||||
|
||||
boolean isInternal = false;
|
||||
if (client == null) {
|
||||
client = new MongoClient();
|
||||
isInternal = true;
|
||||
}
|
||||
|
||||
Collection<String> dbNamesToUse = dbNames;
|
||||
if (dbNamesToUse.isEmpty()) {
|
||||
dbNamesToUse = client.getDatabaseNames();
|
||||
}
|
||||
|
||||
for (String dbName : dbNamesToUse) {
|
||||
|
||||
if (preserveDatabases.contains(dbName.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (types.contains(Types.DATABASE)) {
|
||||
client.dropDatabase(dbName);
|
||||
LOGGER.debug("Dropping DB '{}'. ", dbName);
|
||||
}
|
||||
|
||||
if (types.contains(Types.COLLECTION)) {
|
||||
|
||||
DB db = client.getDB(dbName);
|
||||
Collection<String> collectionsToUse = initCollectionNames(db);
|
||||
for (String collectionName : collectionsToUse) {
|
||||
if (db.collectionExists(collectionName)) {
|
||||
db.getCollectionFromString(collectionName).drop();
|
||||
LOGGER.debug("Dropping collection '{}' for DB '{}'. ", collectionName, dbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (types.contains(Types.INDEX)) {
|
||||
|
||||
DB db = client.getDB(dbName);
|
||||
Collection<String> collectionsToUse = initCollectionNames(db);
|
||||
for (String collectionName : collectionsToUse) {
|
||||
if (db.collectionExists(collectionName)) {
|
||||
db.getCollectionFromString(collectionName).dropIndexes();
|
||||
LOGGER.debug("Dropping indexes in collection '{}' for DB '{}'. ", collectionName, dbName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isInternal) {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> initCollectionNames(DB db) {
|
||||
|
||||
Collection<String> collectionsToUse = collectionNames;
|
||||
if (CollectionUtils.isEmpty(collectionsToUse)) {
|
||||
collectionsToUse = db.getCollectionNames();
|
||||
}
|
||||
return collectionsToUse;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.test.util;
|
||||
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
import org.springframework.data.mongodb.test.util.CleanMongoDB.Types;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class CleanMongoDBJunitRunListener extends RunListener {
|
||||
|
||||
@Override
|
||||
public void testRunFinished(Result result) throws Exception {
|
||||
super.testRunFinished(result);
|
||||
try {
|
||||
new CleanMongoDB().clean(Types.INDEX).apply().evaluate();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.test.util;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.test.util.CleanMongoDB.Types;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CleanMongoDBTests {
|
||||
|
||||
private CleanMongoDB cleaner;
|
||||
private @Mock Statement baseStatementMock;
|
||||
private @Mock Description descriptionMock;
|
||||
private @Mock MongoClient mongoClientMock;
|
||||
private @Mock DB db1mock;
|
||||
private @Mock DB db2mock;
|
||||
private @Mock DBCollection collection1mock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
when(mongoClientMock.getDatabaseNames()).thenReturn(Arrays.asList("admin", "db1", "db2"));
|
||||
when(mongoClientMock.getDB(eq("db1"))).thenReturn(db1mock);
|
||||
when(mongoClientMock.getDB(eq("db2"))).thenReturn(db2mock);
|
||||
when(db1mock.collectionExists(anyString())).thenReturn(true);
|
||||
when(db2mock.collectionExists(anyString())).thenReturn(true);
|
||||
when(db1mock.getCollectionNames()).thenReturn(Collections.singleton("collection-1"));
|
||||
when(db2mock.getCollectionNames()).thenReturn(Collections.<String> emptySet());
|
||||
when(db1mock.getCollectionFromString(eq("collection-1"))).thenReturn(collection1mock);
|
||||
|
||||
cleaner = new CleanMongoDB(mongoClientMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preservesSystemCollectionsCorrectly() throws Throwable {
|
||||
|
||||
cleaner.clean(Types.DATABASE);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, times(1)).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, times(1)).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesCollectionsCorrectly() throws Throwable {
|
||||
|
||||
cleaner.clean(Types.COLLECTION);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
|
||||
verify(collection1mock, times(1)).drop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesIndexesCorrectly() throws Throwable {
|
||||
|
||||
cleaner.clean(Types.INDEX);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
|
||||
verify(collection1mock, times(1)).dropIndexes();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ Export-Template:
|
||||
org.springframework.data.mongodb.*;version="${project.version}"
|
||||
Import-Template:
|
||||
com.google.common.base.*;version="[11.0.0,14.0.0)";resolution:=optional,
|
||||
com.mongodb.*;version="${mongo:[=.=.=,+1.0.0)}",
|
||||
com.mongodb.*;version="${mongo.osgi:[=.=.=,+1.0.0)}",
|
||||
com.mysema.query.*;version="[2.1.1, 3.0.0)";resolution:=optional,
|
||||
javax.annotation.processing.*;version="0",
|
||||
javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional,
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<xi:include href="introduction/introduction.xml"/>
|
||||
<xi:include href="introduction/requirements.xml"/>
|
||||
<xi:include href="introduction/getting-started.xml"/>
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.0.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.2.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repositories.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
@@ -88,10 +88,10 @@
|
||||
<part id="appendix">
|
||||
<title>Appendix</title>
|
||||
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.0.RELEASE/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.2.RELEASE/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repository-namespace-reference.xml" />
|
||||
</xi:include>
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.0.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.8.2.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repository-query-keywords-reference.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
|
||||
@@ -18,20 +18,19 @@
|
||||
<section id="get-started:help:community">
|
||||
<title>Community Forum</title>
|
||||
|
||||
<para>The Spring Data <ulink
|
||||
url="http://forum.spring.io/forum/spring-projects/data">forum
|
||||
</ulink> is a message board for all Spring Data (not just Document)
|
||||
users to share information and help each other. Note that registration
|
||||
is needed <emphasis>only</emphasis> for posting.</para>
|
||||
<para>Spring Data on Stackoverflow <ulink
|
||||
url="http://stackoverflow.com/questions/tagged/spring-data">Stackoverflow
|
||||
</ulink> is a tag for all Spring Data (not just Document) users to share
|
||||
information and help each other. Note that registration is needed
|
||||
<emphasis>only</emphasis> for posting.</para>
|
||||
</section>
|
||||
|
||||
<section id="get-started:help:professional">
|
||||
<title>Professional Support</title>
|
||||
|
||||
<para>Professional, from-the-source support, with guaranteed response
|
||||
time, is available from <ulink
|
||||
url="http://gopivotal.com/">Pivotal Sofware, Inc.</ulink>, the company
|
||||
behind Spring Data and Spring.</para>
|
||||
time, is available from <ulink url="http://gopivotal.com/">Pivotal
|
||||
Sofware, Inc.</ulink>, the company behind Spring Data and Spring.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -44,22 +43,21 @@
|
||||
homepage</ulink>.</para>
|
||||
|
||||
<para>You can help make Spring Data best serve the needs of the Spring
|
||||
community by interacting with developers through the Spring Community
|
||||
<ulink url="http://forum.spring.io/">forums</ulink>. To follow
|
||||
developer activity look for the mailing list information on the Spring
|
||||
Data Mongo homepage.</para>
|
||||
community by interacting with developers through the Community on <ulink
|
||||
url="http://stackoverflow.com/questions/tagged/spring-data">Stackoverflow</ulink>.
|
||||
To follow developer activity look for the mailing list information on the
|
||||
Spring Data Mongo homepage.</para>
|
||||
|
||||
<para>If you encounter a bug or want to suggest an improvement, please
|
||||
create a ticket on the Spring Data issue <ulink
|
||||
url="https://jira.springframework.org/browse/DATAMONGO">tracker</ulink>.</para>
|
||||
url="https://jira.spring.io/browse/DATAMONGO">tracker</ulink>.</para>
|
||||
|
||||
<para>To stay up to date with the latest news and announcements in the
|
||||
Spring eco system, subscribe to the Spring Community <ulink
|
||||
url="https://spring.io">Portal</ulink>.</para>
|
||||
|
||||
<para>Lastly, you can follow the SpringSource Data <ulink
|
||||
url="https://spring.io/blog">blog </ulink>or
|
||||
the project team on Twitter (<ulink
|
||||
url="http://twitter.com/SpringData">SpringData</ulink>)</para>
|
||||
url="https://spring.io/blog">blog </ulink>or the project team on Twitter
|
||||
(<ulink url="http://twitter.com/SpringData">SpringData</ulink>).</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -1,6 +1,130 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.5.4.RELEASE (2014-08-27)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1038 - Assert Mongo instances cleand up properly after test runs.
|
||||
* DATAMONGO-1034 - Improve error message when trying to convert incompatible types.
|
||||
* DATAMONGO-1033 - Release 1.5.4.
|
||||
* DATAMONGO-1030 - Query methods retuning a single entity does not work with projecting types.
|
||||
* DATAMONGO-1027 - Collection inherits complex index from embedded class/object.
|
||||
* DATAMONGO-1025 - Duplicate index creation on embedded documents.
|
||||
* DATAMONGO-1020 - LimitOperator should be a public class.
|
||||
* DATAMONGO-1008 - IndexOperations fail, when "2dsphere" index is present.
|
||||
|
||||
|
||||
Changes in version 1.6.0.RC1 (2014-08-13)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-1024 - Upgrade to Java driver 2.12.3.
|
||||
* DATAMONGO-1021 - Release 1.6 RC1.
|
||||
* DATAMONGO-1020 - LimitOperator should be a public class.
|
||||
* DATAMONGO-1019 - Correct examples in reference documentation.
|
||||
* DATAMONGO-1017 - Add support for custom implementations in CDI repositories.
|
||||
* DATAMONGO-1016 - Remove deprecations in geospatial area.
|
||||
* DATAMONGO-1015 - Move to Asciidoctor for reference documentation.
|
||||
* DATAMONGO-1012 - Proxies for lazy DBRefs with field access should have their id values resolved eagerly.
|
||||
* DATAMONGO-1009 - Adapt to new multi-store configuration detection.
|
||||
* DATAMONGO-1008 - IndexOperations fail, when "2dsphere" index is present.
|
||||
* DATAMONGO-1005 - Improve cycle-detection for DbRef's.
|
||||
* DATAMONGO-1002 - Update.toString(…) might throw exception.
|
||||
* DATAMONGO-1001 - Can't save/update lazy load object.
|
||||
* DATAMONGO-999 - Multiple Mongo Instances always have the same MongoOption Reference - MongoOptionsFactoryBean has a static instance of MongoOptions.
|
||||
* DATAMONGO-996 - Pagination broken after introduction of the support for top/first.
|
||||
* DATAMONGO-995 - Parameter binding in String-based query does not bind all parameters.
|
||||
* DATAMONGO-993 - The system variables $$CURRENT and $$ROOT not handled correctly.
|
||||
* DATAMONGO-992 - Entity can't be deserialized if @TypeAlias is used.
|
||||
* DATAMONGO-991 - Adapt to deprecation removals in Spring Data Commons.
|
||||
* DATAMONGO-989 - MatchOperation should accept CriteriaDefinition.
|
||||
* DATAMONGO-987 - Problem with lazy loading in @DBRef when getting data using MongoTemplate.
|
||||
* DATAMONGO-974 - synthetic field target's name is returned instead of the alias name.
|
||||
* DATAMONGO-973 - Add support for deriving full text queries.
|
||||
* DATAMONGO-957 - Add support for query modifiers.
|
||||
* DATAMONGO-420 - Extra quotes being added to @Query values and fields.
|
||||
|
||||
|
||||
Changes in version 1.5.2.RELEASE (2014-07-28)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1007 - Release 1.5.2.
|
||||
* DATAMONGO-1002 - Update.toString(…) might throw exception.
|
||||
* DATAMONGO-1001 - Can't save/update lazy load object.
|
||||
* DATAMONGO-999 - Multiple Mongo Instances always have the same MongoOption Reference - MongoOptionsFactoryBean has a static instance of MongoOptions.
|
||||
* DATAMONGO-995 - Parameter binding in String-based query does not bind all parameters.
|
||||
* DATAMONGO-992 - Entity can't be deserialized if @TypeAlias is used.
|
||||
* DATAMONGO-989 - MatchOperation should accept CriteriaDefinition.
|
||||
* DATAMONGO-987 - Problem with lazy loading in @DBRef when getting data using MongoTemplate.
|
||||
* DATAMONGO-983 - Remove links to forum.spring.io.
|
||||
* DATAMONGO-982 - Assure compatibility with upcoming MongoDB driver versions.
|
||||
* DATAMONGO-978 - deleteBy/removeBy repository methods don't set type information in Before/AfterDeleteEvent.
|
||||
* DATAMONGO-972 - References are not handled properly in Querydsl integration.
|
||||
* DATAMONGO-969 - String @id field is not mapped to ObjectId when using QueryDSL .id.in(Collection<String>).
|
||||
* DATAMONGO-420 - Extra quotes being added to @Query values and fields.
|
||||
|
||||
|
||||
Changes in version 1.6.0.M1 (2014-07-10)
|
||||
----------------------------------------
|
||||
* DATAMONGO-983 - Remove links to forum.spring.io.
|
||||
* DATAMONGO-982 - Assure compatibility with upcoming MongoDB driver versions.
|
||||
* DATAMONGO-981 - Release 1.6 M1.
|
||||
* DATAMONGO-980 - Use meta annotations from spring data commons for @Score.
|
||||
* DATAMONGO-978 - deleteBy/removeBy repository methods don't set type information in Before/AfterDeleteEvent.
|
||||
* DATAMONGO-977 - Adapt to Spring 4 upgrade.
|
||||
* DATAMONGO-976 - Add support for reading $meta projection on textScore into document.
|
||||
* DATAMONGO-975 - Add support for date/time operators in aggregation framework.
|
||||
* DATAMONGO-973 - Add support for deriving full text queries.
|
||||
* DATAMONGO-972 - References are not handled properly in Querydsl integration.
|
||||
* DATAMONGO-970 - Id query cannot be created if object to remove is DBObject.
|
||||
* DATAMONGO-969 - String @id field is not mapped to ObjectId when using QueryDSL .id.in(Collection<String>).
|
||||
* DATAMONGO-968 - Add support for $meta projections and sorting for textScore metadata.
|
||||
* DATAMONGO-963 - Compound index with expireAfterSeconds causes repeating error on mongodb server.
|
||||
* DATAMONGO-962 - “Cycle found” with Spring Data Mongo 1.5.
|
||||
* DATAMONGO-960 - Allow to pass options to the Aggregation Pipeline.
|
||||
* DATAMONGO-958 - Move to FieldNamingStrategy SPI in Spring Data Commons.
|
||||
* DATAMONGO-954 - Add support for System Variables in Aggregations.
|
||||
* DATAMONGO-953 - Update object should have a proper equals/hashcode/toString.
|
||||
* DATAMONGO-952 - @Query annotation does not work with only field restrictions.
|
||||
* DATAMONGO-950 - Add support for limiting the query result in the query derivation mechanism.
|
||||
* DATAMONGO-949 - CyclicPropertyReferenceException in versions 1.5.0 + for MongoDB.
|
||||
* DATAMONGO-948 - Assertion error in MongoTemplate.getMappedSortObject.
|
||||
* DATAMONGO-944 - Add support $currentDate to Update.
|
||||
* DATAMONGO-938 - Exception when creating geo within Criteria using MapReduce.
|
||||
* DATAMONGO-937 - Add support for creating text index.
|
||||
* DATAMONGO-850 - Add support for text search using $text.
|
||||
* DATAMONGO-745 - @Query($in) and Pageable in result Page total = 0.
|
||||
|
||||
|
||||
Changes in version 1.5.1.RELEASE (2014-06-30)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-971 - Release 1.5.1.
|
||||
* DATAMONGO-970 - Id query cannot be created if object to remove is DBObject.
|
||||
* DATAMONGO-963 - Compound index with expireAfterSeconds causes repeating error on mongodb server.
|
||||
* DATAMONGO-962 - “Cycle found” with Spring Data Mongo 1.5.
|
||||
* DATAMONGO-953 - Update object should have a proper equals/hashcode/toString.
|
||||
* DATAMONGO-952 - @Query annotation does not work with only field restrictions.
|
||||
* DATAMONGO-949 - CyclicPropertyReferenceException in versions 1.5.0 + for MongoDB.
|
||||
* DATAMONGO-948 - Assertion error in MongoTemplate.getMappedSortObject.
|
||||
* DATAMONGO-938 - Exception when creating geo within Criteria using MapReduce.
|
||||
* DATAMONGO-745 - @Query($in) and Pageable in result Page total = 0.
|
||||
|
||||
|
||||
Changes in version 1.4.3.RELEASE (2014-06-18)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-955 - Release 1.4.3.
|
||||
* DATAMONGO-953 - Update object should have a proper equals/hashcode/toString.
|
||||
* DATAMONGO-952 - @Query annotation does not work with only field restrictions.
|
||||
* DATAMONGO-948 - Assertion error in MongoTemplate.getMappedSortObject.
|
||||
* DATAMONGO-938 - Exception when creating geo within Criteria using MapReduce.
|
||||
* DATAMONGO-924 - Aggregation not working with as() method in project() pipeline operator.
|
||||
* DATAMONGO-920 - Fix debug messages for delete events in AbstractMongoEventListener.
|
||||
* DATAMONGO-917 - DefaultDbRefResolver throws NPE when bundled into an uberjar.
|
||||
* DATAMONGO-914 - Improve resolving of LazyLoading proxies for classes that override equals/hashcode.
|
||||
* DATAMONGO-913 - Can't query using lazy DBRef objects.
|
||||
* DATAMONGO-912 - Aggregation#project followed by Aggregation#match with custom converter causes IllegalArgumentException.
|
||||
* DATAMONGO-898 - MapReduce seems not to work when javascript not being escaped.
|
||||
* DATAMONGO-847 - Allow usage of Criteria within Update.
|
||||
* DATAMONGO-745 - @Query($in) and Pageable in result Page total = 0.
|
||||
* DATAMONGO-647 - Using "OrderBy" in "query by method name" ignores the @Field annotation for field alias.
|
||||
|
||||
|
||||
Changes in version 1.5.0.RELEASE (2014-05-20)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-936 - Release 1.5 GA.
|
||||
@@ -361,7 +485,7 @@ Changes in version 1.2.2.GA (2013-07-19)
|
||||
Changes in version 1.3.0.M1 (2013-06-04)
|
||||
----------------------------------------
|
||||
** Bug
|
||||
* [DATAMONGO-571] - Spring Data for MongoDb doesn't save null values when @Version is added to domain class
|
||||
* [DATAMONGO-571] - Spring Data for MongoDb doesn't save null values when @Version is added to domain class
|
||||
* [DATAMONGO-612] - Fix PDF reference documentation name
|
||||
* [DATAMONGO-613] - Images missing from reference documentation
|
||||
* [DATAMONGO-617] - NullPointerException in MongoTemplate.initializeVersionProperty(…)
|
||||
@@ -397,7 +521,7 @@ Changes in version 1.3.0.M1 (2013-06-04)
|
||||
* [DATAMONGO-637] - Typo in Query.query(…)
|
||||
* [DATAMONGO-651] - WriteResult not available from thrown Exception
|
||||
* [DATAMONGO-652] - Add support for elemMatch and positional operator projections
|
||||
* [DATAMONGO-656] - Potential NullPointerException when debugging in MongoTemplate
|
||||
* [DATAMONGO-656] - Potential NullPointerException when debugging in MongoTemplate
|
||||
* [DATAMONGO-657] - Allow to write Map value as DBRef
|
||||
* [DATAMONGO-666] - Fix architecture inconsistency created by MongoDataIntegrityViolationException
|
||||
* [DATAMONGO-680] - SimpleMongoRepository.exists(ID) improvement
|
||||
@@ -419,7 +543,7 @@ Changes in version 1.3.0.M1 (2013-06-04)
|
||||
Changes in version 1.2.1.GA (2013-04-17)
|
||||
----------------------------------------
|
||||
** Bug
|
||||
* [DATAMONGO-571] - Spring Data for MongoDb doesn't save null values when @Version is added to domain class
|
||||
* [DATAMONGO-571] - Spring Data for MongoDb doesn't save null values when @Version is added to domain class
|
||||
* [DATAMONGO-612] - Fix PDF reference documentation name
|
||||
* [DATAMONGO-613] - Images missing from reference documentation
|
||||
* [DATAMONGO-617] - NullPointerException in MongoTemplate.initializeVersionProperty(…)
|
||||
@@ -516,7 +640,7 @@ Changes in version 1.1.1.GA (2012-10-17)
|
||||
Changes in version 1.1.0.GA (2012-10-10)
|
||||
----------------------------------------
|
||||
** Bug
|
||||
* [DATAMONGO-523] - @TypeAlias annotation not used with AbstractMongoConfiguration
|
||||
* [DATAMONGO-523] - @TypeAlias annotation not used with AbstractMongoConfiguration
|
||||
* [DATAMONGO-527] - Criteria.equals(…) broken for complex criterias
|
||||
* [DATAMONGO-530] - MongoMappingContext.setApplicationContext(…) does not invoke superclass method
|
||||
* [DATAMONGO-531] - StackOverflowError when persisting Groovy beans
|
||||
@@ -593,16 +717,16 @@ Changes in version 1.1.0.M2 (2012-24-07)
|
||||
* [DATAMONGO-446] - Pageable query methods returning List are broken
|
||||
* [DATAMONGO-447] - Removal of Documents fails in in debug mode for Documents with complex ids
|
||||
* [DATAMONGO-450] - enabling DEBUG causes RuntimeException
|
||||
* [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable
|
||||
* [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable
|
||||
* [DATAMONGO-458] - When reading back empty collections unmodifiable instances of Collections.emptyList/Set is returned.
|
||||
* [DATAMONGO-462] - findAll() fails with NPE - discovering the root cause
|
||||
* [DATAMONGO-465] - Mongo inserts document with "_id" as an integer but saves with "_id" as a string.
|
||||
* [DATAMONGO-465] - Mongo inserts document with "_id" as an integer but saves with "_id" as a string.
|
||||
* [DATAMONGO-467] - String @id field is not mapped to ObjectId when using QueryDSL ".id" path
|
||||
* [DATAMONGO-469] - Query creation from method names using AND criteria does not work anymore
|
||||
* [DATAMONGO-474] - Wrong property is used for Id mapping
|
||||
* [DATAMONGO-475] - 'group' operation fails where query references non primitive property
|
||||
* [DATAMONGO-480] - The WriteResultChecking is not used in case of insert or save of documents.
|
||||
* [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB
|
||||
* [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB
|
||||
* [DATAMONGO-489] - ClassCastException when loading Map<String, String[]>
|
||||
|
||||
** Improvement
|
||||
@@ -612,7 +736,7 @@ Changes in version 1.1.0.M2 (2012-24-07)
|
||||
* [DATAMONGO-466] - QueryMapper shouldn't map id properties of nested classes
|
||||
* [DATAMONGO-470] - Criteria and Query should have proper equals(…) and hashCode() method.
|
||||
* [DATAMONGO-477] - Change upper bound of Google Guava package import to 13
|
||||
* [DATAMONGO-482] - typo in documentation - 2 i's in usiing
|
||||
* [DATAMONGO-482] - typo in documentation - 2 i's in usiing
|
||||
* [DATAMONGO-486] - Polish namspace implementation
|
||||
* [DATAMONGO-491] - Release 1.1.0.M2
|
||||
|
||||
@@ -632,13 +756,13 @@ Changes in version 1.0.3.RELEASE (2012-24-07)
|
||||
* [DATAMONGO-474] - Wrong property is used for Id mapping
|
||||
* [DATAMONGO-475] - 'group' operation fails where query references non primitive property
|
||||
* [DATAMONGO-480] - The WriteResultChecking is not used in case of insert or save of documents.
|
||||
* [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB
|
||||
* [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB
|
||||
* [DATAMONGO-489] - ClassCastException when loading Map<String, String[]>
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-466] - QueryMapper shouldn't map id properties of nested classes
|
||||
* [DATAMONGO-470] - Criteria and Query should have proper equals(…) and hashCode() method.
|
||||
* [DATAMONGO-482] - typo in documentation - 2 i's in usiing
|
||||
* [DATAMONGO-482] - typo in documentation - 2 i's in usiing
|
||||
|
||||
** Task
|
||||
* [DATAMONGO-492] - Release 1.0.3
|
||||
@@ -662,7 +786,7 @@ Changes in version 1.0.2.RELEASE (2012-06-20)
|
||||
* [DATAMONGO-446] - Pageable query methods returning List are broken
|
||||
* [DATAMONGO-447] - Removal of Documents fails in in debug mode for Documents with complex ids
|
||||
* [DATAMONGO-450] - enabling DEBUG causes RuntimeException
|
||||
* [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable
|
||||
* [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable
|
||||
* [DATAMONGO-461] - MappedConstructor potentially throws NullPointerException
|
||||
* [DATAMONGO-462] - findAll() fails with NPE - discovering the root cause
|
||||
|
||||
@@ -843,7 +967,7 @@ Changes in version 1.0.0.M5 MongoDB (2011-10-24)
|
||||
* [DATAMONGO-282] - Cannot create a "range" query
|
||||
* [DATAMONGO-284] - Execution of Querydsl query maps id incorrectly
|
||||
* [DATAMONGO-285] - NPE in MappingMongoConverter.writeMapInternal when saving a Map<String,Object> with val instance of Collection
|
||||
* [DATAMONGO-288] - querying same property multiple times produces incorrect query
|
||||
* [DATAMONGO-288] - querying same property multiple times produces incorrect query
|
||||
* [DATAMONGO-289] - AbstractMongoEventListener will never call onAfterLoad
|
||||
* [DATAMONGO-294] - List elements nested in Map lose their type when persisted
|
||||
|
||||
@@ -872,7 +996,7 @@ Changes in version 1.0.0.M5 MongoDB (2011-10-24)
|
||||
* [DATAMONGO-274] - Split up repository package according to the structure in Spring Data JPA
|
||||
|
||||
** Task
|
||||
* [DATAMONGO-264] - Ensure Data Document examples work
|
||||
* [DATAMONGO-264] - Ensure Data Document examples work
|
||||
* [DATAMONGO-265] - Create new github repository for mongodb
|
||||
* [DATAMONGO-266] - Create new github repository for CouchDB
|
||||
* [DATAMONGO-297] - Prune project directory
|
||||
@@ -905,7 +1029,7 @@ Changes in version 1.0.0.M4 MongoDB (2011-09-01)
|
||||
* [DATADOC-228] - NullPointerException when persiting Map with null values
|
||||
* [DATADOC-229] - When a parameterized List is used in the PersistentConstructor, conversion fail
|
||||
* [DATADOC-231] - spring-data-mongodb does not work in an OSGi server because of unresolved dependencies
|
||||
* [DATADOC-232] - mongodb allow to $inc many fields in one query, but Updat().inc(firs).inc(last) do only last inc
|
||||
* [DATADOC-232] - mongodb allow to $inc many fields in one query, but Updat().inc(firs).inc(last) do only last inc
|
||||
* [DATADOC-235] - Unable to map unstructured data
|
||||
* [DATADOC-236] - Repository queries do not honour order defined in method name
|
||||
* [DATADOC-237] - @Indexed annotation doesn't honor field name from @Field annotation
|
||||
@@ -919,7 +1043,7 @@ Changes in version 1.0.0.M4 MongoDB (2011-09-01)
|
||||
|
||||
** Improvement
|
||||
* [DATADOC-32] - SimpleMongoConverter could support identifying Spring EL expressions in keys
|
||||
* [DATADOC-63] - Converters to support use of a 'typeId' strategy to determine class to marshall/unmarshal from Mongo
|
||||
* [DATADOC-63] - Converters to support use of a 'typeId' strategy to determine class to marshall/unmarshal from Mongo
|
||||
* [DATADOC-166] - Check for null if various template CRUD methods
|
||||
* [DATADOC-169] - Registering custom converters for a type requires treating the type as simple in mapping context
|
||||
* [DATADOC-171] - IllegalArgumentException when persisting entity with BigDecimal field
|
||||
@@ -1002,7 +1126,7 @@ Querying / Updating
|
||||
* [DATADOC-146] - Advanced Regexp Queries
|
||||
|
||||
Mapping
|
||||
* [DATADOC-95] - Can not save an object that has not had any of its properties set
|
||||
* [DATADOC-95] - Can not save an object that has not had any of its properties set
|
||||
* [DATADOC-97] - ID replacement not working correctly when using updateFirst/updateMulti
|
||||
* [DATADOC-98] - Collection<Collection> or Object[][] doesn't save correctly
|
||||
* [DATADOC-109] - Add MappingContext to MongoConverter interface
|
||||
@@ -1053,10 +1177,10 @@ Mapping
|
||||
* [DATADOC-33] - Introduce annotation to demarcate id field in a domain object
|
||||
|
||||
Repository
|
||||
* [DATADOC-47, DATACMNS-17] - Adapted new metamodel API
|
||||
* [DATADOC-47, DATACMNS-17] - Adapted new metamodel API
|
||||
* [DATADOC-46] - Added support for 'In' and 'NotIn' keyword
|
||||
* [DATADOC-49] - Fixed 'And' and 'Or' keywords
|
||||
* [DATADOC-41] - Added support for executing QueryDsl predicates
|
||||
* [DATADOC-41] - Added support for executing QueryDsl predicates
|
||||
* [DATADOC-69] - Let repository namespace pickup the default mapping context bean and allow configuration
|
||||
* [DATADOC-24] - Allow use of @Query annotation to define queries
|
||||
* [DATADOC-34] - Create indexes for columns that are mentioned in query methods
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.5 GA
|
||||
Spring Data MongoDB 1.5.4
|
||||
Copyright (c) [2010-2014] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
Reference in New Issue
Block a user