Compare commits
24 Commits
1.4.0.RC1
...
1.4.0.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
294616432d | ||
|
|
47dd512f95 | ||
|
|
f16e8d85e5 | ||
|
|
eb03ae61f2 | ||
|
|
5be66a3fee | ||
|
|
d88e4c0e3e | ||
|
|
57d1449008 | ||
|
|
8d00a0d926 | ||
|
|
e3fa844488 | ||
|
|
58bee75a6b | ||
|
|
a402395f5c | ||
|
|
9d5f8f3ba0 | ||
|
|
7ebf953063 | ||
|
|
617ebe0ca7 | ||
|
|
7f76789664 | ||
|
|
81e5919ace | ||
|
|
efd74956dc | ||
|
|
49eee40f7e | ||
|
|
8e93b844c7 | ||
|
|
3e64432f1a | ||
|
|
88c968ad36 | ||
|
|
99eefe0773 | ||
|
|
3d4569be14 | ||
|
|
57455c4a26 |
30
README.md
30
README.md
@@ -1,6 +1,6 @@
|
||||
# Spring Data MongoDB
|
||||
|
||||
The primary goal of the [Spring Data](http://www.springsource.org/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
The primary goal of the [Spring Data](http://projects.spring.io/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
|
||||
The Spring Data MongoDB project aims to provide a familiar and consistent Spring-based programming model for new datastores while retaining store-specific features and capabilities. The Spring Data MongoDB project provides integration with the MongoDB document database. Key functional areas of Spring Data MongoDB are a POJO centric model for interacting with a MongoDB DBCollection and easily writing a repository style data access layer.
|
||||
|
||||
@@ -8,12 +8,12 @@ The Spring Data MongoDB project aims to provide a familiar and consistent Spring
|
||||
|
||||
For a comprehensive treatment of all the Spring Data MongoDB features, please refer to:
|
||||
|
||||
* the [User Guide](http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/)
|
||||
* the [JavaDocs](http://static.springsource.org/spring-data/data-mongodb/docs/current/api/) have extensive comments in them as well.
|
||||
* the home page of [Spring Data MongoDB](http://www.springsource.org/spring-data/mongodb) contains links to articles and other resources.
|
||||
* for more detailed questions, use the [forum](http://forum.springsource.org/forumdisplay.php?f=80).
|
||||
* 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).
|
||||
|
||||
If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://www.springsource.org/projects).
|
||||
If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://projects.spring.io/).
|
||||
|
||||
|
||||
## Quick Start
|
||||
@@ -26,7 +26,7 @@ Add the Maven dependency:
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.3.3.RELEASE</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -36,13 +36,13 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.5.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>http://repo.springsource.org/libs-snapshot</url>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ MongoTemplate is the central support class for Mongo database operations. It pro
|
||||
* Basic POJO mapping support to and from BSON
|
||||
* Convenience methods to interact with the store (insert object, update objects) and MongoDB specific ones (geo-spatial operations, upserts, map-reduce etc.)
|
||||
* Connection affinity callback
|
||||
* Exception translation into Spring's [technology agnostic DAO exception hierarchy](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/dao.html#dao-exceptions).
|
||||
* Exception translation into Spring's [technology agnostic DAO exception hierarchy](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions).
|
||||
|
||||
### Spring Data repositories
|
||||
|
||||
@@ -81,7 +81,7 @@ class ApplicationConfig extends AbstractMongoConfiguration {
|
||||
|
||||
@Override
|
||||
public Mongo mongo() throws Exception {
|
||||
return new Mongo();
|
||||
return new MongoClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,9 +94,9 @@ class ApplicationConfig extends AbstractMongoConfiguration {
|
||||
This sets up a connection to a local MongoDB instance and enables the detection of Spring Data repositories (through `@EnableMongoRepositories`). The same configuration would look like this in XML:
|
||||
|
||||
```xml
|
||||
<bean id="template" class="org.springframework.data.document.mongodb.MongoTemplate">
|
||||
<bean id="template" class="org.springframework.data.mongodb.core.MongoTemplate">
|
||||
<constructor-arg>
|
||||
<bean class="com.mongodb.Mongo">
|
||||
<bean class="com.mongodb.MongoClient">
|
||||
<constructor-arg value="localhost" />
|
||||
<constructor-arg value="27017" />
|
||||
</bean>
|
||||
@@ -139,9 +139,9 @@ 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.springsource.org/forumdisplay.php?f=80) by responding to questions and joining the debate.
|
||||
* 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.
|
||||
* 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://www.springsource.org/node/feed) to springframework.org
|
||||
* Watch for upcoming articles on Spring by [subscribing](http://spring.io/blog) to spring.io.
|
||||
|
||||
Before we accept a non-trivial patch or pull request we will need you to sign the [contributor's agreement](https://support.springsource.com/spring_committer_signup). Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests.
|
||||
|
||||
28
pom.xml
28
pom.xml
@@ -5,17 +5,17 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.0.RC1</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
<description>MongoDB support for Spring Data</description>
|
||||
<url>http://www.springsource.org/spring-data/mongodb</url>
|
||||
<url>http://projects.spring.io/spring-data-mongodb</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.3.0.RC1</version>
|
||||
<version>1.3.0.RELEASE</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.7.0.RC1</springdata.commons>
|
||||
<springdata.commons>1.7.0.RELEASE</springdata.commons>
|
||||
<mongo>2.11.4</mongo>
|
||||
<mongo-osgi>${mongo}</mongo-osgi>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
@@ -102,6 +103,16 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.12.0-rc0</mongo>
|
||||
<mongo-osgi>2.12.0</mongo-osgi>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<!-- MongoDB -->
|
||||
<dependency>
|
||||
@@ -113,13 +124,8 @@
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>http://repo.springsource.org/libs-milestone-local</url>
|
||||
<id>spring-libs-release</id>
|
||||
<url>http://repo.spring.io/libs-release/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.0.RC1</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.0.RC1</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
Bundle-SymbolicName: org.springframework.data.mongodb.crossstore
|
||||
Bundle-Name: Spring Data MongoDB Cross Store Support
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-Vendor: Pivotal Software, Inc.
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
sun.reflect;version="0";resolution:=optional
|
||||
Export-Template:
|
||||
org.springframework.data.mongodb.crossstore.*;version="${project.version}"
|
||||
Import-Template:
|
||||
com.mongodb.*;version="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.4.0.RC1</version>
|
||||
<version>1.4.0.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.4.0.RC1</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Bundle-SymbolicName: org.springframework.data.mongodb.log4j
|
||||
Bundle-Name: Spring Data Mongo DB Log4J Appender
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-Vendor: Pivotal Software, Inc.
|
||||
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)}"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.0.RC1</version>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>apt-maven-plugin</artifactId>
|
||||
<version>1.0.8</version>
|
||||
<version>${apt}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
public interface MongoDocumentWriter {
|
||||
|
||||
}
|
||||
@@ -60,6 +60,11 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
|
||||
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")) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof MongoInternalException) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-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.
|
||||
* You may obtain a copy of the License at
|
||||
@@ -468,10 +468,32 @@ public interface MongoOperations {
|
||||
*/
|
||||
<T> T findOne(Query query, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param collectionName name of the collection to check for objects.
|
||||
* @return
|
||||
*/
|
||||
boolean exists(Query query, String collectionName);
|
||||
|
||||
/**
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param entityClass the parameterized type.
|
||||
* @return
|
||||
*/
|
||||
boolean exists(Query query, Class<?> entityClass);
|
||||
|
||||
/**
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param collectionName name of the collection to check for objects.
|
||||
* @return
|
||||
*/
|
||||
boolean exists(Query query, Class<?> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
@@ -529,12 +551,58 @@ public interface MongoOperations {
|
||||
*/
|
||||
<T> T findById(Object id, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param entityClass the parameterized type.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param collectionName the collection to query.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* {@link FindAndModifyOptions} into account.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information.
|
||||
* @param entityClass the parameterized type.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* {@link FindAndModifyOptions} into account.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param collectionName the collection to query.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
|
||||
String collectionName);
|
||||
|
||||
@@ -598,9 +666,9 @@ public interface MongoOperations {
|
||||
* <p/>
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Conversion API.
|
||||
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
|
||||
* Conversion"</a> for more details.
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
|
||||
* >Spring's Type Conversion"</a> for more details.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Insert is used to initially store the object into the database. To update an existing object use the save method.
|
||||
@@ -655,9 +723,9 @@ public interface MongoOperations {
|
||||
* <p/>
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Conversion API.
|
||||
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
|
||||
* Conversion"</a> for more details.
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
|
||||
* >Spring's Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection
|
||||
*/
|
||||
@@ -672,9 +740,9 @@ public interface MongoOperations {
|
||||
* <p/>
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Spring 3.0's new Type Cobnversion API.
|
||||
* See <a href="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">Spring 3 Type
|
||||
* Conversion"</a> for more details.
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Cobnversion API. See <a
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">Spring's
|
||||
* Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection
|
||||
* @param collectionName name of the collection to store the object in
|
||||
@@ -814,6 +882,14 @@ public interface MongoOperations {
|
||||
*/
|
||||
void remove(Query query, Class<?> entityClass);
|
||||
|
||||
/**
|
||||
* Remove all documents that match the provided query document criteria from the the collection used to store the
|
||||
* entityClass. The Class parameter is also used to help convert the Id of the object if it is present in the query.
|
||||
*
|
||||
* @param query
|
||||
* @param entityClass
|
||||
* @param collectionName
|
||||
*/
|
||||
void remove(Query query, Class<?> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 org.springframework.transaction.support.ResourceHolder;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class MongoSynchronization extends ResourceHolderSynchronization<ResourceHolder, Object> {
|
||||
|
||||
public MongoSynchronization(ResourceHolder resourceHolder, Object resourceKey) {
|
||||
|
||||
@@ -1016,7 +1016,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
: collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse);
|
||||
|
||||
if (entity != null && entity.hasVersionProperty() && !multi) {
|
||||
if (writeResult.getN() == 0) {
|
||||
if (writeResult.getN() == 0 && dbObjectContainsVersionProperty(queryObj, entity)) {
|
||||
throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity: "
|
||||
+ updateObj.toMap().toString() + " to collection " + collectionName);
|
||||
}
|
||||
@@ -1031,14 +1031,22 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
private void increaseVersionForUpdateIfNecessary(MongoPersistentEntity<?> persistentEntity, Update update) {
|
||||
|
||||
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
|
||||
|
||||
String versionPropertyField = persistentEntity.getVersionProperty().getFieldName();
|
||||
if (!update.getUpdateObject().containsField(versionPropertyField)) {
|
||||
update.inc(versionPropertyField, 1L);
|
||||
String versionFieldName = persistentEntity.getVersionProperty().getFieldName();
|
||||
if (!update.modifies(versionFieldName)) {
|
||||
update.inc(versionFieldName, 1L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dbObjectContainsVersionProperty(DBObject dbObject, MongoPersistentEntity<?> persistentEntity) {
|
||||
|
||||
if (persistentEntity == null || !persistentEntity.hasVersionProperty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dbObject.containsField(persistentEntity.getVersionProperty().getFieldName());
|
||||
}
|
||||
|
||||
public void remove(Object object) {
|
||||
|
||||
if (object == null) {
|
||||
|
||||
@@ -262,7 +262,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
@Override
|
||||
public ProjectionOperation as(String alias) {
|
||||
|
||||
Field expressionField = Fields.field(alias, "expr");
|
||||
Field expressionField = Fields.field(alias, alias);
|
||||
return this.operation.and(new ExpressionProjection(expressionField, this.value.toString(), params));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -495,7 +495,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
if (currentNode.hasfirstChildNotOfType(Indexer.class)) {
|
||||
// we have a property path expression like: foo.bar -> render as reference
|
||||
return context.getFieldReference().toString();
|
||||
return context.addToPreviousOrReturn(context.getFieldReference().toString());
|
||||
}
|
||||
|
||||
return context.addToPreviousOrReturn(currentNode.getValue());
|
||||
|
||||
@@ -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,13 +17,13 @@ package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -56,6 +56,7 @@ import org.springframework.util.Assert;
|
||||
* .
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class CustomConversions {
|
||||
|
||||
@@ -67,7 +68,7 @@ public class CustomConversions {
|
||||
private final Set<ConvertiblePair> writingPairs;
|
||||
private final Set<Class<?>> customSimpleTypes;
|
||||
private final SimpleTypeHolder simpleTypeHolder;
|
||||
private final Map<Class<?>, HashMap<Class<?>, CacheValue>> cache;
|
||||
private final ConcurrentMap<ConvertiblePair, CacheValue> customReadTargetTypes;
|
||||
|
||||
private final List<Object> converters;
|
||||
|
||||
@@ -90,7 +91,7 @@ public class CustomConversions {
|
||||
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
|
||||
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
|
||||
this.customSimpleTypes = new HashSet<Class<?>>();
|
||||
this.cache = new HashMap<Class<?>, HashMap<Class<?>, CacheValue>>();
|
||||
this.customReadTargetTypes = new ConcurrentHashMap<GenericConverter.ConvertiblePair, CacheValue>();
|
||||
|
||||
this.converters = new ArrayList<Object>();
|
||||
this.converters.addAll(converters);
|
||||
@@ -195,25 +196,25 @@ public class CustomConversions {
|
||||
*
|
||||
* @param pair
|
||||
*/
|
||||
private void register(ConverterRegistration context) {
|
||||
private void register(ConverterRegistration converterRegistration) {
|
||||
|
||||
ConvertiblePair pair = context.getConvertiblePair();
|
||||
ConvertiblePair pair = converterRegistration.getConvertiblePair();
|
||||
|
||||
if (context.isReading()) {
|
||||
if (converterRegistration.isReading()) {
|
||||
|
||||
readingPairs.add(pair);
|
||||
|
||||
if (LOG.isWarnEnabled() && !context.isSimpleSourceType()) {
|
||||
if (LOG.isWarnEnabled() && !converterRegistration.isSimpleSourceType()) {
|
||||
LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
|
||||
}
|
||||
}
|
||||
|
||||
if (context.isWriting()) {
|
||||
if (converterRegistration.isWriting()) {
|
||||
|
||||
writingPairs.add(pair);
|
||||
customSimpleTypes.add(pair.getSourceType());
|
||||
|
||||
if (LOG.isWarnEnabled() && !context.isSimpleTargetType()) {
|
||||
if (LOG.isWarnEnabled() && !converterRegistration.isSimpleTargetType()) {
|
||||
LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
|
||||
}
|
||||
}
|
||||
@@ -223,11 +224,11 @@ public class CustomConversions {
|
||||
* Returns the target type to convert to in case we have a custom conversion registered to convert the given source
|
||||
* type into a Mongo native one.
|
||||
*
|
||||
* @param source must not be {@literal null}
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Class<?> getCustomWriteTarget(Class<?> source) {
|
||||
return getCustomWriteTarget(source, null);
|
||||
public Class<?> getCustomWriteTarget(Class<?> sourceType) {
|
||||
return getCustomWriteTarget(sourceType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,72 +236,78 @@ public class CustomConversions {
|
||||
* oth the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply return the
|
||||
* first target type matching or {@literal null} if no conversion can be found.
|
||||
*
|
||||
* @param source must not be {@literal null}
|
||||
* @param expectedTargetType
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @param requestedTargetType
|
||||
* @return
|
||||
*/
|
||||
public Class<?> getCustomWriteTarget(Class<?> source, Class<?> expectedTargetType) {
|
||||
public Class<?> getCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(source);
|
||||
return getCustomTarget(source, expectedTargetType, writingPairs);
|
||||
Assert.notNull(sourceType);
|
||||
|
||||
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to write into a Mongo native type. The returned type might
|
||||
* be a subclass oth the given expected type though.
|
||||
* be a subclass of the given expected type though.
|
||||
*
|
||||
* @param source must not be {@literal null}
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomWriteTarget(Class<?> source) {
|
||||
return hasCustomWriteTarget(source, null);
|
||||
public boolean hasCustomWriteTarget(Class<?> sourceType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
return hasCustomWriteTarget(sourceType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to write an object of the given source type into an object
|
||||
* of the given Mongo native target type.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param expectedTargetType
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomWriteTarget(Class<?> source, Class<?> expectedTargetType) {
|
||||
return getCustomWriteTarget(source, expectedTargetType) != null;
|
||||
public boolean hasCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
return getCustomWriteTarget(sourceType, requestedTargetType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to read the given source into the given target type.
|
||||
*
|
||||
* @param source must not be {@literal null}
|
||||
* @param expectedTargetType must not be {@literal null}
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @param requestedTargetType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomReadTarget(Class<?> source, Class<?> expectedTargetType) {
|
||||
public boolean hasCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(expectedTargetType);
|
||||
Assert.notNull(sourceType);
|
||||
Assert.notNull(requestedTargetType);
|
||||
|
||||
return getCustomReadTarget(source, expectedTargetType) != null;
|
||||
return getCustomReadTarget(sourceType, requestedTargetType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the given {@link ConvertiblePair} for ones that have a source compatible type as source. Additionally
|
||||
* checks assignabilty of the target type if one is given.
|
||||
* checks assignability of the target type if one is given.
|
||||
*
|
||||
* @param source must not be {@literal null}
|
||||
* @param expectedTargetType
|
||||
* @param pairs must not be {@literal null}
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType can be {@literal null}.
|
||||
* @param pairs must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getCustomTarget(Class<?> source, Class<?> expectedTargetType, Iterable<ConvertiblePair> pairs) {
|
||||
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
|
||||
Iterable<ConvertiblePair> pairs) {
|
||||
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(sourceType);
|
||||
Assert.notNull(pairs);
|
||||
|
||||
for (ConvertiblePair typePair : pairs) {
|
||||
if (typePair.getSourceType().isAssignableFrom(source)) {
|
||||
if (typePair.getSourceType().isAssignableFrom(sourceType)) {
|
||||
Class<?> targetType = typePair.getTargetType();
|
||||
if (expectedTargetType == null || targetType.isAssignableFrom(expectedTargetType)) {
|
||||
if (requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType)) {
|
||||
return targetType;
|
||||
}
|
||||
}
|
||||
@@ -309,27 +316,33 @@ public class CustomConversions {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> getCustomReadTarget(Class<?> source, Class<?> expectedTargetType) {
|
||||
/**
|
||||
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
|
||||
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Class<?> type = expectedTargetType == null ? PlaceholderType.class : expectedTargetType;
|
||||
Assert.notNull(sourceType);
|
||||
|
||||
Map<Class<?>, CacheValue> map;
|
||||
CacheValue toReturn;
|
||||
|
||||
if ((map = cache.get(source)) == null || (toReturn = map.get(type)) == null) {
|
||||
|
||||
Class<?> target = getCustomTarget(source, type, readingPairs);
|
||||
|
||||
if (cache.get(source) == null) {
|
||||
cache.put(source, new HashMap<Class<?>, CacheValue>());
|
||||
}
|
||||
|
||||
Map<Class<?>, CacheValue> value = cache.get(source);
|
||||
toReturn = target == null ? CacheValue.NULL : new CacheValue(target);
|
||||
value.put(type, toReturn);
|
||||
if (requestedTargetType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return toReturn.clazz;
|
||||
ConvertiblePair lookupKey = new ConvertiblePair(sourceType, requestedTargetType);
|
||||
CacheValue readTargetTypeValue = customReadTargetTypes.get(lookupKey);
|
||||
|
||||
if (readTargetTypeValue != null) {
|
||||
return readTargetTypeValue.getType();
|
||||
}
|
||||
|
||||
readTargetTypeValue = CacheValue.of(getCustomTarget(sourceType, requestedTargetType, readingPairs));
|
||||
CacheValue cacheValue = customReadTargetTypes.putIfAbsent(lookupKey, readTargetTypeValue);
|
||||
|
||||
return cacheValue != null ? cacheValue.getType() : readTargetTypeValue.getType();
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
@@ -338,8 +351,10 @@ public class CustomConversions {
|
||||
INSTANCE;
|
||||
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
|
||||
ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class);
|
||||
ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class);
|
||||
|
||||
return new HashSet<ConvertiblePair>(Arrays.asList(localeToString, booleanToString));
|
||||
}
|
||||
|
||||
@@ -348,29 +363,29 @@ public class CustomConversions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder type to allow registering not-found values in the converter cache.
|
||||
*
|
||||
* @author Patryk Wasik
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class PlaceholderType {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to safely store {@literal null} values in the type cache.
|
||||
*
|
||||
* @author Patryk Wasik
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class CacheValue {
|
||||
|
||||
public static final CacheValue NULL = new CacheValue(null);
|
||||
private final Class<?> clazz;
|
||||
private static final CacheValue ABSENT = new CacheValue(null);
|
||||
|
||||
public CacheValue(Class<?> clazz) {
|
||||
this.clazz = clazz;
|
||||
private final Class<?> type;
|
||||
|
||||
public CacheValue(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
static CacheValue of(Class<?> type) {
|
||||
return type == null ? ABSENT : new CacheValue(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ import com.mongodb.DBRef;
|
||||
* @author Jon Brisbin
|
||||
* @author Patrik Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
|
||||
|
||||
@@ -303,7 +304,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef!");
|
||||
}
|
||||
|
||||
return createDBRef(object, annotation);
|
||||
return createDBRef(object, referingProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,7 +447,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
if (prop.isDbReference()) {
|
||||
DBRef dbRefObj = createDBRef(obj, prop.getDBRef());
|
||||
DBRef dbRefObj = createDBRef(obj, prop);
|
||||
if (null != dbRefObj) {
|
||||
accessor.put(prop, dbRefObj);
|
||||
return;
|
||||
@@ -515,7 +516,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
continue;
|
||||
}
|
||||
|
||||
DBRef dbRef = createDBRef(element, property.getDBRef());
|
||||
DBRef dbRef = createDBRef(element, property);
|
||||
dbList.add(dbRef);
|
||||
}
|
||||
|
||||
@@ -548,7 +549,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
if (conversions.isSimpleType(key.getClass())) {
|
||||
|
||||
String simpleKey = potentiallyEscapeMapKey(key.toString());
|
||||
dbObject.put(simpleKey, value != null ? createDBRef(value, property.getDBRef()) : null);
|
||||
dbObject.put(simpleKey, value != null ? createDBRef(value, property) : null);
|
||||
|
||||
} else {
|
||||
throw new MappingException("Cannot use a complex object as a key value.");
|
||||
@@ -741,7 +742,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target);
|
||||
}
|
||||
|
||||
protected DBRef createDBRef(Object target, org.springframework.data.mongodb.core.mapping.DBRef dbref) {
|
||||
protected DBRef createDBRef(Object target, MongoPersistentProperty property) {
|
||||
|
||||
Assert.notNull(target);
|
||||
|
||||
@@ -750,6 +751,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> targetEntity = mappingContext.getPersistentEntity(target.getClass());
|
||||
targetEntity = targetEntity == null ? targetEntity = mappingContext.getPersistentEntity(property) : targetEntity;
|
||||
|
||||
if (null == targetEntity) {
|
||||
throw new MappingException("No mapping metadata found for " + target.getClass());
|
||||
@@ -761,14 +763,21 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
throw new MappingException("No id property found on class " + targetEntity.getType());
|
||||
}
|
||||
|
||||
BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(target, conversionService);
|
||||
Object id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
|
||||
Object id = null;
|
||||
|
||||
if (target.getClass().equals(idProperty.getType())) {
|
||||
id = target;
|
||||
} else {
|
||||
BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(target, conversionService);
|
||||
id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
|
||||
}
|
||||
|
||||
if (null == id) {
|
||||
throw new MappingException("Cannot create a reference to an object with a NULL id.");
|
||||
}
|
||||
|
||||
return dbRefResolver.createDbRef(dbref, targetEntity, idMapper.convertId(id));
|
||||
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), targetEntity,
|
||||
idMapper.convertId(id));
|
||||
}
|
||||
|
||||
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator eval,
|
||||
@@ -902,15 +911,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return getPotentiallyConvertedSimpleWrite(obj);
|
||||
}
|
||||
|
||||
TypeInformation<?> typeHint = typeInformation == null ? null : ClassTypeInformation.OBJECT;
|
||||
|
||||
if (obj instanceof BasicDBList) {
|
||||
return maybeConvertList((BasicDBList) obj);
|
||||
return maybeConvertList((BasicDBList) obj, typeHint);
|
||||
}
|
||||
|
||||
if (obj instanceof DBObject) {
|
||||
DBObject newValueDbo = new BasicDBObject();
|
||||
for (String vk : ((DBObject) obj).keySet()) {
|
||||
Object o = ((DBObject) obj).get(vk);
|
||||
newValueDbo.put(vk, convertToMongoType(o));
|
||||
newValueDbo.put(vk, convertToMongoType(o, typeHint));
|
||||
}
|
||||
return newValueDbo;
|
||||
}
|
||||
@@ -918,17 +929,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
if (obj instanceof Map) {
|
||||
DBObject result = new BasicDBObject();
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
|
||||
result.put(entry.getKey().toString(), convertToMongoType(entry.getValue()));
|
||||
result.put(entry.getKey().toString(), convertToMongoType(entry.getValue(), typeHint));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (obj.getClass().isArray()) {
|
||||
return maybeConvertList(Arrays.asList((Object[]) obj));
|
||||
return maybeConvertList(Arrays.asList((Object[]) obj), typeHint);
|
||||
}
|
||||
|
||||
if (obj instanceof Collection) {
|
||||
return maybeConvertList((Collection<?>) obj);
|
||||
return maybeConvertList((Collection<?>) obj, typeHint);
|
||||
}
|
||||
|
||||
DBObject newDbo = new BasicDBObject();
|
||||
@@ -941,11 +952,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfoRecursively(newDbo);
|
||||
}
|
||||
|
||||
public BasicDBList maybeConvertList(Iterable<?> source) {
|
||||
public BasicDBList maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
|
||||
|
||||
BasicDBList newDbl = new BasicDBList();
|
||||
for (Object element : source) {
|
||||
newDbl.add(convertToMongoType(element));
|
||||
newDbl.add(convertToMongoType(element, typeInformation));
|
||||
}
|
||||
|
||||
return newDbl;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,18 +17,23 @@ package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
|
||||
@@ -47,6 +52,7 @@ import com.mongodb.DBRef;
|
||||
* @author Oliver Gierke
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class QueryMapper {
|
||||
|
||||
@@ -105,21 +111,36 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
Field field = createPropertyField(entity, key, mappingContext);
|
||||
Entry<String, Object> entry = getMappedObjectForField(field, query.get(key));
|
||||
|
||||
Object rawValue = query.get(key);
|
||||
String newKey = field.getMappedKey();
|
||||
|
||||
if (isNestedKeyword(rawValue) && !field.isIdField()) {
|
||||
Keyword keyword = new Keyword((DBObject) rawValue);
|
||||
result.put(newKey, getMappedKeyword(field, keyword));
|
||||
} else {
|
||||
result.put(newKey, getMappedValue(field, rawValue));
|
||||
}
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the mapped object value for given field out of rawValue taking nested {@link Keyword}s into account
|
||||
*
|
||||
* @param field
|
||||
* @param rawValue
|
||||
* @return
|
||||
*/
|
||||
protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) {
|
||||
|
||||
String key = field.getMappedKey();
|
||||
Object value;
|
||||
|
||||
if (isNestedKeyword(rawValue) && !field.isIdField()) {
|
||||
Keyword keyword = new Keyword((DBObject) rawValue);
|
||||
value = getMappedKeyword(field, keyword);
|
||||
} else {
|
||||
value = getMappedValue(field, rawValue);
|
||||
}
|
||||
|
||||
return Collections.singletonMap(key, value).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
* @param key
|
||||
@@ -138,7 +159,7 @@ public class QueryMapper {
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private DBObject getMappedKeyword(Keyword keyword, MongoPersistentEntity<?> entity) {
|
||||
protected DBObject getMappedKeyword(Keyword keyword, MongoPersistentEntity<?> entity) {
|
||||
|
||||
// $or/$nor
|
||||
if (keyword.isOrOrNor() || keyword.hasIterableValue()) {
|
||||
@@ -164,13 +185,13 @@ public class QueryMapper {
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
private DBObject getMappedKeyword(Field property, Keyword keyword) {
|
||||
protected DBObject getMappedKeyword(Field property, Keyword keyword) {
|
||||
|
||||
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
|
||||
Object value = keyword.getValue();
|
||||
|
||||
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property.getProperty())
|
||||
: getMappedValue(property.with(keyword.getKey()), value);
|
||||
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue(
|
||||
property.with(keyword.getKey()), value);
|
||||
|
||||
return new BasicDBObject(keyword.key, convertedValue);
|
||||
}
|
||||
@@ -184,7 +205,7 @@ public class QueryMapper {
|
||||
* @param newKey the key the value will be bound to eventually
|
||||
* @return
|
||||
*/
|
||||
private Object getMappedValue(Field documentField, Object value) {
|
||||
protected Object getMappedValue(Field documentField, Object value) {
|
||||
|
||||
if (documentField.isIdField()) {
|
||||
|
||||
@@ -217,7 +238,7 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
if (isAssociationConversionNecessary(documentField, value)) {
|
||||
return convertAssociation(value, documentField.getProperty());
|
||||
return convertAssociation(value, documentField);
|
||||
}
|
||||
|
||||
return convertSimpleOrDBObject(value, documentField.getPropertyEntity());
|
||||
@@ -233,9 +254,10 @@ public class QueryMapper {
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private boolean isAssociationConversionNecessary(Field documentField, Object value) {
|
||||
protected boolean isAssociationConversionNecessary(Field documentField, Object value) {
|
||||
return documentField.isAssociation() && value != null
|
||||
&& documentField.getProperty().getActualType().isAssignableFrom(value.getClass());
|
||||
&& (documentField.getProperty().getActualType().isAssignableFrom(value.getClass()) //
|
||||
|| documentField.getPropertyEntity().getIdProperty().getActualType().isAssignableFrom(value.getClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,6 +292,10 @@ public class QueryMapper {
|
||||
return converter.convertToMongoType(source);
|
||||
}
|
||||
|
||||
protected Object convertAssociation(Object source, Field field) {
|
||||
return convertAssociation(source, field.getProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given source assuming it's actually an association to another object.
|
||||
*
|
||||
@@ -277,17 +303,16 @@ public class QueryMapper {
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
private Object convertAssociation(Object source, MongoPersistentProperty property) {
|
||||
protected Object convertAssociation(Object source, MongoPersistentProperty property) {
|
||||
|
||||
if (property == null || !property.isAssociation() || source == null || source instanceof DBRef
|
||||
|| !property.isEntity()) {
|
||||
if (property == null || source == null || source instanceof DBRef) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (source instanceof Iterable) {
|
||||
BasicDBList result = new BasicDBList();
|
||||
for (Object element : (Iterable<?>) source) {
|
||||
result.add(element instanceof DBRef ? element : converter.toDBRef(element, property));
|
||||
result.add(createDbRefFor(element, property));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -296,12 +321,20 @@ public class QueryMapper {
|
||||
BasicDBObject result = new BasicDBObject();
|
||||
DBObject dbObject = (DBObject) source;
|
||||
for (String key : dbObject.keySet()) {
|
||||
Object o = dbObject.get(key);
|
||||
result.put(key, o instanceof DBRef ? o : converter.toDBRef(o, property));
|
||||
result.put(key, createDbRefFor(dbObject.get(key), property));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return createDbRefFor(source, property);
|
||||
}
|
||||
|
||||
private DBRef createDbRefFor(Object source, MongoPersistentProperty property) {
|
||||
|
||||
if (source instanceof DBRef) {
|
||||
return (DBRef) source;
|
||||
}
|
||||
|
||||
return converter.toDBRef(source, property);
|
||||
}
|
||||
|
||||
@@ -360,7 +393,7 @@ public class QueryMapper {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class Keyword {
|
||||
static class Keyword {
|
||||
|
||||
private static final String N_OR_PATTERN = "\\$.*or";
|
||||
|
||||
@@ -450,7 +483,9 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link MongoPersistentProperty} backing the field.
|
||||
* Returns the underlying {@link MongoPersistentProperty} backing the field. For path traversals this will be the
|
||||
* property that represents the value to handle. This means it'll be the leaf property for plain paths or the
|
||||
* association property in case we refer to an association somewhere in the path.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -484,6 +519,19 @@ public class QueryMapper {
|
||||
public String getMappedKey() {
|
||||
return isIdField() ? ID_KEY : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the field references an association in case it refers to a nested field.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean containsAssociation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Association<MongoPersistentProperty> getAssociation() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,10 +542,13 @@ public class QueryMapper {
|
||||
*/
|
||||
protected static class MetadataBackedField extends Field {
|
||||
|
||||
private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s! Associations can only be pointed to directly or via their id property!";
|
||||
|
||||
private final MongoPersistentEntity<?> entity;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final MongoPersistentProperty property;
|
||||
private final PersistentPropertyPath<MongoPersistentProperty> path;
|
||||
private final Association<MongoPersistentProperty> association;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
|
||||
@@ -519,6 +570,7 @@ public class QueryMapper {
|
||||
|
||||
this.path = getPath(name);
|
||||
this.property = path == null ? null : path.getLeafProperty();
|
||||
this.association = findAssociation();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -552,7 +604,7 @@ public class QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
public MongoPersistentProperty getProperty() {
|
||||
return property;
|
||||
return association == null ? property : association.getInverse();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -571,9 +623,34 @@ public class QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
return association != null;
|
||||
}
|
||||
|
||||
MongoPersistentProperty property = getProperty();
|
||||
return property == null ? false : property.isAssociation();
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getAssociation()
|
||||
*/
|
||||
@Override
|
||||
public Association<MongoPersistentProperty> getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the association property in the {@link PersistentPropertyPath}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private final Association<MongoPersistentProperty> findAssociation() {
|
||||
|
||||
if (this.path != null) {
|
||||
for (MongoPersistentProperty p : this.path) {
|
||||
if (p.isAssociation()) {
|
||||
return p.getAssociation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -585,6 +662,10 @@ public class QueryMapper {
|
||||
return path == null ? name : path.toDotPath(getPropertyConverter());
|
||||
}
|
||||
|
||||
protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PersistentPropertyPath} for the given <code>pathExpression</code>.
|
||||
*
|
||||
@@ -594,8 +675,28 @@ public class QueryMapper {
|
||||
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression) {
|
||||
|
||||
try {
|
||||
|
||||
PropertyPath path = PropertyPath.from(pathExpression, entity.getTypeInformation());
|
||||
return mappingContext.getPersistentPropertyPath(path);
|
||||
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path);
|
||||
|
||||
Iterator<MongoPersistentProperty> iterator = propertyPath.iterator();
|
||||
boolean associationDetected = false;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
MongoPersistentProperty property = iterator.next();
|
||||
|
||||
if (property.isAssociation()) {
|
||||
associationDetected = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (associationDetected && !property.isIdProperty()) {
|
||||
throw new MappingException(String.format(INVALID_ASSOCIATION_REFERENCE, pathExpression));
|
||||
}
|
||||
}
|
||||
|
||||
return propertyPath;
|
||||
} catch (PropertyReferenceException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -16,24 +16,34 @@
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifier;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifiers;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* A subclass of {@link QueryMapper} that retains type information on the mongo types.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class UpdateMapper extends QueryMapper {
|
||||
|
||||
private final MongoWriter<?> converter;
|
||||
private final MongoConverter converter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link UpdateMapper} using the given {@link MongoConverter}.
|
||||
@@ -59,6 +69,59 @@ public class UpdateMapper extends QueryMapper {
|
||||
entity.getTypeInformation());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper#getMappedObjectForField(org.springframework.data.mongodb.core.convert.QueryMapper.Field, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) {
|
||||
|
||||
if (!isUpdateModifier(rawValue)) {
|
||||
return super.getMappedObjectForField(field, getMappedValue(field, rawValue));
|
||||
}
|
||||
|
||||
Object value = null;
|
||||
|
||||
if (rawValue instanceof Modifier) {
|
||||
|
||||
value = getMappedValue((Modifier) rawValue);
|
||||
|
||||
} else if (rawValue instanceof Modifiers) {
|
||||
|
||||
DBObject modificationOperations = new BasicDBObject();
|
||||
|
||||
for (Modifier modifier : ((Modifiers) rawValue).getModifiers()) {
|
||||
modificationOperations.putAll(getMappedValue(modifier).toMap());
|
||||
}
|
||||
|
||||
value = modificationOperations;
|
||||
} else {
|
||||
|
||||
throw new IllegalArgumentException(String.format("Unable to map value of type '%s'!", rawValue.getClass()));
|
||||
}
|
||||
|
||||
return Collections.singletonMap(field.getMappedKey(), value).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper#isAssociationConversionNecessary(org.springframework.data.mongodb.core.convert.QueryMapper.Field, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAssociationConversionNecessary(Field documentField, Object value) {
|
||||
return super.isAssociationConversionNecessary(documentField, value) || documentField.containsAssociation();
|
||||
}
|
||||
|
||||
private boolean isUpdateModifier(Object value) {
|
||||
return value instanceof Modifier || value instanceof Modifiers;
|
||||
}
|
||||
|
||||
private DBObject getMappedValue(Modifier modifier) {
|
||||
|
||||
Object value = converter.convertToMongoType(modifier.getValue(), ClassTypeInformation.OBJECT);
|
||||
return new BasicDBObject(modifier.getKey(), value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper#createPropertyField(org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.String, org.springframework.data.mapping.context.MappingContext)
|
||||
@@ -106,7 +169,47 @@ public class UpdateMapper extends QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
|
||||
return new UpdatePropertyConverter(key);
|
||||
return isAssociation() ? new AssociationConverter(getAssociation()) : new UpdatePropertyConverter(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter to skip all properties after an association property was rendered.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private boolean associationFound;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AssociationConverter} for the given {@link Association}.
|
||||
*
|
||||
* @param association must not be {@literal null}.
|
||||
*/
|
||||
public AssociationConverter(Association<MongoPersistentProperty> association) {
|
||||
|
||||
Assert.notNull(association, "Association must not be null!");
|
||||
this.property = association.getInverse();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(MongoPersistentProperty source) {
|
||||
|
||||
if (associationFound) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.equals(source)) {
|
||||
associationFound = true;
|
||||
}
|
||||
|
||||
return source.getFieldName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,10 +51,24 @@ public @interface CompoundIndex {
|
||||
@Deprecated
|
||||
IndexDirection direction() default IndexDirection.ASCENDING;
|
||||
|
||||
/**
|
||||
* @see http://docs.mongodb.org/manual/core/index-unique/
|
||||
* @return
|
||||
*/
|
||||
boolean unique() default false;
|
||||
|
||||
/**
|
||||
* If set to true index will skip over any document that is missing the indexed field.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/core/index-sparse/
|
||||
* @return
|
||||
*/
|
||||
boolean sparse() default false;
|
||||
|
||||
/**
|
||||
* @see http://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping
|
||||
* @return
|
||||
*/
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,8 +41,7 @@ public class Index implements IndexDefinition {
|
||||
|
||||
private boolean sparse = false;
|
||||
|
||||
public Index() {
|
||||
}
|
||||
public Index() {}
|
||||
|
||||
public Index(String key, Direction direction) {
|
||||
fieldSpec.put(key, direction);
|
||||
@@ -83,16 +82,33 @@ public class Index implements IndexDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject all documents that contain a duplicate value for the indexed field.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/core/index-unique/
|
||||
* @return
|
||||
*/
|
||||
public Index unique() {
|
||||
this.unique = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip over any document that is missing the indexed field.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/core/index-sparse/
|
||||
* @return
|
||||
*/
|
||||
public Index sparse() {
|
||||
this.sparse = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping
|
||||
* @param duplicates
|
||||
* @return
|
||||
*/
|
||||
public Index unique(Duplicates duplicates) {
|
||||
if (duplicates == Duplicates.DROP) {
|
||||
this.dropDuplicates = true;
|
||||
|
||||
@@ -32,16 +32,42 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Indexed {
|
||||
|
||||
/**
|
||||
* If set to true reject all documents that contain a duplicate value for the indexed field.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/core/index-unique/
|
||||
* @return
|
||||
*/
|
||||
boolean unique() default false;
|
||||
|
||||
IndexDirection direction() default IndexDirection.ASCENDING;
|
||||
|
||||
/**
|
||||
* If set to true index will skip over any document that is missing the indexed field.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/core/index-sparse/
|
||||
* @return
|
||||
*/
|
||||
boolean sparse() default false;
|
||||
|
||||
/**
|
||||
* @see http://docs.mongodb.org/manual/core/index-creation/#index-creation-duplicate-dropping
|
||||
* @return
|
||||
*/
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
* Index name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Colleciton name for index to be created on.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String collection() default "";
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.mongodb.DBObject;
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class Criteria implements CriteriaDefinition {
|
||||
|
||||
@@ -117,8 +118,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $ne operator
|
||||
* Creates a criterion using the {@literal $ne} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/ne/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -128,8 +130,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lt operator
|
||||
* Creates a criterion using the {@literal $lt} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/lt/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -139,8 +142,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lte operator
|
||||
* Creates a criterion using the {@literal $lte} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/lte/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -150,8 +154,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gt operator
|
||||
* Creates a criterion using the {@literal $gt} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/gt/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -161,8 +166,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gte operator
|
||||
* Creates a criterion using the {@literal $gte} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/gte/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -172,8 +178,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $in operator
|
||||
* Creates a criterion using the {@literal $in} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/in/
|
||||
* @param o the values to match against
|
||||
* @return
|
||||
*/
|
||||
@@ -187,8 +194,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $in operator
|
||||
* Creates a criterion using the {@literal $in} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/in/
|
||||
* @param c the collection containing the values to match against
|
||||
* @return
|
||||
*/
|
||||
@@ -198,8 +206,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $nin operator
|
||||
* Creates a criterion using the {@literal $nin} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/nin/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -207,14 +216,22 @@ public class Criteria implements CriteriaDefinition {
|
||||
return nin(Arrays.asList(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $nin} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/nin/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria nin(Collection<?> o) {
|
||||
criteria.put("$nin", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $mod operator
|
||||
* Creates a criterion using the {@literal $mod} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/mod/
|
||||
* @param value
|
||||
* @param remainder
|
||||
* @return
|
||||
@@ -228,8 +245,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $all operator
|
||||
* Creates a criterion using the {@literal $all} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/all/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
@@ -237,14 +255,22 @@ public class Criteria implements CriteriaDefinition {
|
||||
return all(Arrays.asList(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $all} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/all/
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria all(Collection<?> o) {
|
||||
criteria.put("$all", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $size operator
|
||||
* Creates a criterion using the {@literal $size} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/size/
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
@@ -254,8 +280,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $exists operator
|
||||
* Creates a criterion using the {@literal $exists} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/exists/
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
@@ -265,8 +292,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $type operator
|
||||
* Creates a criterion using the {@literal $type} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/type/
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
@@ -276,22 +304,31 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $not meta operator which affects the clause directly following
|
||||
* Creates a criterion using the {@literal $not} meta operator which affects the clause directly following
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/not/
|
||||
* @return
|
||||
*/
|
||||
public Criteria not() {
|
||||
return not(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $not} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/not/
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private Criteria not(Object value) {
|
||||
criteria.put("$not", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using a $regex
|
||||
* Creates a criterion using a {@literal $regex} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/regex/
|
||||
* @param re
|
||||
* @return
|
||||
*/
|
||||
@@ -300,8 +337,10 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using a $regex and $options
|
||||
* Creates a criterion using a {@literal $regex} and {@literal $options} operator.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/regex/
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/regex/#op._S_options
|
||||
* @param re
|
||||
* @param options
|
||||
* @return
|
||||
@@ -334,8 +373,11 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher.
|
||||
* Creates a geospatial criterion using a {@literal $within $centerSphere} operation. This is only available for Mongo
|
||||
* 1.7 and higher.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/geoWithin/
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/centerSphere/
|
||||
* @param circle must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
@@ -345,6 +387,13 @@ public class Criteria implements CriteriaDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a {@literal $within} operation.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/geoWithin/
|
||||
* @param shape
|
||||
* @return
|
||||
*/
|
||||
public Criteria within(Shape shape) {
|
||||
|
||||
Assert.notNull(shape);
|
||||
@@ -353,8 +402,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $near operation
|
||||
* Creates a geospatial criterion using a {@literal $near} operation.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/near/
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
@@ -365,8 +415,10 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher.
|
||||
* Creates a geospatial criterion using a {@literal $nearSphere} operation. This is only available for Mongo 1.7 and
|
||||
* higher.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/nearSphere/
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
@@ -377,8 +429,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatical criterion using a $maxDistance operation, for use with $near
|
||||
* Creates a geospatical criterion using a {@literal $maxDistance} operation, for use with $near
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/maxDistance/
|
||||
* @param maxDistance
|
||||
* @return
|
||||
*/
|
||||
@@ -388,8 +441,9 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $elemMatch operator
|
||||
* Creates a criterion using the {@literal $elemMatch} operator
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/query/elemMatch/
|
||||
* @param c
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -99,11 +99,23 @@ public class Query {
|
||||
return this.fieldSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set number of documents to skip before returning results.
|
||||
*
|
||||
* @param skip
|
||||
* @return
|
||||
*/
|
||||
public Query skip(int skip) {
|
||||
this.skip = skip;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of returned documents to {@code limit}.
|
||||
*
|
||||
* @param limit
|
||||
* @return
|
||||
*/
|
||||
public Query limit(int limit) {
|
||||
this.limit = limit;
|
||||
return this;
|
||||
@@ -231,14 +243,27 @@ public class Query {
|
||||
return dbo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of documents to skip.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getSkip() {
|
||||
return this.skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of documents to be return.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getLimit() {
|
||||
return this.limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,18 @@
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
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;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
@@ -32,6 +39,7 @@ import com.mongodb.DBObject;
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Becca Gaspard
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class Update {
|
||||
|
||||
@@ -39,7 +47,9 @@ public class Update {
|
||||
LAST, FIRST
|
||||
}
|
||||
|
||||
private HashMap<String, Object> modifierOps = new LinkedHashMap<String, Object>();
|
||||
private Set<String> keysToUpdate = new HashSet<String>();
|
||||
private Map<String, Object> modifierOps = new LinkedHashMap<String, Object>();
|
||||
private Map<String, PushOperatorBuilder> pushCommandBuilders = new LinkedHashMap<String, PushOperatorBuilder>(1);
|
||||
|
||||
/**
|
||||
* Static factory method to create an Update using the provided key
|
||||
@@ -73,15 +83,22 @@ public class Update {
|
||||
continue;
|
||||
}
|
||||
|
||||
update.modifierOps.put(key, object.get(key));
|
||||
Object value = object.get(key);
|
||||
update.modifierOps.put(key, value);
|
||||
if (isKeyword(key) && value instanceof DBObject) {
|
||||
update.keysToUpdate.addAll(((DBObject) value).keySet());
|
||||
} else {
|
||||
update.keysToUpdate.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $set update modifier
|
||||
* Update using the {@literal $set} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/set/
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
@@ -92,8 +109,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $setOnInsert update modifier
|
||||
* Update using the {@literal $setOnInsert} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
@@ -104,8 +122,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $unset update modifier
|
||||
* Update using the {@literal $unset} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/unset/
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@@ -115,8 +134,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $inc update modifier
|
||||
* Update using the {@literal $inc} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/inc/
|
||||
* @param key
|
||||
* @param inc
|
||||
* @return
|
||||
@@ -127,8 +147,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $push update modifier
|
||||
* Update using the {@literal $push} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/push/
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
@@ -139,26 +160,46 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $pushAll update modifier
|
||||
* Update using {@code $push} modifier. <br/>
|
||||
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/push/
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/each/
|
||||
* @param key
|
||||
* @return {@link PushOperatorBuilder} for given key
|
||||
*/
|
||||
public PushOperatorBuilder push(String key) {
|
||||
|
||||
if (!pushCommandBuilders.containsKey(key)) {
|
||||
pushCommandBuilders.put(key, new PushOperatorBuilder(key));
|
||||
}
|
||||
return pushCommandBuilders.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the {@code $pushAll} update modifier. <br>
|
||||
* <b>Note</b>: In mongodb 2.4 the usage of {@code $pushAll} has been deprecated in favor of {@code $push $each}.
|
||||
* {@link #push(String)}) returns a builder that can be used to populate the {@code $each} object.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/pushAll/
|
||||
* @param key
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public Update pushAll(String key, Object[] values) {
|
||||
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
modifierOps.put("$pushAll", keyValue);
|
||||
addMultiFieldOperation("$pushAll", key, convertedValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $addToSet update modifier
|
||||
* Update using the {@literal $addToSet} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/addToSet/
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
@@ -169,8 +210,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $pop update modifier
|
||||
* Update using the {@literal $pop} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/pop/
|
||||
* @param key
|
||||
* @param pos
|
||||
* @return
|
||||
@@ -181,8 +223,9 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $pull update modifier
|
||||
* Update using the {@literal $pull} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/pull/
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
@@ -193,26 +236,27 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $pullAll update modifier
|
||||
* Update using the {@literal $pullAll} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/pullAll/
|
||||
* @param key
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public Update pullAll(String key, Object[] values) {
|
||||
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
modifierOps.put("$pullAll", keyValue);
|
||||
addFieldOperation("$pullAll", key, convertedValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update using the $rename update modifier
|
||||
* Update using the {@literal $rename} update modifier
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/rename/
|
||||
* @param oldName
|
||||
* @param newName
|
||||
* @return
|
||||
@@ -230,8 +274,16 @@ public class Update {
|
||||
return dbo;
|
||||
}
|
||||
|
||||
protected void addFieldOperation(String operator, String key, Object value) {
|
||||
|
||||
Assert.hasText(key, "Key/Path for update must not be null or blank.");
|
||||
modifierOps.put(operator, new BasicDBObject(key, value));
|
||||
this.keysToUpdate.add(key);
|
||||
}
|
||||
|
||||
protected void addMultiFieldOperation(String operator, String key, Object value) {
|
||||
|
||||
Assert.hasText(key, "Key/Path for update must not be null or blank.");
|
||||
Object existingValue = this.modifierOps.get(operator);
|
||||
DBObject keyValueMap;
|
||||
|
||||
@@ -248,5 +300,146 @@ public class Update {
|
||||
}
|
||||
|
||||
keyValueMap.put(key, value);
|
||||
this.keysToUpdate.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given {@code key} will be touched on execution.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public boolean modifies(String key) {
|
||||
return this.keysToUpdate.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects given {@code key} for '$'.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private static boolean isKeyword(String key) {
|
||||
return StringUtils.startsWithIgnoreCase(key, "$");
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifiers holds a distinct collection of {@link Modifier}
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Modifiers {
|
||||
|
||||
private HashMap<String, Modifier> modifiers;
|
||||
|
||||
public Modifiers() {
|
||||
this.modifiers = new LinkedHashMap<String, Modifier>(1);
|
||||
}
|
||||
|
||||
public Collection<Modifier> getModifiers() {
|
||||
return Collections.unmodifiableCollection(this.modifiers.values());
|
||||
}
|
||||
|
||||
public void addModifier(Modifier modifier) {
|
||||
this.modifiers.put(modifier.getKey(), modifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker interface of nested commands.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static interface Modifier {
|
||||
|
||||
/**
|
||||
* @return the command to send eg. {@code $push}
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* @return value to be sent with command
|
||||
*/
|
||||
Object getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Modifier} representing {@code $each}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
private static class Each implements Modifier {
|
||||
|
||||
private Object[] values;
|
||||
|
||||
public Each(Object... values) {
|
||||
this.values = extractValues(values);
|
||||
}
|
||||
|
||||
private Object[] extractValues(Object[] values) {
|
||||
|
||||
if (values == null || values.length == 0) {
|
||||
return values;
|
||||
}
|
||||
|
||||
if (values.length == 1 && values[0] instanceof Collection) {
|
||||
return ((Collection<?>) values[0]).toArray();
|
||||
}
|
||||
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
|
||||
return convertedValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "$each";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return this.values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for creating {@code $push} modifiers
|
||||
*
|
||||
* @author Christop Strobl
|
||||
*/
|
||||
public class PushOperatorBuilder {
|
||||
|
||||
private final String key;
|
||||
private final Modifiers modifiers;
|
||||
|
||||
PushOperatorBuilder(String key) {
|
||||
this.key = key;
|
||||
this.modifiers = new Modifiers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates {@code $each} to {@code $push}
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public Update each(Object... values) {
|
||||
|
||||
this.modifiers.addModifier(new Each(values));
|
||||
return Update.this.push(key, this.modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates {@link #value(Object)} to {@code $push}
|
||||
*
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public Update value(Object value) {
|
||||
return Update.this.push(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
|
||||
}
|
||||
|
||||
private DBObject getMappedQuery(Query query) {
|
||||
return query == null ? null : getMappedQuery(query.getQueryObject());
|
||||
return query == null ? new Query().getQueryObject() : getMappedQuery(query.getQueryObject());
|
||||
}
|
||||
|
||||
private DBObject getMappedQuery(DBObject query) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* 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
|
||||
* 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,
|
||||
@@ -15,33 +15,32 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoDbFactory}.
|
||||
*
|
||||
* @author Thomas Risbergf
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoDbFactoryNoDatabaseRunningTests {
|
||||
|
||||
@Autowired
|
||||
MongoTemplate mongoTemplate;
|
||||
@Autowired MongoTemplate mongoTemplate;
|
||||
|
||||
/**
|
||||
* @see DATADOC-139
|
||||
* @see DATAMONGO-139
|
||||
*/
|
||||
@Test
|
||||
public void startsUpWithoutADatabaseRunning() {
|
||||
|
||||
@@ -70,7 +70,7 @@ public abstract class DBObjectTestUtils {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getTypedValue(DBObject source, String key, Class<T> type) {
|
||||
public static <T> T getTypedValue(DBObject source, String key, Class<T> type) {
|
||||
|
||||
Object value = source.get(key);
|
||||
assertThat(value, is(notNullValue()));
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
@@ -25,6 +26,7 @@ import static org.springframework.data.mongodb.core.query.Update.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -35,7 +37,6 @@ import org.bson.types.ObjectId;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -76,6 +77,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.CommandResult;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
@@ -101,10 +103,14 @@ import com.mongodb.WriteResult;
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class MongoTemplateTests {
|
||||
|
||||
private static final org.springframework.data.util.Version TWO_DOT_FOUR = org.springframework.data.util.Version
|
||||
.parse("2.4");
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
@Autowired MongoDbFactory factory;
|
||||
|
||||
MongoTemplate mappingTemplate;
|
||||
org.springframework.data.util.Version mongoVersion;
|
||||
|
||||
@Rule public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@@ -135,6 +141,7 @@ public class MongoTemplateTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanDb();
|
||||
queryMongoVersionIfNecessary();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -142,6 +149,14 @@ public class MongoTemplateTests {
|
||||
cleanDb();
|
||||
}
|
||||
|
||||
private void queryMongoVersionIfNecessary() {
|
||||
|
||||
if (mongoVersion == null) {
|
||||
CommandResult result = template.executeCommand("{ buildInfo: 1 }");
|
||||
mongoVersion = org.springframework.data.util.Version.parse(result.get("version").toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected void cleanDb() {
|
||||
template.dropCollection(Person.class);
|
||||
template.dropCollection(PersonWithAList.class);
|
||||
@@ -168,6 +183,9 @@ public class MongoTemplateTests {
|
||||
template.dropCollection(BaseDoc.class);
|
||||
template.dropCollection(ObjectWithEnumValue.class);
|
||||
template.dropCollection(DocumentWithCollection.class);
|
||||
template.dropCollection(DocumentWithCollectionOfSimpleType.class);
|
||||
template.dropCollection(DocumentWithMultipleCollections.class);
|
||||
template.dropCollection(DocumentWithDBRefCollection.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -887,7 +905,7 @@ public class MongoTemplateTests {
|
||||
l2.add(31);
|
||||
Query q3 = new Query(Criteria.where("age").in(l1, l2));
|
||||
template.find(q3, PersonWithIdPropertyOfTypeObjectId.class);
|
||||
Assert.fail("Should have trown an InvalidDocumentStoreApiUsageException");
|
||||
fail("Should have trown an InvalidDocumentStoreApiUsageException");
|
||||
} catch (InvalidMongoDbApiUsageException e) {}
|
||||
}
|
||||
|
||||
@@ -2202,8 +2220,8 @@ public class MongoTemplateTests {
|
||||
template.findAndModify(query, update, Document.class);
|
||||
|
||||
Document retrieved = template.findOne(query, Document.class);
|
||||
Assert.assertThat(retrieved.model, instanceOf(ModelA.class));
|
||||
Assert.assertThat(retrieved.model.value(), equalTo("value2"));
|
||||
assertThat(retrieved.model, instanceOf(ModelA.class));
|
||||
assertThat(retrieved.model.value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2212,16 +2230,16 @@ public class MongoTemplateTests {
|
||||
@Test
|
||||
public void updatesShouldRetainTypeInformationEvenForCollections() {
|
||||
|
||||
DocumentWithCollection doc = new DocumentWithCollection();
|
||||
List<Model> models = Arrays.<Model> asList(new ModelA("foo"));
|
||||
|
||||
DocumentWithCollection doc = new DocumentWithCollection(models);
|
||||
doc.id = "4711";
|
||||
doc.model = new ArrayList<Model>();
|
||||
doc.model.add(new ModelA("foo"));
|
||||
template.insert(doc);
|
||||
|
||||
Query query = new Query(Criteria.where("id").is(doc.id));
|
||||
query.addCriteria(where("model.value").is("foo"));
|
||||
query.addCriteria(where("models.value").is("foo"));
|
||||
String newModelValue = "bar";
|
||||
Update update = Update.update("model.$", new ModelA(newModelValue));
|
||||
Update update = Update.update("models.$", new ModelA(newModelValue));
|
||||
template.updateFirst(query, update, DocumentWithCollection.class);
|
||||
|
||||
Query findQuery = new Query(Criteria.where("id").is(doc.id));
|
||||
@@ -2229,15 +2247,237 @@ public class MongoTemplateTests {
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.id, is(doc.id));
|
||||
assertThat(result.model, is(notNullValue()));
|
||||
assertThat(result.model, hasSize(1));
|
||||
assertThat(result.model.get(0).value(), is(newModelValue));
|
||||
assertThat(result.models, is(notNullValue()));
|
||||
assertThat(result.models, hasSize(1));
|
||||
assertThat(result.models.get(0).value(), is(newModelValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void updateMultiShouldAddValuesCorrectlyWhenUsingPushEachWithComplexTypes() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_FOUR), is(true));
|
||||
|
||||
DocumentWithCollection document = new DocumentWithCollection(Collections.<Model> emptyList());
|
||||
template.save(document);
|
||||
Query query = query(where("id").is(document.id));
|
||||
assumeThat(template.findOne(query, DocumentWithCollection.class).models, hasSize(1));
|
||||
|
||||
Update update = new Update().push("models").each(new ModelA("model-b"), new ModelA("model-c"));
|
||||
template.updateMulti(query, update, DocumentWithCollection.class);
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithCollection.class).models, hasSize(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void updateMultiShouldAddValuesCorrectlyWhenUsingPushEachWithSimpleTypes() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_FOUR), is(true));
|
||||
|
||||
DocumentWithCollectionOfSimpleType document = new DocumentWithCollectionOfSimpleType();
|
||||
document.values = Arrays.asList("spring");
|
||||
template.save(document);
|
||||
|
||||
Query query = query(where("id").is(document.id));
|
||||
assumeThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values, hasSize(1));
|
||||
|
||||
Update update = new Update().push("values").each("data", "mongodb");
|
||||
template.updateMulti(query, update, DocumentWithCollectionOfSimpleType.class);
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values, hasSize(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* <<<<<<< HEAD
|
||||
*
|
||||
* @see DATAMONOGO-828
|
||||
*/
|
||||
@Test
|
||||
public void updateFirstShouldDoNothingWhenCalledForEntitiesThatDoNotExist() {
|
||||
|
||||
Query q = query(where("id").is(Long.MIN_VALUE));
|
||||
|
||||
template.updateFirst(q, Update.update("lastname", "supercalifragilisticexpialidocious"), VersionedPerson.class);
|
||||
assertThat(template.findOne(q, VersionedPerson.class), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-354
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateShouldAllowMultiplePushAll() {
|
||||
|
||||
DocumentWithMultipleCollections doc = new DocumentWithMultipleCollections();
|
||||
doc.id = "1234";
|
||||
doc.string1 = Arrays.asList("spring");
|
||||
doc.string2 = Arrays.asList("one");
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().pushAll("string1", new Object[] { "data", "mongodb" });
|
||||
update.pushAll("string2", new String[] { "two", "three" });
|
||||
|
||||
Query findQuery = new Query(Criteria.where("id").is(doc.id));
|
||||
template.updateFirst(findQuery, update, DocumentWithMultipleCollections.class);
|
||||
|
||||
DocumentWithMultipleCollections result = template.findOne(findQuery, DocumentWithMultipleCollections.class);
|
||||
assertThat(result.string1, hasItems("spring", "data", "mongodb"));
|
||||
assertThat(result.string2, hasItems("one", "two", "three"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test
|
||||
public void updateWithPullShouldRemoveNestedItemFromDbRefAnnotatedCollection() {
|
||||
|
||||
Sample sample1 = new Sample("1", "A");
|
||||
Sample sample2 = new Sample("2", "B");
|
||||
template.save(sample1);
|
||||
template.save(sample2);
|
||||
|
||||
DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection();
|
||||
doc.id = "1";
|
||||
doc.dbRefAnnotatedList = Arrays.asList( //
|
||||
sample1, //
|
||||
sample2 //
|
||||
);
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList", doc.dbRefAnnotatedList.get(1));
|
||||
|
||||
Query qry = query(where("id").is("1"));
|
||||
template.updateFirst(qry, update, DocumentWithDBRefCollection.class);
|
||||
|
||||
DocumentWithDBRefCollection result = template.findOne(qry, DocumentWithDBRefCollection.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.dbRefAnnotatedList, hasSize(1));
|
||||
assertThat(result.dbRefAnnotatedList.get(0), is(notNullValue()));
|
||||
assertThat(result.dbRefAnnotatedList.get(0).id, is((Object) "1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test
|
||||
public void updateWithPullShouldRemoveNestedItemFromDbRefAnnotatedCollectionWhenGivenAnIdValueOfComponentTypeEntity() {
|
||||
|
||||
Sample sample1 = new Sample("1", "A");
|
||||
Sample sample2 = new Sample("2", "B");
|
||||
template.save(sample1);
|
||||
template.save(sample2);
|
||||
|
||||
DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection();
|
||||
doc.id = "1";
|
||||
doc.dbRefAnnotatedList = Arrays.asList( //
|
||||
sample1, //
|
||||
sample2 //
|
||||
);
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList.id", "2");
|
||||
|
||||
Query qry = query(where("id").is("1"));
|
||||
template.updateFirst(qry, update, DocumentWithDBRefCollection.class);
|
||||
|
||||
DocumentWithDBRefCollection result = template.findOne(qry, DocumentWithDBRefCollection.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.dbRefAnnotatedList, hasSize(1));
|
||||
assertThat(result.dbRefAnnotatedList.get(0), is(notNullValue()));
|
||||
assertThat(result.dbRefAnnotatedList.get(0).id, is((Object) "1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void updateShouldNotBumpVersionNumberIfVersionPropertyIncludedInUpdate() {
|
||||
|
||||
VersionedPerson person = new VersionedPerson();
|
||||
person.firstname = "Dave";
|
||||
person.lastname = "Matthews";
|
||||
template.save(person);
|
||||
assertThat(person.id, is(notNullValue()));
|
||||
|
||||
Query qry = query(where("id").is(person.id));
|
||||
VersionedPerson personAfterFirstSave = template.findOne(qry, VersionedPerson.class);
|
||||
assertThat(personAfterFirstSave.version, is(0L));
|
||||
|
||||
template.updateFirst(qry, Update.update("lastname", "Bubu").set("version", 100L), VersionedPerson.class);
|
||||
|
||||
VersionedPerson personAfterUpdateFirst = template.findOne(qry, VersionedPerson.class);
|
||||
assertThat(personAfterUpdateFirst.version, is(100L));
|
||||
assertThat(personAfterUpdateFirst.lastname, is("Bubu"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-468
|
||||
*/
|
||||
@Test
|
||||
public void shouldBeAbleToUpdateDbRefPropertyWithDomainObject(){
|
||||
|
||||
Sample sample1 = new Sample("1", "A");
|
||||
Sample sample2 = new Sample("2", "B");
|
||||
template.save(sample1);
|
||||
template.save(sample2);
|
||||
|
||||
DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection();
|
||||
doc.id = "1";
|
||||
doc.dbRefProperty = sample1;
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().set("dbRefProperty",sample2);
|
||||
|
||||
Query qry = query(where("id").is("1"));
|
||||
template.updateFirst(qry, update, DocumentWithDBRefCollection.class);
|
||||
|
||||
DocumentWithDBRefCollection updatedDoc = template.findOne(qry, DocumentWithDBRefCollection.class);
|
||||
|
||||
assertThat(updatedDoc,is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty,is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty.id,is(sample2.id));
|
||||
assertThat(updatedDoc.dbRefProperty.field,is(sample2.field));
|
||||
}
|
||||
|
||||
static class DocumentWithDBRefCollection {
|
||||
|
||||
@Id public String id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public List<Sample> dbRefAnnotatedList;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef
|
||||
public Sample dbRefProperty;
|
||||
}
|
||||
|
||||
static class DocumentWithCollection {
|
||||
|
||||
@Id public String id;
|
||||
public List<Model> model;
|
||||
@Id String id;
|
||||
List<Model> models;
|
||||
|
||||
DocumentWithCollection(List<Model> models) {
|
||||
this.models = models;
|
||||
}
|
||||
}
|
||||
|
||||
static class DocumentWithCollectionOfSimpleType {
|
||||
|
||||
@Id String id;
|
||||
List<String> values;
|
||||
}
|
||||
|
||||
static class DocumentWithMultipleCollections {
|
||||
@Id String id;
|
||||
List<String> string1;
|
||||
List<String> string2;
|
||||
}
|
||||
|
||||
static interface Model {
|
||||
@@ -2279,6 +2519,13 @@ public class MongoTemplateTests {
|
||||
|
||||
@Id String id;
|
||||
String field;
|
||||
|
||||
public Sample() {}
|
||||
|
||||
public Sample(String id, String field) {
|
||||
this.id = id;
|
||||
this.field = field;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestClass {
|
||||
|
||||
@@ -189,7 +189,7 @@ public class AggregationTests {
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getMappedResults();
|
||||
|
||||
@@ -217,7 +217,7 @@ public class AggregationTests {
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getMappedResults();
|
||||
|
||||
@@ -241,7 +241,7 @@ public class AggregationTests {
|
||||
AggregationResults<TagCount> results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results.getServerUsed(), is("/127.0.0.1:27017"));
|
||||
assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017"));
|
||||
|
||||
List<TagCount> tagCount = results.getMappedResults();
|
||||
|
||||
@@ -782,6 +782,46 @@ public class AggregationTests {
|
||||
assertThat(String.valueOf(firstItem.get("_id")), is("u1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-840
|
||||
*/
|
||||
@Test
|
||||
public void shouldAggregateOrderDataToAnInvoice() {
|
||||
|
||||
mongoTemplate.dropCollection(Order.class);
|
||||
|
||||
double taxRate = 0.19;
|
||||
|
||||
LineItem product1 = new LineItem("1", "p1", 1.23);
|
||||
LineItem product2 = new LineItem("2", "p2", 0.87, 2);
|
||||
LineItem product3 = new LineItem("3", "p3", 5.33);
|
||||
|
||||
Order order = new Order("o4711", "c42", new Date()).addItem(product1).addItem(product2).addItem(product3);
|
||||
|
||||
mongoTemplate.save(order);
|
||||
|
||||
AggregationResults<Invoice> results = mongoTemplate.aggregate(newAggregation(Order.class, //
|
||||
match(where("id").is(order.getId())), unwind("items"), //
|
||||
project("id", "customerId", "items") //
|
||||
.andExpression("items.price * items.quantity").as("lineTotal"), //
|
||||
group("id") //
|
||||
.sum("lineTotal").as("netAmount") //
|
||||
.addToSet("items").as("items"), //
|
||||
project("id", "items", "netAmount") //
|
||||
.and("orderId").previousOperation() //
|
||||
.andExpression("netAmount * [0]", taxRate).as("taxAmount") //
|
||||
.andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") //
|
||||
), Invoice.class);
|
||||
|
||||
Invoice invoice = results.getUniqueMappedResult();
|
||||
|
||||
assertThat(invoice, is(notNullValue()));
|
||||
assertThat(invoice.getOrderId(), is(order.getId()));
|
||||
assertThat(invoice.getNetAmount(), is(closeTo(8.3, 000001)));
|
||||
assertThat(invoice.getTaxAmount(), is(closeTo(1.577, 000001)));
|
||||
assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001)));
|
||||
}
|
||||
|
||||
private void assertLikeStats(LikeStats like, String id, long count) {
|
||||
|
||||
assertThat(like, is(notNullValue()));
|
||||
|
||||
@@ -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,6 +28,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
@@ -161,4 +162,21 @@ public class AggregationUnitTests {
|
||||
assertThat(fields.get("aCnt"), is((Object) 1));
|
||||
assertThat(fields.get("a"), is((Object) "$_id.a"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-838
|
||||
*/
|
||||
@Test
|
||||
public void expressionBasedFieldsShouldBeReferencableInFollowingOperations() {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
project("a").andExpression("b+c").as("foo"), //
|
||||
group("a").sum("foo").as("foosum") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
DBObject secondProjection = ((List<DBObject>) agg.get("pipeline")).get(1);
|
||||
DBObject fields = getAsDBObject(secondProjection, "$group");
|
||||
assertThat(fields.get("foosum"), is((Object) new BasicDBObject("$sum", "$foo")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class Invoice {
|
||||
|
||||
String orderId;
|
||||
|
||||
double taxAmount;
|
||||
|
||||
double netAmount;
|
||||
|
||||
double totalAmount;
|
||||
|
||||
List<LineItem> items;
|
||||
|
||||
public String getOrderId() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
public void setOrderId(String orderId) {
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
public double getTaxAmount() {
|
||||
return taxAmount;
|
||||
}
|
||||
|
||||
public void setTaxAmount(double taxAmount) {
|
||||
this.taxAmount = taxAmount;
|
||||
}
|
||||
|
||||
public double getNetAmount() {
|
||||
return netAmount;
|
||||
}
|
||||
|
||||
public void setNetAmount(double netAmount) {
|
||||
this.netAmount = netAmount;
|
||||
}
|
||||
|
||||
public double getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(double totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public List<LineItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<LineItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class LineItem {
|
||||
|
||||
final String id;
|
||||
|
||||
final String caption;
|
||||
|
||||
final double price;
|
||||
|
||||
int quantity = 1;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private LineItem() {
|
||||
this(null, null, 0.0, 0);
|
||||
}
|
||||
|
||||
public LineItem(String id, String caption, double price) {
|
||||
this.id = id;
|
||||
this.caption = caption;
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public LineItem(String id, String caption, double price, int quantity) {
|
||||
this(id, caption, price);
|
||||
this.quantity = quantity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class Order {
|
||||
|
||||
final String id;
|
||||
|
||||
final String customerId;
|
||||
|
||||
final Date orderDate;
|
||||
|
||||
final List<LineItem> items;
|
||||
|
||||
public Order(String id, String customerId, Date orderDate) {
|
||||
this(id, customerId, orderDate, new ArrayList<LineItem>());
|
||||
}
|
||||
|
||||
public Order(String id, String customerId, Date orderDate, List<LineItem> items) {
|
||||
this.id = id;
|
||||
this.customerId = customerId;
|
||||
this.orderDate = orderDate;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Order addItem(LineItem item) {
|
||||
|
||||
List<LineItem> newItems = new ArrayList<LineItem>(items != null ? items : Collections.<LineItem> emptyList());
|
||||
newItems.add(item);
|
||||
|
||||
return new Order(id, customerId, orderDate, newItems);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public Date getOrderDate() {
|
||||
return orderDate;
|
||||
}
|
||||
|
||||
public List<LineItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SpelExpressionTransformer}.
|
||||
@@ -174,6 +175,26 @@ public class SpelExpressionTransformerUnitTests {
|
||||
is("{ \"$multiply\" : [ { \"$add\" : [ 1 , 42 , 1.2345]} , 23]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-840
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderCompoundExpressionsWithIndexerAndFieldReference() {
|
||||
|
||||
Person person = new Person();
|
||||
person.setAge(10);
|
||||
assertThat(transform("[0].age + a.c", person), is("{ \"$add\" : [ 10 , \"$a.c\"]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-840
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderCompoundExpressionsWithOnlyFieldReferences() {
|
||||
|
||||
assertThat(transform("a.b + a.c"), is("{ \"$add\" : [ \"$a.b\" , \"$a.c\"]}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRenderStringFunctions() {
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
@@ -57,7 +58,9 @@ import org.springframework.data.mongodb.core.convert.DBObjectAccessorUnitTests.P
|
||||
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.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
@@ -72,6 +75,7 @@ import com.mongodb.util.JSON;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Patrik Wasik
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MappingMongoConverterUnitTests {
|
||||
@@ -1021,12 +1025,12 @@ public class MappingMongoConverterUnitTests {
|
||||
address.city = "London";
|
||||
address.street = "Foo";
|
||||
|
||||
Object result = converter.convertToMongoType(Collections.singleton(address));
|
||||
Object result = converter.convertToMongoType(Collections.singleton(address), ClassTypeInformation.OBJECT);
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
Set<?> readResult = converter.read(Set.class, (BasicDBList) result);
|
||||
assertThat(readResult.size(), is(1));
|
||||
assertThat(readResult.iterator().next(), is(instanceOf(Map.class)));
|
||||
assertThat(readResult.iterator().next(), is(instanceOf(Address.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1240,9 +1244,9 @@ public class MappingMongoConverterUnitTests {
|
||||
DB db = mock(DB.class);
|
||||
DBRef dbRef = new DBRef(db, "collection", "id");
|
||||
|
||||
org.springframework.data.mongodb.core.mapping.DBRef annotation = mock(org.springframework.data.mongodb.core.mapping.DBRef.class);
|
||||
MongoPersistentProperty property = mock(MongoPersistentProperty.class);
|
||||
|
||||
assertThat(converter.createDBRef(dbRef, annotation), is(dbRef));
|
||||
assertThat(converter.createDBRef(dbRef, property), is(dbRef));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1381,6 +1385,75 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(aValue.get("c"), is((Object) "C"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
|
||||
Address address = new Address();
|
||||
address.city = "London";
|
||||
address.street = "Foo";
|
||||
|
||||
Object result = converter.convertToMongoType(Collections.singletonList(address),
|
||||
ClassTypeInformation.from(Address.class));
|
||||
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
BasicDBList dbList = (BasicDBList) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(getTypedValue(getAsDBObject(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void convertsListToBasicDBListWithoutTypeInformationForSimpleTypes() {
|
||||
|
||||
Object result = converter.convertToMongoType(Collections.singletonList("foo"));
|
||||
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
BasicDBList dbList = (BasicDBList) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(dbList.get(0), instanceOf(String.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
|
||||
Address address = new Address();
|
||||
address.city = "London";
|
||||
address.street = "Foo";
|
||||
|
||||
Object result = converter.convertToMongoType(new Address[] { address }, ClassTypeInformation.OBJECT);
|
||||
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
BasicDBList dbList = (BasicDBList) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(getTypedValue(getAsDBObject(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void convertsArrayToBasicDBListWithoutTypeInformationForSimpleTypes() {
|
||||
|
||||
Object result = converter.convertToMongoType(new String[] { "foo" });
|
||||
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
BasicDBList dbList = (BasicDBList) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(dbList.get(0), instanceOf(String.class));
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
|
||||
@@ -16,28 +16,41 @@
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.collection.IsMapContaining.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
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.core.convert.converter.Converter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UpdateMapper}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UpdateMapperUnitTests {
|
||||
@@ -45,12 +58,26 @@ public class UpdateMapperUnitTests {
|
||||
@Mock MongoDbFactory factory;
|
||||
MappingMongoConverter converter;
|
||||
MongoMappingContext context;
|
||||
UpdateMapper mapper;
|
||||
|
||||
private Converter<NestedEntity, DBObject> writingConverterSpy;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
this.writingConverterSpy = Mockito.spy(new NestedEntityWriteConverter());
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(writingConverterSpy));
|
||||
|
||||
this.context = new MongoMappingContext();
|
||||
this.context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
|
||||
this.context.initialize();
|
||||
|
||||
this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context);
|
||||
this.converter.setCustomConversions(conversions);
|
||||
this.converter.afterPropertiesSet();
|
||||
|
||||
this.mapper = new UpdateMapper(converter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +87,6 @@ public class UpdateMapperUnitTests {
|
||||
public void updateMapperRetainsTypeInformationForCollectionField() {
|
||||
|
||||
Update update = new Update().push("list", new ConcreteChildClass("2", "BAR"));
|
||||
UpdateMapper mapper = new UpdateMapper(converter);
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ParentClass.class));
|
||||
@@ -175,6 +201,181 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(someObject.get("value"), is((Object) "bubu"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test
|
||||
public void updateMapperShouldConvertPushCorrectlyWhenCalledWithEachUsingSimpleTypes() {
|
||||
|
||||
Update update = new Update().push("values").each("spring", "data", "mongodb");
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Model.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject values = getAsDBObject(push, "values");
|
||||
BasicDBList each = getAsDBList(values, "$each");
|
||||
|
||||
assertThat(push.get("_class"), nullValue());
|
||||
assertThat(values.get("_class"), nullValue());
|
||||
|
||||
assertThat(each.toMap(), (Matcher) allOf(hasValue("spring"), hasValue("data"), hasValue("mongodb")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void updateMapperShouldConvertPushWhithoutAddingClassInformationWhenUsedWithEvery() {
|
||||
|
||||
Update update = new Update().push("values").each("spring", "data", "mongodb");
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Model.class));
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject values = getAsDBObject(push, "values");
|
||||
|
||||
assertThat(push.get("_class"), nullValue());
|
||||
assertThat(values.get("_class"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test
|
||||
public void updateMapperShouldConvertPushCorrectlyWhenCalledWithEachUsingCustomTypes() {
|
||||
|
||||
Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb"));
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ModelWrapper.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject model = getAsDBObject(push, "models");
|
||||
BasicDBList each = getAsDBList(model, "$each");
|
||||
BasicDBList values = getAsDBList((DBObject) each.get(0), "values");
|
||||
|
||||
assertThat(values.toMap(), (Matcher) allOf(hasValue("spring"), hasValue("data"), hasValue("mongodb")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void updateMapperShouldRetainClassInformationForPushCorrectlyWhenCalledWithEachUsingCustomTypes() {
|
||||
|
||||
Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb"));
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ModelWrapper.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject model = getAsDBObject(push, "models");
|
||||
BasicDBList each = getAsDBList(model, "$each");
|
||||
|
||||
assertThat(((DBObject) each.get(0)).get("_class").toString(), equalTo(ListModel.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateShouldAllowMultiplePushEachForDifferentFields() {
|
||||
|
||||
Update update = new Update().push("category").each("spring", "data").push("type").each("mongodb");
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
assertThat(getAsDBObject(push, "category").containsField("$each"), is(true));
|
||||
assertThat(getAsDBObject(push, "type").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-410
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateMapperShouldConsiderCustomWriteTarget() {
|
||||
|
||||
List<NestedEntity> someValues = Arrays.asList(new NestedEntity("spring"), new NestedEntity("data"),
|
||||
new NestedEntity("mongodb"));
|
||||
NestedEntity[] array = new NestedEntity[someValues.size()];
|
||||
|
||||
Update update = new Update().pushAll("collectionOfNestedEntities", someValues.toArray(array));
|
||||
mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(DomainEntity.class));
|
||||
|
||||
verify(writingConverterSpy, times(3)).convert(Mockito.any(NestedEntity.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test
|
||||
public void createsDbRefForEntityIdOnPulls() {
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList.id", "2");
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
|
||||
DBObject pullClause = getAsDBObject(mappedObject, "$pull");
|
||||
assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef(null, "entity", "2")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test
|
||||
public void createsDbRefForEntityOnPulls() {
|
||||
|
||||
Entity entity = new Entity();
|
||||
entity.id = "5";
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList", entity);
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
|
||||
DBObject pullClause = getAsDBObject(mappedObject, "$pull");
|
||||
assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef(null, "entity", entity.id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test(expected = MappingException.class)
|
||||
public void rejectsInvalidFieldReferenceForDbRef() {
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList.name", "NAME");
|
||||
mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-404
|
||||
*/
|
||||
@Test
|
||||
public void rendersNestedDbRefCorrectly() {
|
||||
|
||||
Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2");
|
||||
DBObject mappedObject = mapper
|
||||
.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Wrapper.class));
|
||||
|
||||
DBObject pullClause = getAsDBObject(mappedObject, "$pull");
|
||||
assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-468
|
||||
*/
|
||||
@Test
|
||||
public void rendersUpdateOfDbRefPropertyWithDomainObjectCorrectly() {
|
||||
|
||||
Entity entity = new Entity();
|
||||
entity.id = "5";
|
||||
|
||||
Update update = new Update().set("dbRefProperty", entity);
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
|
||||
DBObject setClause = getAsDBObject(mappedObject, "$set");
|
||||
assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef(null, "entity", entity.id)));
|
||||
}
|
||||
|
||||
static interface Model {}
|
||||
|
||||
static class ModelImpl implements Model {
|
||||
@@ -189,6 +390,20 @@ public class UpdateMapperUnitTests {
|
||||
Model model;
|
||||
}
|
||||
|
||||
static class ListModelWrapper {
|
||||
|
||||
List<Model> models;
|
||||
}
|
||||
|
||||
static class ListModel {
|
||||
|
||||
List<String> values;
|
||||
|
||||
public ListModel(String... values) {
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
}
|
||||
|
||||
static class ParentClass {
|
||||
|
||||
String id;
|
||||
@@ -223,4 +438,49 @@ public class UpdateMapperUnitTests {
|
||||
super(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
static class DomainEntity {
|
||||
List<NestedEntity> collectionOfNestedEntities;
|
||||
}
|
||||
|
||||
static class NestedEntity {
|
||||
String name;
|
||||
|
||||
public NestedEntity(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
static class NestedEntityWriteConverter implements Converter<NestedEntity, DBObject> {
|
||||
|
||||
@Override
|
||||
public DBObject convert(NestedEntity source) {
|
||||
return new BasicDBObject();
|
||||
}
|
||||
}
|
||||
|
||||
static class DocumentWithDBRefCollection {
|
||||
|
||||
@Id public String id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public List<Entity> dbRefAnnotatedList;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public Entity dbRefProperty;
|
||||
}
|
||||
|
||||
static class Entity {
|
||||
|
||||
@Id public String id;
|
||||
String name;
|
||||
}
|
||||
|
||||
static class Wrapper {
|
||||
|
||||
@Field("mapped") DocumentWithDBRefCollection nested;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.mapreduce;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.mapreduce.GroupBy.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
@@ -42,6 +44,12 @@ import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
|
||||
/**
|
||||
* Integration tests for group-by operations.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class GroupByTests {
|
||||
@@ -84,49 +92,46 @@ public class GroupByTests {
|
||||
|
||||
@Test
|
||||
public void singleKeyCreation() {
|
||||
|
||||
DBObject gc = new GroupBy("a").getGroupByObject();
|
||||
// String expected =
|
||||
// "{ \"group\" : { \"ns\" : \"test\" , \"key\" : { \"a\" : 1} , \"cond\" : null , \"$reduce\" : null , \"initial\" : null }}";
|
||||
String expected = "{ \"key\" : { \"a\" : 1} , \"$reduce\" : null , \"initial\" : null }";
|
||||
Assert.assertEquals(expected, gc.toString());
|
||||
|
||||
assertThat(gc.toString(), is("{ \"key\" : { \"a\" : 1} , \"$reduce\" : null , \"initial\" : null }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleKeyCreation() {
|
||||
|
||||
DBObject gc = GroupBy.key("a", "b").getGroupByObject();
|
||||
// String expected =
|
||||
// "{ \"group\" : { \"ns\" : \"test\" , \"key\" : { \"a\" : 1 , \"b\" : 1} , \"cond\" : null , \"$reduce\" : null , \"initial\" : null }}";
|
||||
String expected = "{ \"key\" : { \"a\" : 1 , \"b\" : 1} , \"$reduce\" : null , \"initial\" : null }";
|
||||
Assert.assertEquals(expected, gc.toString());
|
||||
|
||||
assertThat(gc.toString(), is("{ \"key\" : { \"a\" : 1 , \"b\" : 1} , \"$reduce\" : null , \"initial\" : null }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyFunctionCreation() {
|
||||
|
||||
DBObject gc = GroupBy.keyFunction("classpath:keyFunction.js").getGroupByObject();
|
||||
String expected = "{ \"$keyf\" : \"classpath:keyFunction.js\" , \"$reduce\" : null , \"initial\" : null }";
|
||||
Assert.assertEquals(expected, gc.toString());
|
||||
|
||||
assertThat(gc.toString(),
|
||||
is("{ \"$keyf\" : \"classpath:keyFunction.js\" , \"$reduce\" : null , \"initial\" : null }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroup() {
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results;
|
||||
|
||||
results = mongoTemplate.group(
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
"group_test_collection",
|
||||
GroupBy.key("x").initialDocument(new BasicDBObject("count", 0))
|
||||
.reduceFunction("function(doc, prev) { prev.count += 1 }"), XObject.class);
|
||||
|
||||
assertMapReduceResults(results);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithKeyFunction() {
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results;
|
||||
|
||||
results = mongoTemplate.group(
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
"group_test_collection",
|
||||
GroupBy.keyFunction("function(doc) { return { x : doc.x }; }").initialDocument("{ count: 0 }")
|
||||
.reduceFunction("function(doc, prev) { prev.count += 1 }"), XObject.class);
|
||||
@@ -136,30 +141,35 @@ public class GroupByTests {
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithFunctionsAsResources() {
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results;
|
||||
|
||||
results = mongoTemplate.group("group_test_collection", GroupBy.keyFunction("classpath:keyFunction.js")
|
||||
.initialDocument("{ count: 0 }").reduceFunction("classpath:groupReduce.js"), XObject.class);
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
"group_test_collection",
|
||||
GroupBy.keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }")
|
||||
.reduceFunction("classpath:groupReduce.js"), XObject.class);
|
||||
|
||||
assertMapReduceResults(results);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithQueryAndFunctionsAsResources() {
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results;
|
||||
|
||||
results = mongoTemplate.group(where("x").gt(0), "group_test_collection", keyFunction("classpath:keyFunction.js")
|
||||
.initialDocument("{ count: 0 }").reduceFunction("classpath:groupReduce.js"), XObject.class);
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
where("x").gt(0),
|
||||
"group_test_collection",
|
||||
keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }").reduceFunction(
|
||||
"classpath:groupReduce.js"), XObject.class);
|
||||
|
||||
assertMapReduceResults(results);
|
||||
}
|
||||
|
||||
private void assertMapReduceResults(GroupByResults<XObject> results) {
|
||||
|
||||
DBObject dboRawResults = results.getRawResults();
|
||||
String expected = "{ \"serverUsed\" : \"/127.0.0.1:27017\" , \"retval\" : [ { \"x\" : 1.0 , \"count\" : 2.0} , { \"x\" : 2.0 , \"count\" : 1.0} , { \"x\" : 3.0 , \"count\" : 3.0}] , \"count\" : 6.0 , \"keys\" : 3 , \"ok\" : 1.0}";
|
||||
Assert.assertEquals(expected, dboRawResults.toString());
|
||||
|
||||
assertThat(dboRawResults.containsField("serverUsed"), is(true));
|
||||
assertThat(dboRawResults.get("serverUsed").toString(), endsWith("127.0.0.1:27017"));
|
||||
|
||||
int numResults = 0;
|
||||
for (XObject xObject : results) {
|
||||
@@ -174,13 +184,15 @@ public class GroupByTests {
|
||||
}
|
||||
numResults++;
|
||||
}
|
||||
Assert.assertEquals(3, numResults);
|
||||
Assert.assertEquals(6, results.getCount(), 0.001);
|
||||
Assert.assertEquals(3, results.getKeys());
|
||||
assertThat(numResults, is(3));
|
||||
assertThat(results.getKeys(), is(3));
|
||||
assertEquals(6, results.getCount(), 0.001);
|
||||
}
|
||||
|
||||
private void createGroupByData() {
|
||||
|
||||
DBCollection c = mongoTemplate.getDb().getCollection("group_test_collection");
|
||||
|
||||
c.save(new BasicDBObject("x", 1));
|
||||
c.save(new BasicDBObject("x", 1));
|
||||
c.save(new BasicDBObject("x", 2));
|
||||
@@ -188,5 +200,4 @@ public class GroupByTests {
|
||||
c.save(new BasicDBObject("x", 3));
|
||||
c.save(new BasicDBObject("x", 3));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.junit.Test;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Risberg
|
||||
* @author Becca Gaspard
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class UpdateTests {
|
||||
|
||||
@@ -94,6 +95,23 @@ public class UpdateTests {
|
||||
is("{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-354
|
||||
*/
|
||||
@Test
|
||||
public void testMultiplePushAllShouldBePossibleWhenUsingDifferentFields() {
|
||||
|
||||
Map<String, String> m1 = Collections.singletonMap("name", "Sven");
|
||||
Map<String, String> m2 = Collections.singletonMap("name", "Maria");
|
||||
|
||||
Update u = new Update().pushAll("authors", new Object[] { m1, m2 });
|
||||
u.pushAll("books", new Object[] { "Spring in Action" });
|
||||
|
||||
assertThat(
|
||||
u.getUpdateObject().toString(),
|
||||
is("{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}] , \"books\" : [ \"Spring in Action\"]}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddToSet() {
|
||||
|
||||
@@ -170,4 +188,100 @@ public class UpdateTests {
|
||||
Update u = new Update().setOnInsert("size", 1).setOnInsert("count", 1);
|
||||
assertThat(u.getUpdateObject().toString(), is("{ \"$setOnInsert\" : { \"size\" : 1 , \"count\" : 1}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnTrueWhenMultiFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
assertThat(update.modifies("foo"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenMultiFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
assertThat(update.modifies("oof"), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnTrueWhenSingleFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().pullAll("foo", new Object[] { "bar" });
|
||||
assertThat(update.modifies("foo"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenSingleFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().pullAll("foo", new Object[] { "bar" });
|
||||
assertThat(update.modifies("oof"), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenCalledOnEmptyUpdate() {
|
||||
assertThat(new Update().modifies("foo"), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnTrueWhenUpdateWithKeyCreatedFromDbObject() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
Update clone = Update.fromDBObject(update.getUpdateObject());
|
||||
|
||||
assertThat(clone.modifies("foo"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenUpdateWithoutKeyCreatedFromDbObject() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
Update clone = Update.fromDBObject(update.getUpdateObject());
|
||||
|
||||
assertThat(clone.modifies("oof"), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-853
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testAddingMultiFieldOperationThrowsExceptionWhenCalledWithNullKey() {
|
||||
new Update().addMultiFieldOperation("$op", null, "exprected to throw IllegalArgumentException.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-853
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testAddingSingleFieldOperationThrowsExceptionWhenCalledWithNullKey() {
|
||||
new Update().addFieldOperation("$op", null, "exprected to throw IllegalArgumentException.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-853
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreatingUpdateWithNullKeyThrowsException() {
|
||||
Update.update(null, "value");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.repository.ContactRepository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -71,6 +72,7 @@ public class MongoRepositoryFactoryBeanUnitTests {
|
||||
|
||||
factoryBean.setLazyInit(true);
|
||||
factoryBean.setMongoOperations(operations);
|
||||
factoryBean.setRepositoryInterface(ContactRepository.class);
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
RepositoryFactorySupport factory = factoryBean.createRepositoryFactory();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Bundle-SymbolicName: org.springframework.data.mongodb
|
||||
Bundle-Name: Spring Data MongoDB Support
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-Vendor: Pivotal Software, Inc.
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
sun.reflect;version="0";resolution:=optional
|
||||
@@ -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="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,
|
||||
|
||||
@@ -34,15 +34,28 @@
|
||||
<firstname>Thomas</firstname>
|
||||
<surname>Darimont</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Christoph</firstname>
|
||||
<surname>Strobl</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<legalnotice>
|
||||
<para>Copies of this document may be made for your own use and for
|
||||
distribution to others, provided that you do not charge any fee for such
|
||||
copies and further provided that each copy contains this Copyright
|
||||
Notice, whether distributed in print or electronically.
|
||||
</para>
|
||||
distribution to others, provided that you do not charge any fee for such
|
||||
copies and further provided that each copy contains this Copyright
|
||||
Notice, whether distributed in print or electronically.</para>
|
||||
</legalnotice>
|
||||
|
||||
<pubdate />
|
||||
|
||||
<copyright>
|
||||
<year>2008-2014</year>
|
||||
|
||||
<holder> The original authors.</holder>
|
||||
</copyright>
|
||||
|
||||
<productname>Spring Data MongoDB - Reference Documentation</productname>
|
||||
</bookinfo>
|
||||
|
||||
<toc/>
|
||||
@@ -53,10 +66,9 @@
|
||||
<title>Introduction</title>
|
||||
|
||||
<xi:include href="introduction/introduction.xml"/>
|
||||
<xi:include href="introduction/why-sd-doc.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.7.0.RC1/src/docbkx/repositories.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.0.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repositories.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
@@ -76,10 +88,10 @@
|
||||
<part id="appendix">
|
||||
<title>Appendix</title>
|
||||
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.0.RC1/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.0.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.7.0.RC1/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.0.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repository-query-keywords-reference.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<title>Community Forum</title>
|
||||
|
||||
<para>The Spring Data <ulink
|
||||
url="http://forum.springframework.org/forumdisplay.php?f=80">forum
|
||||
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>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<para>Professional, from-the-source support, with guaranteed response
|
||||
time, is available from <ulink
|
||||
url="http://www.springsource.com">SpringSource</ulink>, the company
|
||||
url="http://gopivotal.com/">Pivotal Sofware, Inc.</ulink>, the company
|
||||
behind Spring Data and Spring.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -62,21 +62,26 @@
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <ulink url="http://www.mongodb.org/#">online shell</ulink>
|
||||
<para>The <ulink url="http://docs.mongodb.org/manual/">manual</ulink> introduces MongoDB and contains links to
|
||||
getting started guides, reference documentation and tutorials.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The <ulink url="http://try.mongodb.org/">online shell</ulink>
|
||||
provides a convenient way to interact with a MongoDB instance in
|
||||
combination with the online <ulink type=""
|
||||
url="http://www.mongodb.org/display/DOCS/Tutorial">tutorial.</ulink></para>
|
||||
url="http://docs.mongodb.org/manual/tutorial/getting-started/">tutorial.</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>MongoDB <ulink
|
||||
url="http://www.mongodb.org/display/DOCS/Java+Language+Center">Java
|
||||
url="http://docs.mongodb.org/ecosystem/drivers/java/">Java
|
||||
Language Center</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Several <ulink
|
||||
url="http://www.mongodb.org/display/DOCS/Books">books</ulink>
|
||||
url="http://www.mongodb.org/books">books</ulink>
|
||||
available for purchase</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<chapter id="requirements">
|
||||
<title>Requirements</title>
|
||||
|
||||
<para>Spring Data Document 1.x binaries requires JDK level 6.0 and above,
|
||||
<para>Spring Data MongoDB 1.x binaries requires JDK level 6.0 and above,
|
||||
and
|
||||
<ulink url="https://spring.io/docs">Spring Framework</ulink>
|
||||
3.0.x and above.
|
||||
3.2.x and above.
|
||||
</para>
|
||||
<para>
|
||||
In terms of document stores,
|
||||
<ulink url="http://www.mongodb.org/">MongoDB</ulink>
|
||||
preferably version 1.6.5
|
||||
or later or
|
||||
<ulink url="http://couchdb.apache.org/">CouchDB</ulink>
|
||||
1.0.1 or later are required.
|
||||
preferably version 2.4.
|
||||
</para>
|
||||
</chapter>
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="why-Spring Data-Document">
|
||||
<title>Why Spring Data - Document?</title>
|
||||
|
||||
<para>The Spring Framework is the leading full-stack Java/JEE application
|
||||
framework. It provides a lightweight container and a non-invasive
|
||||
programming model enabled by the use of dependency injection, AOP, and
|
||||
portable service abstractions.</para>
|
||||
|
||||
<para><ulink url="http://en.wikipedia.org/wiki/NoSQL">NoSQL</ulink> storages
|
||||
provide an alternative to classical RDBMS for horizontal scalability and
|
||||
speed. In terms of implementation, Document stores represent one of the most
|
||||
popular types of stores in the NoSQL space. The document database supported
|
||||
by Spring Data are MongoDB and CouchDB, though just MongoDB integration has
|
||||
been released to date.</para>
|
||||
|
||||
<para>The goal of the Spring Data Document (or DATADOC) framework is to
|
||||
provide an extension to the Spring programming model that supports writing
|
||||
applications that use Document databases. The Spring framework has always
|
||||
promoted a POJO programming model with a strong emphasis on portability and
|
||||
productivity. These values are caried over into Spring Data Document.
|
||||
</para>
|
||||
|
||||
<para>Notable features that are used in Spring Data Document from the Spring
|
||||
framework are the Features that particular, features from the Spring
|
||||
framework that are used are the Conversion Service, JMX Exporters, portable
|
||||
Data Access Exception hierarchy, Spring Expression Language, and Java based
|
||||
IoC container configuration. The programming model follows the familiar
|
||||
Spring 'template' style, so if you are familar with Spring template classes
|
||||
such as JdbcTemplate, JmsTemplate, RestTemplate, you will feel right at
|
||||
home. For example, MongoTemplate removes much of the boilerplate code you
|
||||
would have to write when using the MongoDB driver to save POJOs as well as a
|
||||
rich java based query interface to retrieve POJOs. The programming model
|
||||
also offers a new Repository approach in which the Spring container will
|
||||
provide an implementation of a Repository based soley off an interface
|
||||
definition which can also include custom finder methods.</para>
|
||||
</chapter>
|
||||
@@ -8,7 +8,7 @@
|
||||
data stores can be of different types. One might be relational while the
|
||||
other a document store. For this use case we have created a separate module
|
||||
in the MongoDB support that handles what we call cross-store support. The
|
||||
current implemenatation is based on JPA as the driver for the relational
|
||||
current implementation is based on JPA as the driver for the relational
|
||||
database and we allow select fields in the Entities to be stored in a Mongo
|
||||
database. In addition to allowing you to store your data in two stores we
|
||||
also coordinate persistence operations for the non-transactional MongoDB
|
||||
@@ -186,14 +186,14 @@
|
||||
<para>We are assuming that you have a working JPA application so we will
|
||||
only cover the additional steps needed to persist part of your Entity in
|
||||
your Mongo database. First you need to identify the field you want
|
||||
persited. It should be a domain class and follow the general rules for the
|
||||
persisted. It should be a domain class and follow the general rules for the
|
||||
Mongo mapping support covered in previous chapters. The field you want
|
||||
persisted in MongoDB should be annotated using the
|
||||
<classname>@RelatedDocument</classname> annotation. That is really all you
|
||||
need to do!. The cross-store aspects take care of the rest. This includes
|
||||
marking the field with @Transient so it won't be persisted using JPA,
|
||||
keeping track of any changes made to the field value and writing them to
|
||||
the database on succesfull transaction completion, loading the document
|
||||
the database on successful transaction completion, loading the document
|
||||
from MongoDB the first time the value is used in your application. Here is
|
||||
an example of a simple Entity that has a field annotated with
|
||||
@RelatedEntity.</para>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<chapter id="mapping-chapter">
|
||||
<title>Mapping</title>
|
||||
|
||||
<para>Rich maping support is provided by the
|
||||
<para>Rich mapping support is provided by the
|
||||
<classname>MongoMappingConverter</classname>.
|
||||
<classname>MongoMappingConverter</classname> has a rich metadata model that
|
||||
provides a full feature set of functionality to map domain objects to
|
||||
@@ -60,13 +60,13 @@
|
||||
<listitem>
|
||||
<para>You can have a single non-zero argument constructor whose
|
||||
constructor argument names match top level field names of document,
|
||||
that constructor will be used. Otherewise the zero arg constructor
|
||||
will be used. if there is more than one non-zero argument constructor
|
||||
an exception will be thrown.</para>
|
||||
that constructor will be used. Otherwise the zero arg constructor will
|
||||
be used. if there is more than one non-zero argument constructor an
|
||||
exception will be thrown.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<section>
|
||||
<section id="mapping.conventions.id-field">
|
||||
<title>How the '_id' field is handled in the mapping layer</title>
|
||||
|
||||
<para>MongoDB requires that you have an '_id' field for all documents.
|
||||
@@ -104,7 +104,7 @@
|
||||
the Java class it will be converted to and stored as an ObjectId if
|
||||
possible. ObjectId as a field type is also valid. If you specify a
|
||||
value for 'id' in your application, the conversion to an ObjectId is
|
||||
delected to the MongoDBdriver. If the specified 'id' value cannot be
|
||||
detected to the MongoDBdriver. If the specified 'id' value cannot be
|
||||
converted to an ObjectId, then the value will be stored as is in the
|
||||
document's _id field.</para>
|
||||
</listitem>
|
||||
@@ -172,12 +172,14 @@ public class GeoSpatialAppConfig extends AbstractMongoConfiguration {
|
||||
|
||||
// the following are optional
|
||||
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
|
||||
Set<Converter<?, ?>> converterList = new HashSet<Converter<?, ?>>();
|
||||
public CustomConversions customConversions() throws Exception {
|
||||
List<Converter<?, ?>> converterList = new ArrayList<Converter<?, ?>>();
|
||||
converterList.add(new org.springframework.data.mongodb.test.PersonReadConverter());
|
||||
converterList.add(new org.springframework.data.mongodb.test.PersonWriteConverter());
|
||||
converter.setCustomConverters(converterList);
|
||||
return new CustomConversions(converterList);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -454,7 +456,7 @@ public class Person<T extends Address> {
|
||||
<section id="mapping-custom-object-construction">
|
||||
<title>Customized Object Construction</title>
|
||||
|
||||
<para>The Mapping Subsystem allows the customization of the object
|
||||
<para>The mapping subsystem allows the customization of the object
|
||||
construction by annotating a constructor with the
|
||||
<literal>@PersistenceConstructor</literal> annotation. The values to be
|
||||
used for the constructor parameters are resolved in the following
|
||||
@@ -487,9 +489,9 @@ public class Person<T extends Address> {
|
||||
|
||||
<programlisting language="java">class OrderItem {
|
||||
|
||||
@Id String id;
|
||||
int quantity;
|
||||
double unitPrice;
|
||||
private @Id String id;
|
||||
private int quantity;
|
||||
private double unitPrice;
|
||||
|
||||
OrderItem(String id, @Value("#root.qty ?: 0") int quantity, double unitPrice) {
|
||||
this.id = id;
|
||||
@@ -642,20 +644,17 @@ public class Person {
|
||||
3 Type Conversion</ulink>.</para>
|
||||
</note>
|
||||
|
||||
<para>The <methodname>setConverters</methodname> method on
|
||||
<classname>SimpleMongoConverter</classname> and
|
||||
<classname>MappingMongoConverter</classname> should be used for this
|
||||
purpose. The method
|
||||
<methodname>afterMappingMongoConverterCreation</methodname> in
|
||||
<classname>AbstractMongoConfiguration</classname> can be overriden to
|
||||
configure a MappingMongoConverter. The examples <link
|
||||
linkend="???">here</link> at the begining of this chapter show how to
|
||||
perform the configuration using Java and XML.</para>
|
||||
<para>The method <methodname>customConversions</methodname> in
|
||||
<classname>AbstractMongoConfiguration</classname> can be used to
|
||||
configure Converters. The examples <link
|
||||
linkend="mapping-configuration">here</link> at the begining of this
|
||||
chapter show how to perform the configuration using Java and XML.</para>
|
||||
|
||||
<para>Below is an example of a Spring Converter implementation that
|
||||
converts from a DBObject to a Person POJO.</para>
|
||||
|
||||
<programlisting language="java">public class PersonReadConverter implements Converter<DBObject, Person> {
|
||||
<programlisting language="java">@ReadingConverter
|
||||
public class PersonReadConverter implements Converter<DBObject, Person> {
|
||||
|
||||
public Person convert(DBObject source) {
|
||||
Person p = new Person((ObjectId) source.get("_id"), (String) source.get("name"));
|
||||
@@ -668,7 +667,8 @@ public class Person {
|
||||
<para>Here is an example that converts from a Person to a
|
||||
DBObject.</para>
|
||||
|
||||
<programlisting language="java">public class PersonWriteConverter implements Converter<Person, DBObject> {
|
||||
<programlisting language="java">@WritingConverter
|
||||
public class PersonWriteConverter implements Converter<Person, DBObject> {
|
||||
|
||||
public DBObject convert(Person source) {
|
||||
DBObject dbo = new BasicDBObject();
|
||||
@@ -679,8 +679,6 @@ public class Person {
|
||||
}
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para/>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -225,6 +225,15 @@ public class PersonRepositoryTests {
|
||||
<entry><code>{"age" : {"$gt" : age}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>GreaterThanEqual</literal></entry>
|
||||
|
||||
<entry><methodname>findByAgeGreaterThanEqual(int
|
||||
age)</methodname></entry>
|
||||
|
||||
<entry><code>{"age" : {"$gte" : age}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>LessThan</literal></entry>
|
||||
|
||||
@@ -234,6 +243,15 @@ public class PersonRepositoryTests {
|
||||
<entry><code>{"age" : {"$lt" : age}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>LessThanEqual</literal></entry>
|
||||
|
||||
<entry><methodname>findByAgeLessThanEqual(int
|
||||
age)</methodname></entry>
|
||||
|
||||
<entry><code>{"age" : {"$lte" : age}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>Between</literal></entry>
|
||||
|
||||
@@ -243,6 +261,24 @@ public class PersonRepositoryTests {
|
||||
<entry><code>{"age" : {"$gt" : from, "$lt" : to}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>In</literal></entry>
|
||||
|
||||
<entry><methodname>findByAgeIn(Collection ages)
|
||||
</methodname></entry>
|
||||
|
||||
<entry><code>{"age" : {"$in" : [ages...]}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>NotIn</literal></entry>
|
||||
|
||||
<entry><methodname>findByAgeNotIn(Collection ages)
|
||||
</methodname></entry>
|
||||
|
||||
<entry><code>{"age" : {"$nin" : [ages...]}}</code></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal>IsNotNull</literal>,
|
||||
<literal>NotNull</literal></entry>
|
||||
@@ -358,7 +394,7 @@ public class PersonRepositoryTests {
|
||||
</tgroup>
|
||||
</table></para>
|
||||
|
||||
<section>
|
||||
<section id="mongodb.repositories.queries.geo-spatial">
|
||||
<title>Geo-spatial repository queries</title>
|
||||
|
||||
<para>As you've just seen there are a few keywords triggering
|
||||
@@ -418,7 +454,7 @@ Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
</simplesect>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mongodb.repositories.queries.json-based">
|
||||
<title>MongoDB JSON based query methods and field restriction</title>
|
||||
|
||||
<para>By adding the annotation
|
||||
@@ -452,7 +488,7 @@ Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
set and its value will therefore be null.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mongodb.repositories.queries.type-safe">
|
||||
<title>Type-safe Query methods</title>
|
||||
|
||||
<para>MongoDB repository support integrates with the <ulink
|
||||
@@ -529,7 +565,7 @@ Page<Person> page = repository.findAll(person.lastname.contains("a"),
|
||||
</programlisting>
|
||||
|
||||
<para>To use this in your repository implementation, simply inherit from
|
||||
it in additiion to other repository interfaces. This is shown
|
||||
it in addition to other repository interfaces. This is shown
|
||||
below</para>
|
||||
|
||||
<programlisting lang="" language="java">public interface PersonRepository extends MongoRepository<Person, String>, QueryDslPredicateExecutor<Person> {
|
||||
@@ -543,12 +579,12 @@ Page<Person> page = repository.findAll(person.lastname.contains("a"),
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mongodb.repositories.misc">
|
||||
<title>Miscellaneous</title>
|
||||
|
||||
<para/>
|
||||
|
||||
<section>
|
||||
<section id="mongodb.repositories.misc.cdi-integration">
|
||||
<title>CDI Integration</title>
|
||||
|
||||
<para>Instances of the repository interfaces are usually created by a
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Automatic implementatin of Repository interfaces including support
|
||||
for custom finder methods.</para>
|
||||
<para>Automatic implementation of Repository interfaces including
|
||||
support for custom finder methods.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
@@ -81,20 +81,20 @@
|
||||
<title>Getting Started</title>
|
||||
|
||||
<para>Spring MongoDB support requires MongoDB 1.4 or higher and Java SE 5
|
||||
or higher. The latest production release (2.0.x as of this writing) is
|
||||
or higher. The latest production release (2.4.9 as of this writing) is
|
||||
recommended. An easy way to bootstrap setting up a working environment is
|
||||
to create a Spring based project in <ulink
|
||||
url="http://www.springsource.com/developer/sts">STS</ulink>.</para>
|
||||
url="http://spring.io/tools/sts">STS</ulink>.</para>
|
||||
|
||||
<para>First you need to set up a running Mongodb server. Refer to the
|
||||
<ulink url="http://www.mongodb.org/display/DOCS/Quickstart">Mongodb Quick
|
||||
Start guide</ulink> for an explanation on how to startup a MongoDB
|
||||
<ulink url="http://docs.mongodb.org/manual/core/introduction/">Mongodb
|
||||
Quick Start guide</ulink> for an explanation on how to startup a MongoDB
|
||||
instance. Once installed starting MongoDB is typically a matter of
|
||||
executing the following command:
|
||||
<literal>MONGO_HOME/bin/mongod</literal></para>
|
||||
|
||||
<para>To create a Spring project in STS go to File -> New -> Spring
|
||||
Template Project -> Simple Spring Utility Project --> press Yes when
|
||||
Template Project -> Simple Spring Utility Project -> press Yes when
|
||||
prompted. Then enter a project and a package name such as
|
||||
org.spring.mongodb.example.</para>
|
||||
|
||||
@@ -107,14 +107,14 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.1.0.RELEASE</version>
|
||||
<version>1.3.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies></programlisting>
|
||||
|
||||
<para>Also change the version of Spring in the pom.xml to be</para>
|
||||
|
||||
<programlisting lang="" language="xml"><spring.framework.version>3.1.2.RELEASE</spring.framework.version></programlisting>
|
||||
<programlisting lang="" language="xml"><spring.framework.version>3.2.8.RELEASE</spring.framework.version></programlisting>
|
||||
|
||||
<para>You will also need to add the location of the Spring Milestone
|
||||
repository for maven to your pom.xml which is at the same level of your
|
||||
@@ -124,7 +124,7 @@
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<name>Spring Maven MILESTONE Repository</name>
|
||||
<url>http://repo.springsource.org/libs-milestone</url>
|
||||
<url>http://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories></programlisting>
|
||||
|
||||
@@ -310,7 +310,7 @@ public class AppConfig {
|
||||
classes annoated with the <literal>@Repository</literal> annotation.
|
||||
This hierarchy and use of <literal>@Repository</literal> is described in
|
||||
<ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/dao.html">Spring's
|
||||
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html">Spring's
|
||||
DAO support features</ulink>.</para>
|
||||
|
||||
<para>An example of a Java based bean metadata that supports exception
|
||||
@@ -434,7 +434,7 @@ public class AppConfig {
|
||||
DB getDb(String dbName) throws DataAccessException;
|
||||
}</programlisting>
|
||||
|
||||
<para>The following sections show how you can use the contiainer with
|
||||
<para>The following sections show how you can use the container with
|
||||
either Java or the XML based metadata to configure an instance of the
|
||||
<classname>MongoDbFactory</classname> interface. In turn, you can use
|
||||
the <classname>MongoDbFactory</classname> instance to configure
|
||||
@@ -970,7 +970,7 @@ DEBUG work.data.mongodb.core.MongoTemplate: 376 - Dropped collection [database.p
|
||||
functionality</para>
|
||||
</note>
|
||||
|
||||
<para>The query stynax used in the example is explained in more detail in
|
||||
<para>The query syntax used in the example is explained in more detail in
|
||||
the section <link linkend="mongo.query">Querying Documents</link>.</para>
|
||||
|
||||
<section id="mongo-template.id-handling">
|
||||
@@ -1079,7 +1079,7 @@ mongoTemplate.save(sample);
|
||||
</example>
|
||||
|
||||
<para>As you can see we store the type information for the actual root
|
||||
class persistet as well as for the nested type as it is complex and a
|
||||
class persistent as well as for the nested type as it is complex and a
|
||||
subtype of <classname>Contact</classname>. So if you're now using
|
||||
<methodname>mongoTemplate.findAll(Object.class, "sample")</methodname>
|
||||
we are able to find out that the document stored shall be a
|
||||
@@ -1197,8 +1197,8 @@ class SampleMongoConfiguration extends AbstractMongoConfiguration {
|
||||
overriden using mapping metadata.</para>
|
||||
|
||||
<para>When inserting or saving, if the Id property is not set, the
|
||||
assumption is that its value will be autogenerated by the database. As
|
||||
such, for autogeneration of an ObjectId to succeed the type of the Id
|
||||
assumption is that its value will be auto-generated by the database. As
|
||||
such, for auto-generation of an ObjectId to succeed the type of the Id
|
||||
property/field in your class must be either a
|
||||
<classname>String</classname>, <classname>ObjectId</classname>, or
|
||||
<classname>BigInteger</classname>.</para>
|
||||
@@ -1284,14 +1284,14 @@ Person qp = mongoTemplate.findOne(query(where("age").is(33)), Person.class);
|
||||
<listitem>
|
||||
<para><emphasis role="bold">insertAll</emphasis> Takes a
|
||||
<literal>Collection </literal>of objects as the first parameter.
|
||||
This method ispects each object and inserts it to the
|
||||
This method inspects each object and inserts it to the
|
||||
appropriate collection based on the rules specified
|
||||
above.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><emphasis role="bold">save</emphasis> Save the object
|
||||
ovewriting any object that might exist with the same id.</para>
|
||||
overwriting any object that might exist with the same id.</para>
|
||||
</listitem>
|
||||
</itemizedlist></para>
|
||||
</section>
|
||||
@@ -1371,7 +1371,7 @@ import static org.springframework.data.mongodb.core.query.Update;
|
||||
<title>Methods for the Update class</title>
|
||||
|
||||
<para>The Update class can be used with a little 'syntax sugar' as its
|
||||
methods are meant to be chained together and you can kickstart the
|
||||
methods are meant to be chained together and you can kick-start the
|
||||
creation of a new Update instance via the static method
|
||||
<literal>public static Update update(String key, Object
|
||||
value)</literal> and using static imports.</para>
|
||||
@@ -1450,7 +1450,7 @@ import static org.springframework.data.mongodb.core.query.Update;
|
||||
<section id="mongo-template.upserts">
|
||||
<title>Upserting documents in a collection</title>
|
||||
|
||||
<para>Related to perfomring an <methodname>updateFirst</methodname>
|
||||
<para>Related to performing an <methodname>updateFirst</methodname>
|
||||
operations, you can also perform an upsert operation which will perform
|
||||
an insert if no document is found that matches the query. The document
|
||||
that is inserted is a combination of the query document and the update
|
||||
@@ -1459,7 +1459,7 @@ import static org.springframework.data.mongodb.core.query.Update;
|
||||
<programlisting>template.upsert(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update")), update("address", addr), Person.class);</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mongo-template.find-and-upsert">
|
||||
<title>Finding and Upserting documents in a collection</title>
|
||||
|
||||
<para>The <methodname>findAndModify(…)</methodname> method on
|
||||
@@ -1617,7 +1617,7 @@ List<Person> result = mongoTemplate.find(query(where("age").lt(50)
|
||||
</emphasis> <literal>(String key) </literal>Adds a chained
|
||||
<classname>Criteria</classname> with the specified
|
||||
<literal>key</literal> to the current
|
||||
<classname>Criteria</classname> and retuns the newly created
|
||||
<classname>Criteria</classname> and returns the newly created
|
||||
one</para>
|
||||
</listitem>
|
||||
|
||||
@@ -2029,7 +2029,7 @@ GeoResults<Restaurant> = operations.geoNear(query, Restaurant.class);</pro
|
||||
methods on MongoOperations to simplify the creation and execution of
|
||||
Map-Reduce operations. It can convert the results of a Map-Reduce
|
||||
operation to a POJO also integrates with Spring's <ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/resources.html">Resource
|
||||
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html">Resource
|
||||
abstraction</ulink> abstraction. This will let you place your JavaScript
|
||||
files on the file system, classpath, http server or any other Spring
|
||||
Resource implementation and then reference the JavaScript resources via an
|
||||
@@ -2143,7 +2143,7 @@ MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "
|
||||
providing methods on MongoOperations to simplify the creation and
|
||||
execution of group operations. It can convert the results of the group
|
||||
operation to a POJO and also integrates with Spring's <ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/resources.html">Resource
|
||||
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html">Resource
|
||||
abstraction</ulink> abstraction. This will let you place your JavaScript
|
||||
files on the file system, classpath, http server or any other Spring
|
||||
Resource implementation and then reference the JavaScript resources via an
|
||||
@@ -2156,7 +2156,7 @@ MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "
|
||||
<title>Example Usage</title>
|
||||
|
||||
<para>In order to understand how group operations work the following
|
||||
example is used, which is somewhat artifical. For a more realistic
|
||||
example is used, which is somewhat artificial. For a more realistic
|
||||
example consult the book 'MongoDB - The definitive guide'. A collection
|
||||
named "group_test_collection" created with the following rows.</para>
|
||||
|
||||
@@ -2232,7 +2232,7 @@ MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "
|
||||
}
|
||||
}</programlisting>
|
||||
|
||||
<para>You can also obtain tha raw result as a
|
||||
<para>You can also obtain the raw result as a
|
||||
<classname>DbObject</classname> by calling the method
|
||||
<methodname>getRawResults</methodname> on the
|
||||
<classname>GroupByResults</classname> class.</para>
|
||||
@@ -2318,9 +2318,9 @@ GroupByResults<XObject> results = mongoTemplate.group(where("x").gt(0),
|
||||
|
||||
<para><classname>AggregationResults</classname> is the container for
|
||||
the result of an aggregate operation. It provides access to the raw
|
||||
aggreation result in the form of an <classname>DBObject</classname>,
|
||||
to the mapped objects and information which performed the
|
||||
aggregation.</para>
|
||||
aggregation result in the form of an
|
||||
<classname>DBObject</classname>, to the mapped objects and
|
||||
information which performed the aggregation.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
@@ -2344,8 +2344,8 @@ List<OutputType> mappedResult = results.getMappedResults();
|
||||
<classname>MongoTemplate</classname> will derive the name of the input
|
||||
collection from this class. Otherwise if you don't not specify an input
|
||||
class you must provide the name of the input collection explicitly. If
|
||||
an input-class and an explicity input-collection is provided the latter
|
||||
takes precedence.</para>
|
||||
an input-class and an input-collection is provided the latter takes
|
||||
precedence.</para>
|
||||
</section>
|
||||
|
||||
<section id="mongo.aggregation.supported-aggregation-operations">
|
||||
@@ -2460,7 +2460,7 @@ project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, b
|
||||
documentation.</para>
|
||||
|
||||
<section id="mongo.aggregation.projection.expressions">
|
||||
<title>Spring Expresison Support in Projection Expressions</title>
|
||||
<title>Spring Expression Support in Projection Expressions</title>
|
||||
|
||||
<para>As of Version 1.4.0 we support the use of SpEL expression in
|
||||
projection expressions via the <methodname>andExpression</methodname>
|
||||
@@ -2471,7 +2471,7 @@ project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, b
|
||||
calculations.</para>
|
||||
|
||||
<example>
|
||||
<title>Complex calulations with SpEL expressions</title>
|
||||
<title>Complex calculations with SpEL expressions</title>
|
||||
|
||||
<para>The following SpEL expression:</para>
|
||||
|
||||
@@ -2499,14 +2499,14 @@ project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, b
|
||||
<section id="mongo.aggregation.examples">
|
||||
<title>Aggregation Framework Examples</title>
|
||||
|
||||
<para>The follwing examples demonstrate the usage patterns for the
|
||||
<para>The following examples demonstrate the usage patterns for the
|
||||
MongoDB Aggregation Framework with Spring Data MongoDB.</para>
|
||||
|
||||
<example id="mongo.aggregation.examples.example1">
|
||||
<title>Aggregation Framework Example 1</title>
|
||||
|
||||
<para>In this introductory example we want to aggregate a list of tags
|
||||
to get the occurence count of a particular tag from a MongoDB
|
||||
to get the occurrence count of a particular tag from a MongoDB
|
||||
collection called <code>"tags"</code> sorted by the occurrence count
|
||||
in descending order. This example demonstrates the usage of grouping,
|
||||
sorting, projections (selection) and unwinding (result
|
||||
@@ -2555,7 +2555,7 @@ List<TagCount> tagCount = results.getMappedResults();</programlisting>
|
||||
<listitem>
|
||||
<para>In the forth step we use the <methodname>group</methodname>
|
||||
operation to define a group for each <code>"tags"</code>-value for
|
||||
which we aggregate the occurence count via the
|
||||
which we aggregate the occurrence count via the
|
||||
<methodname>count</methodname> aggregation operator and collect the
|
||||
result in a new field called <code>"n"</code>.</para>
|
||||
</listitem>
|
||||
@@ -2569,7 +2569,7 @@ List<TagCount> tagCount = results.getMappedResults();</programlisting>
|
||||
|
||||
<listitem>
|
||||
<para>As the sixth step we sort the resulting list of tags by their
|
||||
occurence count in descending order via the
|
||||
occurrence count in descending order via the
|
||||
<methodname>sort</methodname> operation.</para>
|
||||
</listitem>
|
||||
|
||||
@@ -2687,7 +2687,7 @@ ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);
|
||||
<para>As the forth step we select the <code>"state"</code> field
|
||||
from the previous <methodname>group</methodname> operation. Note
|
||||
that <code>"state"</code> again implicitly references an group-id
|
||||
field. As we do not want an implict generated id to appear, we
|
||||
field. As we do not want an implicit generated id to appear, we
|
||||
exclude the id from the previous operation via
|
||||
<code>and(previousOperation()).exclude()</code>. As we want to
|
||||
populate the nested <classname>City</classname> structures in our
|
||||
@@ -2885,7 +2885,7 @@ List<DBObject> resultList = result.getMappedResults();</programlisting>
|
||||
<note>
|
||||
<para>For more information on the Spring type conversion service see the
|
||||
reference docs <ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/reference/validation.html#core-convert">here</ulink>.</para>
|
||||
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">here</ulink>.</para>
|
||||
</note>
|
||||
|
||||
<section id="mongo.custom-converters.writer">
|
||||
@@ -2916,7 +2916,7 @@ public class PersonWriteConverter implements Converter<Person, DBObject> {
|
||||
<section id="mongo.custom-converters.reader">
|
||||
<title>Reading using a Spring Converter</title>
|
||||
|
||||
<para>An example implemention of a Converter that converts from a
|
||||
<para>An example implementation of a Converter that converts from a
|
||||
DBObject ot a Person object is shownn below</para>
|
||||
|
||||
<programlisting language="java">public class PersonReadConverter implements Converter<DBObject, Person> {
|
||||
@@ -2968,7 +2968,7 @@ public class PersonWriteConverter implements Converter<Person, DBObject> {
|
||||
</mongo:mapping-converter></programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mongo.converter-disambiguation">
|
||||
<title>Converter disambiguation</title>
|
||||
|
||||
<para>Generally we inspect the <interfacename>Converter</interfacename>
|
||||
@@ -2999,12 +2999,12 @@ class MyConverter implements Converter<String, Person> { … }</programlis
|
||||
</section>
|
||||
|
||||
<section id="mongo-template.index-and-collections">
|
||||
<title>Index and Collection managment</title>
|
||||
<title>Index and Collection management</title>
|
||||
|
||||
<para><classname>MongoTemplate</classname> provides a few methods for
|
||||
managing indexes and collections. These are collected into a helper
|
||||
interface called <interfacename>IndexOperations</interfacename>. You
|
||||
access these operations by calilng the method
|
||||
access these operations by calling the method
|
||||
<methodname>indexOps</methodname> and pass in either the collection name
|
||||
or the <literal>java.lang.Class</literal> of your entity (the collection
|
||||
name will be derived from the .class either by name or via annotation
|
||||
@@ -3244,11 +3244,11 @@ mongoTemplate.dropCollection("MyNewCollection"); </programlisting>
|
||||
interface.</para>
|
||||
|
||||
<para>The motivation behind mapping to Spring's <ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/reference/dao.html#dao-exceptions">consistent
|
||||
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions">consistent
|
||||
data access exception hierarchy</ulink> is that you are then able to write
|
||||
portable and descriptive exception handling code without resorting to
|
||||
coding against <ulink
|
||||
url="http://www.mongodb.org/display/DOCS/Error+Codes">MongoDB error
|
||||
url="http://www.mongodb.org/about/contributors/error-codes/">MongoDB error
|
||||
codes</ulink>. All of Spring's data access exceptions are inherited from
|
||||
the root <classname>DataAccessException</classname> class so you can be
|
||||
sure that you will be able to catch all database related exception within
|
||||
@@ -3390,8 +3390,8 @@ mongoTemplate.dropCollection("MyNewCollection"); </programlisting>
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
|
||||
<para>You can no get the template injected and perform storing and
|
||||
retrieving operations to it.</para>
|
||||
<para>The template can now be injected and used to perform storage and
|
||||
retrieval operations.</para>
|
||||
|
||||
<example>
|
||||
<title>Using GridFsTemplate to store files</title>
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.4.0.RELEASE (2014-02-24)
|
||||
---------------------------------------------
|
||||
|
||||
** Fix
|
||||
* [DATAMONGO-354] - MongoTemplate should support multiple $pushAll in one update.
|
||||
* [DATAMONGO-404] - Removing a DBRef using pull does not work.
|
||||
* [DATAMONGO-410] - Update with pushAll should recognize defined Converter.
|
||||
* [DATAMONGO-812] - $pushAll is deprecated since mongodb 2.4 move to $push $each.
|
||||
* [DATAMONGO-830] - Fix NPE during cache warmup in CustomConversions.
|
||||
* [DATAMONGO-838] - Support for refering to expression based field in group operation.
|
||||
* [DATAMONGO-840] - Support for nested MongoDB field references in SpEL expressions within Projections.
|
||||
* [DATAMONGO-842] - Fix documentation error in GRIDFS section.
|
||||
* [DATAMONGO-852] - Increase version for update should traverse DBObject correctly in order to find version property.
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-468] - Simplification for updates of DBRef fields with mongoTemplate.
|
||||
* [DATAMONGO-849] - Documentation on github should not reference invalid class.
|
||||
|
||||
** Task
|
||||
* [DATAMONGO-848] - Ensure compatibility with Mongo Java driver 2.12.
|
||||
* [DATAMONGO-853] - Update no longer allows null keys.
|
||||
* [DATAMONGO-856] - Update documentation.
|
||||
|
||||
Changes in version 1.3.4.RELEASE (2014-02-17)
|
||||
---------------------------------------------
|
||||
** Bug
|
||||
* [DATAMONGO-407] - Collection with generics losing element type after $set update
|
||||
* [DATAMONGO-410] - Update with pushAll doesnt recognize defined Converter
|
||||
* [DATAMONGO-686] - ClassCastException while reusing Query object
|
||||
* [DATAMONGO-805] - Excluding DBRef field in a query causes a MappingException
|
||||
* [DATAMONGO-807] - using findAndModify removes the _class field of encapsulated classes, causing MappingInstantiationException
|
||||
* [DATAMONGO-808] - Spring data mongoDB not working with IPv6 address directly
|
||||
* [DATAMONGO-811] - updateFirst methods do not increment @Version field
|
||||
* [DATAMONGO-816] - Unable to execute query with DocumentCallbackHandler when query contains Criteria with enums.
|
||||
* [DATAMONGO-828] - UpdateFirst throws OptimisticLockingFailureException when updating document that does not exist
|
||||
* [DATAMONGO-830] - NPE during cache warmup in CustomConversions
|
||||
* [DATAMONGO-842] - Documentation error in GRIDFS section
|
||||
** Improvement
|
||||
* [DATAMONGO-813] - GridFsTemplate.getResource(location) throws NPE if don't find file
|
||||
** Task
|
||||
* [DATAMONGO-824] - Add contribution guidelines
|
||||
* [DATAMONGO-846] - Release 1.3.4
|
||||
|
||||
Changes in version 1.4.0.RC1 (2014-01-29)
|
||||
---------------------------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Spring Data MongoDB 1.4.0 RC1
|
||||
Copyright (c) [2010-2014] Pivotal Inc.
|
||||
Spring Data MongoDB 1.4.0.RELEASE
|
||||
Copyright (c) [2010-2014] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
You may not use this product except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SPRING DATA MongoDB 1.4.0.RC1
|
||||
-----------------------------
|
||||
SPRING DATA MongoDB 1.4.0.RELEASE
|
||||
---------------------------------
|
||||
|
||||
Spring Data MongoDB is released under the terms of the Apache Software License Version 2.0 (see license.txt).
|
||||
|
||||
@@ -13,5 +13,5 @@ The reference manual and javadoc are located in the 'docs' directory.
|
||||
|
||||
ADDITIONAL RESOURCES:
|
||||
|
||||
Spring Data Homepage: http://www.springsource.org/spring-data
|
||||
Spring Data Forum: http://forum.springsource.org/forumdisplay.php?f=80
|
||||
Spring Data Homepage: http://projects.spring.io/spring-data
|
||||
Spring Data Forum: http://forum.spring.io/forum/spring-projects/data/nosql
|
||||
|
||||
Reference in New Issue
Block a user