Compare commits
98 Commits
issue/arch
...
1.9.7.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00d9da3027 | ||
|
|
53eaaa02a0 | ||
|
|
315b642b3b | ||
|
|
958752e72f | ||
|
|
6b1dbe372e | ||
|
|
a644187131 | ||
|
|
681a4f9855 | ||
|
|
f2be1b2ca9 | ||
|
|
3c203eba8e | ||
|
|
f3b0665d94 | ||
|
|
9f43d3fc5a | ||
|
|
95985fffc8 | ||
|
|
6c6ac6da5b | ||
|
|
c1ac8767b7 | ||
|
|
96068eb0e2 | ||
|
|
36d2e0942b | ||
|
|
b585783b75 | ||
|
|
3924b6f12a | ||
|
|
2674880946 | ||
|
|
6c6f953a42 | ||
|
|
772e8ac85e | ||
|
|
2bbffed62b | ||
|
|
685990bdd6 | ||
|
|
ff83ac3fb4 | ||
|
|
6827a09f26 | ||
|
|
a5148f89c1 | ||
|
|
995a680823 | ||
|
|
9f0abb69fd | ||
|
|
d65eebe9c3 | ||
|
|
ca4f1f1b7c | ||
|
|
46b119ce71 | ||
|
|
fc0dd7d094 | ||
|
|
712d8be7bb | ||
|
|
536dcc14ca | ||
|
|
dc44c3a455 | ||
|
|
8e90366712 | ||
|
|
ed36fd7260 | ||
|
|
31a6a74743 | ||
|
|
001ff508b5 | ||
|
|
6882fa9d10 | ||
|
|
91eaae0ef6 | ||
|
|
785dc6ab78 | ||
|
|
f011a9a4ee | ||
|
|
c6c58050e7 | ||
|
|
5fce8bcac6 | ||
|
|
2f522bae5c | ||
|
|
2e6f91924d | ||
|
|
15f7a9c74a | ||
|
|
49f52f0258 | ||
|
|
396ea471fb | ||
|
|
eef17dd000 | ||
|
|
84dc03b9d1 | ||
|
|
0ce220d54f | ||
|
|
b693136396 | ||
|
|
3c117db43b | ||
|
|
075ccb1d00 | ||
|
|
e3bddd1c19 | ||
|
|
22b113ce64 | ||
|
|
0f5e91b091 | ||
|
|
557a528690 | ||
|
|
762569c826 | ||
|
|
ad7d82f521 | ||
|
|
04deaacbec | ||
|
|
ec443f2b5e | ||
|
|
f1b04ff354 | ||
|
|
62dd7d070a | ||
|
|
5df92a86a3 | ||
|
|
2ca3df1ff4 | ||
|
|
a8751249fd | ||
|
|
19abff826e | ||
|
|
5f199cf81f | ||
|
|
eb26b78a19 | ||
|
|
3d0053c61a | ||
|
|
7b15d246e8 | ||
|
|
b75f4a2834 | ||
|
|
d6ac4c6df5 | ||
|
|
02f56c88f5 | ||
|
|
cd35b9ed2a | ||
|
|
e8944a6c3a | ||
|
|
6fcbc225eb | ||
|
|
f06eda488c | ||
|
|
0ef910445d | ||
|
|
b22ee9d27c | ||
|
|
859a0e83c8 | ||
|
|
b35f151b80 | ||
|
|
17afb07e45 | ||
|
|
b407963344 | ||
|
|
8b31ba1836 | ||
|
|
0cf6edae43 | ||
|
|
0824105377 | ||
|
|
dc936a5b7b | ||
|
|
c8fe02e48e | ||
|
|
32547db306 | ||
|
|
41902154ca | ||
|
|
2354ced1bf | ||
|
|
791cc3a1b8 | ||
|
|
021c03fbbf | ||
|
|
e4a59f29d0 |
20
.travis.yml
20
.travis.yml
@@ -3,13 +3,29 @@ language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
services:
|
||||
- mongodb
|
||||
before_script:
|
||||
- mongod --version
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- PROFILE=ci
|
||||
- PROFILE=mongo-next
|
||||
- PROFILE=mongo3
|
||||
- PROFILE=mongo3-next
|
||||
- PROFILE=mongo31
|
||||
- PROFILE=mongo32
|
||||
- PROFILE=mongo33
|
||||
- PROFILE=mongo34-next
|
||||
|
||||
# Current MongoDB version is 2.4.2 as of 2016-04, see https://github.com/travis-ci/travis-ci/issues/3694
|
||||
# apt-get starts a MongoDB instance so it's not started using before_script
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- mongodb-3.2-precise
|
||||
packages:
|
||||
- mongodb-org-server
|
||||
- mongodb-org-shell
|
||||
|
||||
sudo: false
|
||||
|
||||
|
||||
41
pom.xml
41
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.9.0.RELEASE</version>
|
||||
<version>1.9.7.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,7 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.8.0.RELEASE</version>
|
||||
<version>1.8.7.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -28,8 +28,8 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.12.0.RELEASE</springdata.commons>
|
||||
<mongo>2.14.0</mongo>
|
||||
<springdata.commons>1.12.7.RELEASE</springdata.commons>
|
||||
<mongo>2.14.3</mongo>
|
||||
<mongo.osgi>2.13.0</mongo.osgi>
|
||||
</properties>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.14.0-SNAPSHOT</mongo>
|
||||
<mongo>2.15.0-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -148,16 +148,41 @@
|
||||
|
||||
<id>mongo31</id>
|
||||
<properties>
|
||||
<mongo>3.1.0</mongo>
|
||||
<mongo>3.1.1</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo32-next</id>
|
||||
<id>mongo32</id>
|
||||
<properties>
|
||||
<mongo>3.2.0-SNAPSHOT</mongo>
|
||||
<mongo>3.2.2</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo33</id>
|
||||
<properties>
|
||||
<mongo>3.3.0</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mongo-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo34-next</id>
|
||||
<properties>
|
||||
<mongo>3.4.0-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.9.0.RELEASE</version>
|
||||
<version>1.9.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.9.0.RELEASE</version>
|
||||
<version>1.9.7.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.9.0.RELEASE</version>
|
||||
<version>1.9.7.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.9.0.RELEASE</version>
|
||||
<version>1.9.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -30,6 +30,7 @@ import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
@@ -37,6 +38,7 @@ import com.mongodb.WriteConcern;
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @auhtor Christoph Strobl
|
||||
*/
|
||||
public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
|
||||
@@ -58,8 +60,8 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
protected String collectionPattern = "%c";
|
||||
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
|
||||
protected String applicationId = System.getProperty("APPLICATION_ID", null);
|
||||
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE;
|
||||
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
|
||||
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.ACKNOWLEDGED;
|
||||
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.UNACKNOWLEDGED;
|
||||
protected Mongo mongo;
|
||||
protected DB db;
|
||||
|
||||
@@ -128,7 +130,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
}
|
||||
|
||||
protected void connectToMongo() throws UnknownHostException {
|
||||
this.mongo = new Mongo(host, port);
|
||||
this.mongo = new MongoClient(host, port);
|
||||
this.db = mongo.getDB(database);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,37 +22,44 @@ import java.util.Calendar;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.MDC;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoLog4jAppender}.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoLog4jAppenderIntegrationTests {
|
||||
|
||||
static final String NAME = MongoLog4jAppenderIntegrationTests.class.getName();
|
||||
|
||||
private static final Logger log = Logger.getLogger(NAME);
|
||||
Mongo mongo;
|
||||
MongoClient mongo;
|
||||
DB db;
|
||||
String collection;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
mongo = new Mongo("localhost", 27017);
|
||||
mongo = new MongoClient("localhost", 27017);
|
||||
db = mongo.getDB("logs");
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
|
||||
db.getCollection(collection).drop();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
db.getCollection(collection).remove(new BasicDBObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,7 +71,6 @@ public class MongoLog4jAppenderIntegrationTests {
|
||||
log.error("ERROR message");
|
||||
|
||||
DBCursor msgs = db.getCollection(collection).find();
|
||||
|
||||
assertThat(msgs.count(), is(4));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.9.0.RELEASE</version>
|
||||
<version>1.9.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2016 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,24 +15,24 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.*;
|
||||
import static org.springframework.data.mongodb.config.BeanNames.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.auditing.config.AuditingConfiguration;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
|
||||
import org.springframework.data.support.IsNewStrategyFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,6 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
defaultDependenciesIfNecessary(registry, annotationMetadata);
|
||||
super.registerBeanDefinitions(annotationMetadata, registry);
|
||||
}
|
||||
|
||||
@@ -85,7 +84,11 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
|
||||
builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
|
||||
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContextLookup.class);
|
||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
|
||||
builder.addConstructorArgValue(definition.getBeanDefinition());
|
||||
return configureDefaultAuditHandlerAttributes(configuration, builder);
|
||||
}
|
||||
|
||||
@@ -102,29 +105,58 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
|
||||
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AuditingEventListener.class);
|
||||
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
|
||||
getAuditingHandlerBeanName(), registry));
|
||||
listenerBeanDefinitionBuilder
|
||||
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
|
||||
|
||||
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
|
||||
AuditingEventListener.class.getName(), registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default bean definitions for a {@link MongoMappingContext} and an {@link IsNewStrategyFactory} in case we
|
||||
* don't find beans with the assumed names in the registry.
|
||||
*
|
||||
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
|
||||
* @param source the source which the registered components shall be registered with
|
||||
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingMongoConverter} bean available in
|
||||
* the application context.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
static class MongoMappingContextLookup
|
||||
implements FactoryBean<MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>> {
|
||||
|
||||
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
|
||||
private final MappingMongoConverter converter;
|
||||
|
||||
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
|
||||
definition.setRole(ROLE_INFRASTRUCTURE);
|
||||
definition.setSource(source);
|
||||
/**
|
||||
* Creates a new {@link MongoMappingContextLookup} for the given {@link MappingMongoConverter}.
|
||||
*
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public MongoMappingContextLookup(MappingMongoConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Override
|
||||
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getObject() throws Exception {
|
||||
return converter.getMappingContext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MappingContext.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BulkWriteException;
|
||||
import com.mongodb.BulkWriteOperation;
|
||||
import com.mongodb.BulkWriteRequestBuilder;
|
||||
@@ -38,6 +39,7 @@ import com.mongodb.WriteConcern;
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.9
|
||||
*/
|
||||
class DefaultBulkOperations implements BulkOperations {
|
||||
@@ -117,7 +119,15 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
|
||||
Assert.notNull(document, "Document must not be null!");
|
||||
|
||||
bulk.insert((DBObject) mongoOperations.getConverter().convertToMongoType(document));
|
||||
if (document instanceof DBObject) {
|
||||
|
||||
bulk.insert((DBObject) document);
|
||||
return this;
|
||||
}
|
||||
|
||||
DBObject sink = new BasicDBObject();
|
||||
mongoOperations.getConverter().write(document, sink);
|
||||
bulk.insert(sink);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
* Copyright 2011-2016 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,17 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexField;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
@@ -42,12 +40,12 @@ import com.mongodb.MongoException;
|
||||
*/
|
||||
public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private static final Double ONE = Double.valueOf(1);
|
||||
private static final Double MINUS_ONE = Double.valueOf(-1);
|
||||
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
|
||||
private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final String collectionName;
|
||||
private final QueryMapper mapper;
|
||||
private final Class<?> type;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultIndexOperations}.
|
||||
@@ -56,12 +54,26 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
* @param collectionName must not be {@literal null}.
|
||||
*/
|
||||
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
|
||||
this(mongoOperations, collectionName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultIndexOperations}.
|
||||
*
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param type Type used for mapping potential partial index filter expression. Can be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName, Class<?> type) {
|
||||
|
||||
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
|
||||
Assert.notNull(collectionName, "Collection name can not be null!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
this.collectionName = collectionName;
|
||||
this.mapper = new QueryMapper(mongoOperations.getConverter());
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -69,9 +81,20 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
*/
|
||||
public void ensureIndex(final IndexDefinition indexDefinition) {
|
||||
|
||||
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject indexOptions = indexDefinition.getIndexOptions();
|
||||
|
||||
if (indexOptions != null && indexOptions.containsField(PARTIAL_FILTER_EXPRESSION_KEY)) {
|
||||
|
||||
Assert.isInstanceOf(DBObject.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
|
||||
|
||||
indexOptions.put(PARTIAL_FILTER_EXPRESSION_KEY,
|
||||
mapper.getMappedObject((DBObject) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
|
||||
lookupPersistentEntity(type, collectionName)));
|
||||
}
|
||||
|
||||
if (indexOptions != null) {
|
||||
collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
|
||||
} else {
|
||||
@@ -79,6 +102,24 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
|
||||
|
||||
if (entityType != null) {
|
||||
return mongoOperations.getConverter().getMappingContext().getPersistentEntity(entityType);
|
||||
}
|
||||
|
||||
Collection<? extends MongoPersistentEntity<?>> entities = mongoOperations.getConverter().getMappingContext()
|
||||
.getPersistentEntities();
|
||||
|
||||
for (MongoPersistentEntity<?> entity : entities) {
|
||||
if (entity.getCollection().equals(collection)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,7 +167,9 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
public List<IndexInfo> getIndexInfo() {
|
||||
|
||||
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
|
||||
|
||||
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
|
||||
List<DBObject> dbObjectList = collection.getIndexInfo();
|
||||
return getIndexData(dbObjectList);
|
||||
}
|
||||
@@ -136,44 +179,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
|
||||
|
||||
for (DBObject ix : dbObjectList) {
|
||||
|
||||
DBObject keyDbObject = (DBObject) ix.get("key");
|
||||
int numberOfElements = keyDbObject.keySet().size();
|
||||
|
||||
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
|
||||
|
||||
for (String key : keyDbObject.keySet()) {
|
||||
|
||||
Object value = keyDbObject.get(key);
|
||||
|
||||
if (TWO_D_IDENTIFIERS.contains(value)) {
|
||||
indexFields.add(IndexField.geo(key));
|
||||
} else if ("text".equals(value)) {
|
||||
|
||||
DBObject weights = (DBObject) ix.get("weights");
|
||||
for (String fieldName : weights.keySet()) {
|
||||
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Double keyValue = new Double(value.toString());
|
||||
|
||||
if (ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, ASC));
|
||||
} else if (MINUS_ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, DESC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String name = ix.get("name").toString();
|
||||
|
||||
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
|
||||
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
|
||||
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
|
||||
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
|
||||
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
|
||||
indexInfoList.add(IndexInfo.indexInfoOf(ix));
|
||||
}
|
||||
|
||||
return indexInfoList;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -98,7 +98,7 @@ class DefaultScriptOperations implements ScriptOperations {
|
||||
|
||||
@Override
|
||||
public Object doInDB(DB db) throws MongoException, DataAccessException {
|
||||
return db.eval(script.getCode(), convertScriptArgs(args));
|
||||
return db.eval(script.getCode(), convertScriptArgs(false, args));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -155,7 +155,7 @@ class DefaultScriptOperations implements ScriptOperations {
|
||||
return scriptNames;
|
||||
}
|
||||
|
||||
private Object[] convertScriptArgs(Object... args) {
|
||||
private Object[] convertScriptArgs(boolean quote, Object... args) {
|
||||
|
||||
if (ObjectUtils.isEmpty(args)) {
|
||||
return args;
|
||||
@@ -164,15 +164,15 @@ class DefaultScriptOperations implements ScriptOperations {
|
||||
List<Object> convertedValues = new ArrayList<Object>(args.length);
|
||||
|
||||
for (Object arg : args) {
|
||||
convertedValues.add(arg instanceof String ? String.format("'%s'", arg) : this.mongoOperations.getConverter()
|
||||
.convertToMongoType(arg));
|
||||
convertedValues.add(arg instanceof String && quote ? String.format("'%s'", arg)
|
||||
: this.mongoOperations.getConverter().convertToMongoType(arg));
|
||||
}
|
||||
|
||||
return convertedValues.toArray();
|
||||
}
|
||||
|
||||
private String convertAndJoinScriptArgs(Object... args) {
|
||||
return ObjectUtils.isEmpty(args) ? "" : StringUtils.arrayToCommaDelimitedString(convertScriptArgs(args));
|
||||
return ObjectUtils.isEmpty(args) ? "" : StringUtils.arrayToCommaDelimitedString(convertScriptArgs(true, args));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -125,7 +125,7 @@ import com.mongodb.util.JSONParseException;
|
||||
|
||||
/**
|
||||
* Primary implementation of {@link MongoOperations}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Graeme Rocher
|
||||
* @author Mark Pollack
|
||||
@@ -174,7 +174,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Constructor used for a basic template configuration
|
||||
*
|
||||
*
|
||||
* @param mongo must not be {@literal null}.
|
||||
* @param databaseName must not be {@literal null} or empty.
|
||||
*/
|
||||
@@ -185,7 +185,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Constructor used for a template configuration with user credentials in the form of
|
||||
* {@link org.springframework.data.authentication.UserCredentials}
|
||||
*
|
||||
*
|
||||
* @param mongo must not be {@literal null}.
|
||||
* @param databaseName must not be {@literal null} or empty.
|
||||
* @param userCredentials
|
||||
@@ -196,7 +196,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Constructor used for a basic template configuration.
|
||||
*
|
||||
*
|
||||
* @param mongoDbFactory must not be {@literal null}.
|
||||
*/
|
||||
public MongoTemplate(MongoDbFactory mongoDbFactory) {
|
||||
@@ -205,7 +205,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Constructor used for a basic template configuration.
|
||||
*
|
||||
*
|
||||
* @param mongoDbFactory must not be {@literal null}.
|
||||
* @param mongoConverter
|
||||
*/
|
||||
@@ -234,7 +234,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Configures the {@link WriteResultChecking} to be used with the template. Setting {@literal null} will reset the
|
||||
* default of {@value #DEFAULT_WRITE_RESULT_CHECKING}.
|
||||
*
|
||||
*
|
||||
* @param resultChecking
|
||||
*/
|
||||
public void setWriteResultChecking(WriteResultChecking resultChecking) {
|
||||
@@ -245,7 +245,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* Configures the {@link WriteConcern} to be used with the template. If none is configured the {@link WriteConcern}
|
||||
* configured on the {@link MongoDbFactory} will apply. If you configured a {@link Mongo} instance no
|
||||
* {@link WriteConcern} will be used.
|
||||
*
|
||||
*
|
||||
* @param writeConcern
|
||||
*/
|
||||
public void setWriteConcern(WriteConcern writeConcern) {
|
||||
@@ -254,7 +254,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Configures the {@link WriteConcernResolver} to be used with the template.
|
||||
*
|
||||
*
|
||||
* @param writeConcernResolver
|
||||
*/
|
||||
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
|
||||
@@ -264,7 +264,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are
|
||||
* performed.
|
||||
*
|
||||
*
|
||||
* @param readPreference
|
||||
*/
|
||||
public void setReadPreference(ReadPreference readPreference) {
|
||||
@@ -291,7 +291,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext}
|
||||
* can be found we manually add the internally created one as {@link ApplicationListener} to make sure indexes get
|
||||
* created appropriately for entity types persisted through this {@link MongoTemplate} instance.
|
||||
*
|
||||
*
|
||||
* @param context must not be {@literal null}.
|
||||
*/
|
||||
private void prepareIndexCreator(ApplicationContext context) {
|
||||
@@ -311,15 +311,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default {@link org.springframework.data.mongodb.core.core.convert.MongoConverter}.
|
||||
*
|
||||
* Returns the default {@link org.springframework.data.mongodb.core.convert.MongoConverter}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public MongoConverter getConverter() {
|
||||
return this.mongoConverter;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoOperations#executeAsStream(org.springframework.data.mongodb.core.query.Query, java.lang.Class)
|
||||
*/
|
||||
@@ -412,7 +412,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Execute a MongoDB query and iterate over the query results on a per-document basis with a
|
||||
* {@link DocumentCallbackHandler} using the provided CursorPreparer.
|
||||
*
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification, must not be {@literal null}.
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
@@ -541,7 +541,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
public IndexOperations indexOps(Class<?> entityClass) {
|
||||
return new DefaultIndexOperations(this, determineCollectionName(entityClass));
|
||||
return new DefaultIndexOperations(this, determineCollectionName(entityClass), entityClass);
|
||||
}
|
||||
|
||||
public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {
|
||||
@@ -683,7 +683,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/*
|
||||
* As MongoDB currently (2.4.4) doesn't support the skipping of elements in near queries
|
||||
* we skip the elements ourselves to avoid at least the document 2 object mapping overhead.
|
||||
*
|
||||
*
|
||||
* @see https://jira.mongodb.org/browse/SERVER-3925
|
||||
*/
|
||||
if (index >= elementsToSkip) {
|
||||
@@ -788,7 +788,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Prepare the collection before any processing is done using it. This allows a convenient way to apply settings like
|
||||
* slaveOk() etc. Can be overridden in sub-classes.
|
||||
*
|
||||
*
|
||||
* @param collection
|
||||
*/
|
||||
protected void prepareCollection(DBCollection collection) {
|
||||
@@ -802,7 +802,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* settings in sub-classes. <br />
|
||||
* In case of using MongoDB Java driver version 3 the returned {@link WriteConcern} will be defaulted to
|
||||
* {@link WriteConcern#ACKNOWLEDGED} when {@link WriteResultChecking} is set to {@link WriteResultChecking#EXCEPTION}.
|
||||
*
|
||||
*
|
||||
* @param writeConcern any WriteConcern already configured or null
|
||||
* @return The prepared WriteConcern or null
|
||||
*/
|
||||
@@ -932,7 +932,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
maybeEmitEvent(new BeforeSaveEvent<T>(o, dbDoc, collectionName));
|
||||
dbObjectList.add(dbDoc);
|
||||
}
|
||||
List<ObjectId> ids = insertDBObjectList(collectionName, dbObjectList);
|
||||
|
||||
List<Object> ids = consolidateIdentifiers(insertDBObjectList(collectionName, dbObjectList), dbObjectList);
|
||||
|
||||
int i = 0;
|
||||
for (T obj : batchToSave) {
|
||||
if (i < ids.size()) {
|
||||
@@ -1037,6 +1039,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: 2.0 - Change method signature to return List<Object> and return all identifiers (DATAMONGO-1513,
|
||||
// DATAMONGO-1519)
|
||||
protected List<ObjectId> insertDBObjectList(final String collectionName, final List<DBObject> dbDocList) {
|
||||
if (dbDocList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
@@ -1208,7 +1212,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Returns {@link Entry} containing the field name of the id property as {@link Entry#getKey()} and the {@link Id}s
|
||||
* property value as its {@link Entry#getValue()}.
|
||||
*
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
@@ -1235,7 +1239,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Returns a {@link Query} for the given entity by its id.
|
||||
*
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -1247,7 +1251,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Returns a {@link Query} for the given entities by their ids.
|
||||
*
|
||||
*
|
||||
* @param objects must not be {@literal null} or {@literal empty}.
|
||||
* @return
|
||||
*/
|
||||
@@ -1518,7 +1522,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* Retrieve and remove all documents matching the given {@code query} by calling {@link #find(Query, Class, String)}
|
||||
* and {@link #remove(Query, Class, String)}, whereas the {@link Query} for {@link #remove(Query, Class, String)} is
|
||||
* constructed out of the find result.
|
||||
*
|
||||
*
|
||||
* @param collectionName
|
||||
* @param query
|
||||
* @param entityClass
|
||||
@@ -1558,7 +1562,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Returns the potentially mapped results of the given {@commandResult} contained some.
|
||||
*
|
||||
*
|
||||
* @param outputType
|
||||
* @param commandResult
|
||||
* @return
|
||||
@@ -1670,7 +1674,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Create the specified collection using the provided options
|
||||
*
|
||||
*
|
||||
* @param collectionName
|
||||
* @param collectionOptions
|
||||
* @return the collection that was created
|
||||
@@ -1691,7 +1695,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter.
|
||||
* The query document is specified as a standard {@link DBObject} and so is the fields specification.
|
||||
*
|
||||
*
|
||||
* @param collectionName name of the collection to retrieve the objects from.
|
||||
* @param query the query document that specifies the criteria used to find a record.
|
||||
* @param fields the document that specifies the fields to be returned.
|
||||
@@ -1716,7 +1720,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter. The
|
||||
* query document is specified as a standard DBObject and so is the fields specification.
|
||||
*
|
||||
*
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @param query the query document that specifies the criteria used to find a record
|
||||
* @param fields the document that specifies the fields to be returned
|
||||
@@ -1732,7 +1736,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified type. The object is
|
||||
* converted from the MongoDB native representation using an instance of {@see MongoConverter}. The query document is
|
||||
* specified as a standard DBObject and so is the fields specification.
|
||||
*
|
||||
*
|
||||
* @param collectionName name of the collection to retrieve the objects from.
|
||||
* @param query the query document that specifies the criteria used to find a record.
|
||||
* @param fields the document that specifies the fields to be returned.
|
||||
@@ -1785,7 +1789,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* The first document that matches the query is returned and also removed from the collection in the database.
|
||||
* <p/>
|
||||
* The query document is specified as a standard DBObject and so is the fields specification.
|
||||
*
|
||||
*
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @param query the query document that specifies the criteria used to find a record
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
@@ -1836,7 +1840,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Populates the id property of the saved object, if it's not set already.
|
||||
*
|
||||
*
|
||||
* @param savedObject
|
||||
* @param id
|
||||
*/
|
||||
@@ -1886,7 +1890,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* <li>Execute the given {@link ConnectionCallback} for a {@link DBObject}.</li>
|
||||
* <li>Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.</li>
|
||||
* <ol>
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* @param collectionCallback the callback to retrieve the {@link DBObject} with
|
||||
* @param objectCallback the {@link DbObjectCallback} to transform {@link DBObject}s into the actual domain type
|
||||
@@ -1915,7 +1919,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
* <li>Iterate over the {@link DBCursor} and applies the given {@link DbObjectCallback} to each of the
|
||||
* {@link DBObject}s collecting the actual result {@link List}.</li>
|
||||
* <ol>
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* @param collectionCallback the callback to retrieve the {@link DBCursor} with
|
||||
* @param preparer the {@link CursorPreparer} to potentially modify the {@link DBCursor} before ireating over it
|
||||
@@ -2022,7 +2026,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Handles {@link WriteResult} errors based on the configured {@link WriteResultChecking}.
|
||||
*
|
||||
*
|
||||
* @param writeResult
|
||||
* @param query
|
||||
* @param operation
|
||||
@@ -2066,7 +2070,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Inspects the given {@link CommandResult} for erros and potentially throws an
|
||||
* {@link InvalidDataAccessApiUsageException} for that error.
|
||||
*
|
||||
*
|
||||
* @param result must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
*/
|
||||
@@ -2104,7 +2108,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
|
||||
* exception if the conversation failed. Thus allows safe re-throwing of the return value.
|
||||
*
|
||||
*
|
||||
* @param ex the exception to translate
|
||||
* @param exceptionTranslator the {@link PersistenceExceptionTranslator} to be used for translation
|
||||
* @return
|
||||
@@ -2115,12 +2119,34 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
return resolved == null ? ex : resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all identifiers for the given documents. Will augment the given identifiers and fill in only the ones that
|
||||
* are {@literal null} currently. This would've been better solved in {@link #insertDBObjectList(String, List)}
|
||||
* directly but would require a signature change of that method.
|
||||
*
|
||||
* @param ids
|
||||
* @param documents
|
||||
* @return TODO: Remove for 2.0 and change method signature of {@link #insertDBObjectList(String, List)}.
|
||||
*/
|
||||
private static List<Object> consolidateIdentifiers(List<ObjectId> ids, List<DBObject> documents) {
|
||||
|
||||
List<Object> result = new ArrayList<Object>(ids.size());
|
||||
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
|
||||
ObjectId objectId = ids.get(i);
|
||||
result.add(objectId == null ? documents.get(i).get(ID_FIELD) : objectId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Callback implementations
|
||||
|
||||
/**
|
||||
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
|
||||
* {@link DBObject} and executes that against the {@link DBCollection}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
@@ -2154,7 +2180,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
|
||||
* {@link DBObject} and executes that against the {@link DBCollection}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
@@ -2168,7 +2194,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
public FindCallback(DBObject query, DBObject fields) {
|
||||
this.query = query;
|
||||
this.query = query == null ? new BasicDBObject() : query;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
@@ -2185,7 +2211,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
|
||||
* {@link DBObject} and executes that against the {@link DBCollection}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
private static class FindAndRemoveCallback implements CollectionCallback<DBObject> {
|
||||
@@ -2230,7 +2256,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Simple internal callback to allow operations on a {@link DBObject}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@@ -2243,7 +2269,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Simple {@link DbObjectCallback} that will transform {@link DBObject} into the given target type using the given
|
||||
* {@link MongoReader}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@@ -2363,7 +2389,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to
|
||||
* a delegate and creates a {@link GeoResult} from the result.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class GeoNearResultDbObjectCallback<T> implements DbObjectCallback<GeoResult<T>> {
|
||||
@@ -2374,7 +2400,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
/**
|
||||
* Creates a new {@link GeoNearResultDbObjectCallback} using the given {@link DbObjectCallback} delegate for
|
||||
* {@link GeoResult} content unmarshalling.
|
||||
*
|
||||
*
|
||||
* @param delegate must not be {@literal null}.
|
||||
*/
|
||||
public GeoNearResultDbObjectCallback(DbObjectCallback<T> delegate, Metric metric) {
|
||||
@@ -2396,7 +2422,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* A {@link CloseableIterator} that is backed by a MongoDB {@link Cursor}.
|
||||
*
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@@ -2408,7 +2434,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link Cursor}.
|
||||
*
|
||||
*
|
||||
* @param cursor
|
||||
* @param exceptionTranslator
|
||||
* @param objectReadCallback
|
||||
|
||||
@@ -248,11 +248,22 @@ public class Aggregation {
|
||||
*
|
||||
* @param elementsToSkip must not be less than zero.
|
||||
* @return
|
||||
* @deprecated prepare to get this one removed in favor of {@link #skip(long)}.
|
||||
*/
|
||||
public static SkipOperation skip(int elementsToSkip) {
|
||||
return new SkipOperation(elementsToSkip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SkipOperation} skipping the given number of elements.
|
||||
*
|
||||
* @param elementsToSkip must not be less than zero.
|
||||
* @return
|
||||
*/
|
||||
public static SkipOperation skip(long elementsToSkip) {
|
||||
return new SkipOperation(elementsToSkip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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.
|
||||
@@ -20,16 +20,17 @@ import com.mongodb.DBObject;
|
||||
/**
|
||||
* An {@link AggregationExpression} can be used with field expressions in aggregation pipeline stages like
|
||||
* {@code project} and {@code group}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface AggregationExpression {
|
||||
public interface AggregationExpression {
|
||||
|
||||
/**
|
||||
* Turns the {@link AggregationExpression} into a {@link DBObject} within the given
|
||||
* {@link AggregationOperationContext}.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2017 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.
|
||||
@@ -20,6 +20,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
|
||||
@@ -552,7 +553,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
/**
|
||||
* Generates an {@code $mod} expression that divides the value of the given field by the previously mentioned field
|
||||
* and returns the remainder.
|
||||
*
|
||||
*
|
||||
* @param fieldReference
|
||||
* @return
|
||||
*/
|
||||
@@ -566,7 +567,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return project("size");
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@@ -622,6 +623,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class FieldProjection extends Projection {
|
||||
|
||||
@@ -640,7 +642,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private FieldProjection(Field field, Object value) {
|
||||
|
||||
super(field);
|
||||
super(new ExposedField(field.getName(), true));
|
||||
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
@@ -732,7 +734,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -38,7 +38,7 @@ public class SkipOperation implements AggregationOperation {
|
||||
/**
|
||||
* Creates a new {@link SkipOperation} skipping the given number of elements.
|
||||
*
|
||||
* @param skipCount number of documents to skip.
|
||||
* @param skipCount number of documents to skip, must not be less than zero.
|
||||
*/
|
||||
public SkipOperation(long skipCount) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2017 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.
|
||||
@@ -34,6 +34,7 @@ import com.mongodb.DBObject;
|
||||
* property references into document field names.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @since 1.3
|
||||
*/
|
||||
public class TypeBasedAggregationOperationContext implements AggregationOperationContext {
|
||||
@@ -95,7 +96,7 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
|
||||
|
||||
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(
|
||||
field.getTarget(), type);
|
||||
Field mappedField = field(propertyPath.getLeafProperty().getName(),
|
||||
Field mappedField = field(field.getName(),
|
||||
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
|
||||
|
||||
return new FieldReference(new ExposedField(mappedField, true));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 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.
|
||||
@@ -56,6 +56,7 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class CustomConversions {
|
||||
|
||||
|
||||
@@ -114,6 +114,40 @@ class DBObjectAccessor {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying {@link DBObject} has a value ({@literal null} or non-{@literal null}) for the given
|
||||
* {@link MongoPersistentProperty}.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean hasValue(MongoPersistentProperty property) {
|
||||
|
||||
Assert.notNull(property, "Property must not be null!");
|
||||
|
||||
String fieldName = property.getFieldName();
|
||||
|
||||
if (!fieldName.contains(".")) {
|
||||
return this.dbObject.containsField(fieldName);
|
||||
}
|
||||
|
||||
String[] parts = fieldName.split("\\.");
|
||||
Map<String, Object> source = this.dbObject;
|
||||
Object result = null;
|
||||
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
|
||||
result = source.get(parts[i - 1]);
|
||||
source = getAsMap(result);
|
||||
|
||||
if (source == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return source.containsKey(parts[parts.length - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given source object as map, i.e. {@link BasicDBObject}s and maps as is or {@literal null} otherwise.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -180,8 +180,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
|
||||
Serializable {
|
||||
static class LazyLoadingInterceptor
|
||||
implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable {
|
||||
|
||||
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD;
|
||||
|
||||
@@ -387,7 +387,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
} catch (RuntimeException ex) {
|
||||
|
||||
DataAccessException translatedException = this.exceptionTranslator.translateExceptionIfPossible(ex);
|
||||
throw new LazyLoadingException("Unable to lazily resolve DBRef!", translatedException);
|
||||
throw new LazyLoadingException("Unable to lazily resolve DBRef!",
|
||||
translatedException != null ? translatedException : ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -599,7 +599,7 @@ abstract class GeoConverters {
|
||||
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"),
|
||||
String.format("Cannot convert type '%s' to Point.", source.get("type")));
|
||||
|
||||
List<Double> dbl = (List<Double>) source.get("coordinates");
|
||||
List<Number> dbl = (List<Number>) source.get("coordinates");
|
||||
return new GeoJsonPoint(dbl.get(0).doubleValue(), dbl.get(1).doubleValue());
|
||||
}
|
||||
}
|
||||
@@ -832,7 +832,7 @@ abstract class GeoConverters {
|
||||
|
||||
Assert.isInstanceOf(List.class, point);
|
||||
|
||||
List<Double> coordinatesList = (List<Double>) point;
|
||||
List<Number> coordinatesList = (List<Number>) point;
|
||||
|
||||
points.add(new GeoJsonPoint(coordinatesList.get(0).doubleValue(), coordinatesList.get(1).doubleValue()));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2015 by the original author(s).
|
||||
* Copyright 2011-2017 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -95,7 +96,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
/**
|
||||
* Creates a new {@link MappingMongoConverter} given the new {@link DbRefResolver} and {@link MappingContext}.
|
||||
*
|
||||
* @param mongoDbFactory must not be {@literal null}.
|
||||
* @param dbRefResolver must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public MappingMongoConverter(DbRefResolver dbRefResolver,
|
||||
@@ -258,14 +259,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
// make sure id property is set before all other properties
|
||||
Object idValue = null;
|
||||
final DBObjectAccessor dbObjectAccessor = new DBObjectAccessor(dbo);
|
||||
|
||||
if (idProperty != null) {
|
||||
if (idProperty != null && dbObjectAccessor.hasValue(idProperty)) {
|
||||
idValue = getValueInternal(idProperty, dbo, evaluator, path);
|
||||
accessor.setProperty(idProperty, idValue);
|
||||
}
|
||||
|
||||
final ObjectPath currentPath = path.push(result, entity,
|
||||
idValue != null ? dbo.get(idProperty.getFieldName()) : null);
|
||||
final ObjectPath currentPath = path.push(result, entity, idValue != null ? dbObjectAccessor.get(idProperty) : null);
|
||||
|
||||
// Set properties not already set in the constructor
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
@@ -276,7 +277,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dbo.containsField(prop.getFieldName()) || entity.isConstructorArgument(prop)) {
|
||||
if (entity.isConstructorArgument(prop) || !dbObjectAccessor.hasValue(prop)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -289,7 +290,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
final MongoPersistentProperty property = association.getInverse();
|
||||
Object value = dbo.get(property.getFieldName());
|
||||
Object value = dbObjectAccessor.get(property);
|
||||
|
||||
if (value == null || entity.isConstructorArgument(property)) {
|
||||
return;
|
||||
@@ -313,12 +314,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.MongoWriter#toDBRef(java.lang.Object, org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
|
||||
*/
|
||||
public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) {
|
||||
public DBRef toDBRef(Object object, MongoPersistentProperty referringProperty) {
|
||||
|
||||
org.springframework.data.mongodb.core.mapping.DBRef annotation = null;
|
||||
|
||||
if (referingProperty != null) {
|
||||
annotation = referingProperty.getDBRef();
|
||||
if (referringProperty != null) {
|
||||
annotation = referringProperty.getDBRef();
|
||||
Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef!");
|
||||
}
|
||||
|
||||
@@ -327,14 +328,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return ((LazyLoadingProxy) object).toDBRef();
|
||||
}
|
||||
|
||||
return createDBRef(object, referingProperty);
|
||||
return createDBRef(object, referringProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Root entry method into write conversion. Adds a type discriminator to the {@link DBObject}. Shouldn't be called for
|
||||
* nested conversions.
|
||||
*
|
||||
* @see org.springframework.data.mongodb.core.core.convert.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
|
||||
* @see org.springframework.data.mongodb.core.convert.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
|
||||
*/
|
||||
public void write(final Object obj, final DBObject dbo) {
|
||||
|
||||
@@ -885,10 +886,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Class<?> collectionType = targetType.getType();
|
||||
|
||||
if (sourceValue.isEmpty()) {
|
||||
return getPotentiallyConvertedSimpleRead(new HashSet<Object>(), collectionType);
|
||||
}
|
||||
|
||||
TypeInformation<?> componentType = targetType.getComponentType();
|
||||
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
|
||||
|
||||
@@ -896,9 +893,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
|
||||
: CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
|
||||
|
||||
for (int i = 0; i < sourceValue.size(); i++) {
|
||||
if (sourceValue.isEmpty()) {
|
||||
return getPotentiallyConvertedSimpleRead(items, collectionType);
|
||||
}
|
||||
|
||||
Object dbObjItem = sourceValue.get(i);
|
||||
for (Object dbObjItem : sourceValue) {
|
||||
|
||||
if (dbObjItem instanceof DBRef) {
|
||||
items.add(
|
||||
@@ -992,20 +991,32 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
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, typeHint));
|
||||
}
|
||||
|
||||
return newValueDbo;
|
||||
}
|
||||
|
||||
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(), typeHint));
|
||||
|
||||
Map<Object, Object> converted = new LinkedHashMap<Object, Object>();
|
||||
|
||||
for (Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
|
||||
|
||||
TypeInformation<? extends Object> valueTypeHint = typeHint != null && typeHint.getMapValueType() != null
|
||||
? typeHint.getMapValueType() : typeHint;
|
||||
|
||||
converted.put(getPotentiallyConvertedSimpleWrite(entry.getKey()).toString(),
|
||||
convertToMongoType(entry.getValue(), valueTypeHint));
|
||||
}
|
||||
return result;
|
||||
|
||||
return new BasicDBObject(converted);
|
||||
}
|
||||
|
||||
if (obj.getClass().isArray()) {
|
||||
|
||||
@@ -81,7 +81,10 @@ abstract class MongoConverters {
|
||||
converters.add(DBObjectToNamedMongoScriptCoverter.INSTANCE);
|
||||
converters.add(CurrencyToStringConverter.INSTANCE);
|
||||
converters.add(StringToCurrencyConverter.INSTANCE);
|
||||
converters.add(NumberToNumberConverterFactory.INSTANCE);
|
||||
converters.add(AtomicIntegerToIntegerConverter.INSTANCE);
|
||||
converters.add(AtomicLongToLongConverter.INSTANCE);
|
||||
converters.add(LongToAtomicLongConverter.INSTANCE);
|
||||
converters.add(IntegerToAtomicIntegerConverter.INSTANCE);
|
||||
|
||||
return converters;
|
||||
}
|
||||
@@ -374,4 +377,68 @@ abstract class MongoConverters {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConverterFactory} implementation converting {@link AtomicLong} into {@link Long}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@WritingConverter
|
||||
public static enum AtomicLongToLongConverter implements Converter<AtomicLong, Long> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Long convert(AtomicLong source) {
|
||||
return NumberUtils.convertNumberToTargetClass(source, Long.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConverterFactory} implementation converting {@link AtomicInteger} into {@link Integer}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@WritingConverter
|
||||
public static enum AtomicIntegerToIntegerConverter implements Converter<AtomicInteger, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Integer convert(AtomicInteger source) {
|
||||
return NumberUtils.convertNumberToTargetClass(source, Integer.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConverterFactory} implementation converting {@link Long} into {@link AtomicLong}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@ReadingConverter
|
||||
public static enum LongToAtomicLongConverter implements Converter<Long, AtomicLong> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public AtomicLong convert(Long source) {
|
||||
return source != null ? new AtomicLong(source) : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ConverterFactory} implementation converting {@link Integer} into {@link AtomicInteger}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@ReadingConverter
|
||||
public static enum IntegerToAtomicIntegerConverter implements Converter<Integer, AtomicInteger> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public AtomicInteger convert(Integer source) {
|
||||
return source != null ? new AtomicInteger(source) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import com.mongodb.DBRef;
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class QueryMapper {
|
||||
|
||||
@@ -66,7 +67,7 @@ public class QueryMapper {
|
||||
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
|
||||
|
||||
private enum MetaMapping {
|
||||
FORCE, WHEN_PRESENT, IGNORE;
|
||||
FORCE, WHEN_PRESENT, IGNORE
|
||||
}
|
||||
|
||||
private final ConversionService conversionService;
|
||||
@@ -316,7 +317,7 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
if (isNestedKeyword(value)) {
|
||||
return getMappedKeyword(new Keyword((DBObject) value), null);
|
||||
return getMappedKeyword(new Keyword((DBObject) value), documentField.getPropertyEntity());
|
||||
}
|
||||
|
||||
if (isAssociationConversionNecessary(documentField, value)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -39,6 +39,7 @@ public class GeospatialIndex implements IndexDefinition {
|
||||
private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
|
||||
private Double bucketSize = 1.0;
|
||||
private String additionalField;
|
||||
private IndexFilter filter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeospatialIndex} for the given field.
|
||||
@@ -119,6 +120,22 @@ public class GeospatialIndex implements IndexDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
|
||||
*
|
||||
* @param filter can be {@literal null}.
|
||||
* @return
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
|
||||
* @since 1.10
|
||||
*/
|
||||
public GeospatialIndex partial(IndexFilter filter) {
|
||||
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DBObject getIndexKeys() {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
@@ -186,6 +203,10 @@ public class GeospatialIndex implements IndexDefinition {
|
||||
break;
|
||||
}
|
||||
|
||||
if (filter != null) {
|
||||
dbo.put("partialFilterExpression", filter.getFilterObject());
|
||||
}
|
||||
|
||||
return dbo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -63,6 +63,8 @@ public class Index implements IndexDefinition {
|
||||
|
||||
private long expire = -1;
|
||||
|
||||
private IndexFilter filter;
|
||||
|
||||
public Index() {}
|
||||
|
||||
public Index(String key, Direction direction) {
|
||||
@@ -176,6 +178,21 @@ public class Index implements IndexDefinition {
|
||||
return unique();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
|
||||
*
|
||||
* @param filter can be {@literal null}.
|
||||
* @return
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
|
||||
* @since 1.10
|
||||
*/
|
||||
public Index partial(IndexFilter filter) {
|
||||
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys()
|
||||
@@ -213,6 +230,9 @@ public class Index implements IndexDefinition {
|
||||
dbo.put("expireAfterSeconds", expire);
|
||||
}
|
||||
|
||||
if (filter != null) {
|
||||
dbo.put("partialFilterExpression", filter.getFilterObject());
|
||||
}
|
||||
return dbo;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Use {@link IndexFilter} to create the partial filter expression used when creating
|
||||
* <a href="https://docs.mongodb.com/manual/core/index-partial/">Partial Indexes</a>.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public interface IndexFilter {
|
||||
|
||||
/**
|
||||
* Get the raw (unmapped) filter expression.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
DBObject getFilterObject();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 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,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -23,6 +26,8 @@ import java.util.List;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
@@ -30,6 +35,10 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public class IndexInfo {
|
||||
|
||||
private static final Double ONE = Double.valueOf(1);
|
||||
private static final Double MINUS_ONE = Double.valueOf(-1);
|
||||
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
|
||||
|
||||
private final List<IndexField> indexFields;
|
||||
|
||||
private final String name;
|
||||
@@ -37,6 +46,7 @@ public class IndexInfo {
|
||||
private final boolean dropDuplicates;
|
||||
private final boolean sparse;
|
||||
private final String language;
|
||||
private String partialFilterExpression;
|
||||
|
||||
/**
|
||||
* @deprecated Will be removed in 1.7. Please use {@link #IndexInfo(List, String, boolean, boolean, boolean, String)}
|
||||
@@ -62,6 +72,64 @@ public class IndexInfo {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IndexInfo} parsing required properties from the given {@literal sourceDocument}.
|
||||
*
|
||||
* @param sourceDocument
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static IndexInfo indexInfoOf(DBObject sourceDocument) {
|
||||
|
||||
DBObject keyDbObject = (DBObject) sourceDocument.get("key");
|
||||
int numberOfElements = keyDbObject.keySet().size();
|
||||
|
||||
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
|
||||
|
||||
for (String key : keyDbObject.keySet()) {
|
||||
|
||||
Object value = keyDbObject.get(key);
|
||||
|
||||
if (TWO_D_IDENTIFIERS.contains(value)) {
|
||||
|
||||
indexFields.add(IndexField.geo(key));
|
||||
|
||||
} else if ("text".equals(value)) {
|
||||
|
||||
DBObject weights = (DBObject) sourceDocument.get("weights");
|
||||
|
||||
for (String fieldName : weights.keySet()) {
|
||||
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Double keyValue = new Double(value.toString());
|
||||
|
||||
if (ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, ASC));
|
||||
} else if (MINUS_ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, DESC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String name = sourceDocument.get("name").toString();
|
||||
|
||||
boolean unique = sourceDocument.containsField("unique") ? (Boolean) sourceDocument.get("unique") : false;
|
||||
boolean dropDuplicates = sourceDocument.containsField("dropDups") ? (Boolean) sourceDocument.get("dropDups")
|
||||
: false;
|
||||
boolean sparse = sourceDocument.containsField("sparse") ? (Boolean) sourceDocument.get("sparse") : false;
|
||||
String language = sourceDocument.containsField("default_language") ? (String) sourceDocument.get("default_language")
|
||||
: "";
|
||||
String partialFilter = sourceDocument.containsField("partialFilterExpression")
|
||||
? sourceDocument.get("partialFilterExpression").toString() : "";
|
||||
|
||||
IndexInfo info = new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language);
|
||||
info.partialFilterExpression = partialFilter;
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the individual index fields of the index.
|
||||
*
|
||||
@@ -113,10 +181,19 @@ public class IndexInfo {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.0
|
||||
*/
|
||||
public String getPartialFilterExpression() {
|
||||
return partialFilterExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates="
|
||||
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + "]";
|
||||
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + ", partialFilterExpression="
|
||||
+ partialFilterExpression + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +207,7 @@ public class IndexInfo {
|
||||
result = prime * result + (sparse ? 1231 : 1237);
|
||||
result = prime * result + (unique ? 1231 : 1237);
|
||||
result = prime * result + ObjectUtils.nullSafeHashCode(language);
|
||||
result = prime * result + ObjectUtils.nullSafeHashCode(partialFilterExpression);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -171,6 +249,9 @@ public class IndexInfo {
|
||||
if (!ObjectUtils.nullSafeEquals(language, other.language)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(partialFilterExpression, other.partialFilterExpression)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* {@link IndexFilter} implementation for usage with plain {@link DBObject} as well as {@link CriteriaDefinition} filter
|
||||
* expressions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class PartialIndexFilter implements IndexFilter {
|
||||
|
||||
private final @NonNull Object filterExpression;
|
||||
|
||||
/**
|
||||
* Create new {@link PartialIndexFilter} for given {@link DBObject filter expression}.
|
||||
*
|
||||
* @param where must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static PartialIndexFilter of(DBObject where) {
|
||||
return new PartialIndexFilter(where);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link PartialIndexFilter} for given {@link CriteriaDefinition filter expression}.
|
||||
*
|
||||
* @param where must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static PartialIndexFilter of(CriteriaDefinition where) {
|
||||
return new PartialIndexFilter(where);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexFilter#getFilterObject()
|
||||
*/
|
||||
public DBObject getFilterObject() {
|
||||
|
||||
if (filterExpression instanceof DBObject) {
|
||||
return (DBObject) filterExpression;
|
||||
}
|
||||
|
||||
if (filterExpression instanceof CriteriaDefinition) {
|
||||
return ((CriteriaDefinition) filterExpression).getCriteriaObject();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unknown type %s used as filter expression.", filterExpression.getClass()));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -39,6 +39,7 @@ public class TextIndexDefinition implements IndexDefinition {
|
||||
private Set<TextIndexedFieldSpec> fieldSpecs;
|
||||
private String defaultLanguage;
|
||||
private String languageOverride;
|
||||
private IndexFilter filter;
|
||||
|
||||
TextIndexDefinition() {
|
||||
fieldSpecs = new LinkedHashSet<TextIndexedFieldSpec>();
|
||||
@@ -129,6 +130,10 @@ public class TextIndexDefinition implements IndexDefinition {
|
||||
options.put("language_override", languageOverride);
|
||||
}
|
||||
|
||||
if (filter != null) {
|
||||
options.put("partialFilterExpression", filter.getFilterObject());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -288,8 +293,8 @@ public class TextIndexDefinition implements IndexDefinition {
|
||||
public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
|
||||
|
||||
if (this.instance.fieldSpecs.contains(ALL_FIELDS)) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format("Cannot add %s to field spec for all fields.",
|
||||
fieldname));
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("Cannot add %s to field spec for all fields.", fieldname));
|
||||
}
|
||||
|
||||
this.instance.fieldSpecs.add(new TextIndexedFieldSpec(fieldname, weight));
|
||||
@@ -318,15 +323,30 @@ public class TextIndexDefinition implements IndexDefinition {
|
||||
public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
|
||||
|
||||
if (StringUtils.hasText(this.instance.languageOverride)) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Cannot set language override on %s as it is already defined on %s.", fieldname,
|
||||
this.instance.languageOverride));
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("Cannot set language override on %s as it is already defined on %s.", fieldname,
|
||||
this.instance.languageOverride));
|
||||
}
|
||||
|
||||
this.instance.languageOverride = fieldname;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only index the documents that meet the specified {@link IndexFilter filter expression}.
|
||||
*
|
||||
* @param filter can be {@literal null}.
|
||||
* @return
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
|
||||
* @since 1.10
|
||||
*/
|
||||
public TextIndexDefinitionBuilder partial(IndexFilter filter) {
|
||||
|
||||
this.instance.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextIndexDefinition build() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* Copyright (c) 2011-2017 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,8 @@ import org.bson.types.Binary;
|
||||
import org.bson.types.CodeWScope;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
@@ -34,6 +36,7 @@ import com.mongodb.DBRef;
|
||||
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Mongo specific simple types.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public abstract class MongoSimpleTypes {
|
||||
|
||||
@@ -54,12 +57,17 @@ public abstract class MongoSimpleTypes {
|
||||
simpleTypes.add(Pattern.class);
|
||||
simpleTypes.add(Binary.class);
|
||||
simpleTypes.add(UUID.class);
|
||||
|
||||
if (MongoClientVersion.isMongo34Driver()) {
|
||||
simpleTypes
|
||||
.add(ClassUtils.resolveClassName("org.bson.types.Decimal128", MongoSimpleTypes.class.getClassLoader()));
|
||||
}
|
||||
|
||||
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
|
||||
}
|
||||
|
||||
private static final Set<Class<?>> MONGO_SIMPLE_TYPES;
|
||||
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(MONGO_SIMPLE_TYPES, true);
|
||||
|
||||
private MongoSimpleTypes() {
|
||||
}
|
||||
private MongoSimpleTypes() {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -45,6 +45,7 @@ import com.mongodb.gridfs.GridFSInputFile;
|
||||
* @author Philipp Schneider
|
||||
* @author Thomas Darimont
|
||||
* @author Martin Baumgartner
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver {
|
||||
|
||||
@@ -182,7 +183,7 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
|
||||
public List<GridFSDBFile> find(Query query) {
|
||||
|
||||
if (query == null) {
|
||||
return getGridFs().find((DBObject) null);
|
||||
return getGridFs().find(new BasicDBObject());
|
||||
}
|
||||
|
||||
DBObject queryObject = getMappedQuery(query.getQueryObject());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 the original author or authors.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -55,7 +55,7 @@ public interface MongoRepository<T, ID extends Serializable>
|
||||
List<T> findAll(Sort sort);
|
||||
|
||||
/**
|
||||
* Inserts the given a given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
|
||||
* Inserts the given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
|
||||
* the returned instance for further operations as the save operation might have changed the entity instance
|
||||
* completely. Prefer using {@link #save(Object)} instead to avoid the usage of store-specific API.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2016 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.
|
||||
@@ -19,21 +19,15 @@ import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mongodb.config.BeanNames;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
@@ -47,8 +41,6 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
private static final String MONGO_TEMPLATE_REF = "mongo-template-ref";
|
||||
private static final String CREATE_QUERY_INDEXES = "create-query-indexes";
|
||||
|
||||
private boolean fallbackMappingContextCreated = false;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName()
|
||||
@@ -81,7 +73,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.<Class<? extends Annotation>> singleton(Document.class);
|
||||
return Collections.<Class<? extends Annotation>>singleton(Document.class);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -90,19 +82,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Class<?>> getIdentifyingTypes() {
|
||||
return Collections.<Class<?>> singleton(MongoRepository.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
|
||||
|
||||
if (fallbackMappingContextCreated) {
|
||||
builder.addPropertyReference("mappingContext", BeanNames.MAPPING_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
return Collections.<Class<?>>singleton(MongoRepository.class);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -130,23 +110,4 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
builder.addPropertyReference("mongoOperations", attributes.getString("mongoTemplateRef"));
|
||||
builder.addPropertyValue("createIndexesForQueryMethods", attributes.getBoolean("createIndexesForQueryMethods"));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {
|
||||
|
||||
super.registerBeansForRoot(registry, configurationSource);
|
||||
|
||||
if (!registry.containsBeanDefinition(BeanNames.MAPPING_CONTEXT_BEAN_NAME)) {
|
||||
|
||||
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
|
||||
definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
definition.setSource(configurationSource.getSource());
|
||||
|
||||
registry.registerBeanDefinition(BeanNames.MAPPING_CONTEXT_BEAN_NAME, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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,8 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
@@ -39,6 +46,7 @@ import com.mongodb.util.JSON;
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @since 1.9
|
||||
*/
|
||||
class ExpressionEvaluatingParameterBinder {
|
||||
@@ -85,7 +93,7 @@ class ExpressionEvaluatingParameterBinder {
|
||||
*
|
||||
* @param input must not be {@literal null} or empty.
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @param bindings must not be {@literal null}.
|
||||
* @param bindingContext must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) {
|
||||
@@ -94,47 +102,72 @@ class ExpressionEvaluatingParameterBinder {
|
||||
return input;
|
||||
}
|
||||
|
||||
boolean isCompletlyParameterizedQuery = input.matches("^\\?\\d+$");
|
||||
StringBuilder result = new StringBuilder(input);
|
||||
|
||||
for (ParameterBinding binding : bindingContext.getBindings()) {
|
||||
|
||||
String parameter = binding.getParameter();
|
||||
int idx = result.indexOf(parameter);
|
||||
|
||||
if (idx == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
|
||||
|
||||
int start = idx;
|
||||
int end = idx + parameter.length();
|
||||
|
||||
// If the value to bind is an object literal we need to remove the quoting around the expression insertion point.
|
||||
if (valueForBinding.startsWith("{") && !isCompletlyParameterizedQuery) {
|
||||
|
||||
// Is the insertion point actually surrounded by quotes?
|
||||
char beforeStart = result.charAt(start - 1);
|
||||
char afterEnd = result.charAt(end);
|
||||
|
||||
if ((beforeStart == '\'' || beforeStart == '"') && (afterEnd == '\'' || afterEnd == '"')) {
|
||||
|
||||
// Skip preceding and following quote
|
||||
start -= 1;
|
||||
end += 1;
|
||||
}
|
||||
}
|
||||
|
||||
result.replace(start, end, valueForBinding);
|
||||
if (input.matches("^\\?\\d+$")) {
|
||||
return getParameterValueForBinding(accessor, bindingContext.getParameters(),
|
||||
bindingContext.getBindings().iterator().next());
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
Matcher matcher = createReplacementPattern(bindingContext.getBindings()).matcher(input);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
|
||||
ParameterBinding binding = bindingContext.getBindingFor(extractPlaceholder(matcher.group()));
|
||||
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
|
||||
|
||||
// appendReplacement does not like unescaped $ sign and others, so we need to quote that stuff first
|
||||
matcher.appendReplacement(buffer, Matcher.quoteReplacement(valueForBinding));
|
||||
|
||||
if (binding.isQuoted()) {
|
||||
postProcessQuotedBinding(buffer, valueForBinding);
|
||||
}
|
||||
}
|
||||
|
||||
matcher.appendTail(buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize String binding by replacing single quoted values with double quotes which prevents potential single quotes
|
||||
* contained in replacement to interfere with the Json parsing. Also take care of complex objects by removing the
|
||||
* quotation entirely.
|
||||
*
|
||||
* @param buffer the {@link StringBuffer} to operate upon.
|
||||
* @param valueForBinding the actual binding value.
|
||||
*/
|
||||
private void postProcessQuotedBinding(StringBuffer buffer, String valueForBinding) {
|
||||
|
||||
int quotationMarkIndex = buffer.length() - valueForBinding.length() - 1;
|
||||
char quotationMark = buffer.charAt(quotationMarkIndex);
|
||||
|
||||
while (quotationMark != '\'' && quotationMark != '"') {
|
||||
|
||||
quotationMarkIndex--;
|
||||
|
||||
if (quotationMarkIndex < 0) {
|
||||
throw new IllegalArgumentException("Could not find opening quotes for quoted parameter");
|
||||
}
|
||||
|
||||
quotationMark = buffer.charAt(quotationMarkIndex);
|
||||
}
|
||||
|
||||
if (valueForBinding.startsWith("{")) { // remove quotation char before the complex object string
|
||||
|
||||
buffer.deleteCharAt(quotationMarkIndex);
|
||||
|
||||
} else {
|
||||
|
||||
if (quotationMark == '\'') {
|
||||
buffer.replace(quotationMarkIndex, quotationMarkIndex + 1, "\"");
|
||||
}
|
||||
|
||||
buffer.append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the serialized value to be used for the given {@link ParameterBinding}.
|
||||
*
|
||||
*
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @param parameters
|
||||
* @param binding must not be {@literal null}.
|
||||
@@ -148,7 +181,7 @@ class ExpressionEvaluatingParameterBinder {
|
||||
: accessor.getBindableValue(binding.getParameterIndex());
|
||||
|
||||
if (value instanceof String && binding.isQuoted()) {
|
||||
return (String) value;
|
||||
return ((String) value).startsWith("{") ? (String) value : ((String) value).replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
if (value instanceof byte[]) {
|
||||
@@ -167,7 +200,7 @@ class ExpressionEvaluatingParameterBinder {
|
||||
|
||||
/**
|
||||
* Evaluates the given {@code expressionString}.
|
||||
*
|
||||
*
|
||||
* @param expressionString must not be {@literal null} or empty.
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @param parameterValues must not be {@literal null}.
|
||||
@@ -181,25 +214,61 @@ class ExpressionEvaluatingParameterBinder {
|
||||
return expression.getValue(evaluationContext, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a replacement {@link Pattern} for all {@link ParameterBinding#getParameter() binding parameters} including
|
||||
* a potentially trailing quotation mark.
|
||||
*
|
||||
* @param bindings
|
||||
* @return
|
||||
*/
|
||||
private Pattern createReplacementPattern(List<ParameterBinding> bindings) {
|
||||
|
||||
StringBuilder regex = new StringBuilder();
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
|
||||
regex.append("|");
|
||||
regex.append(Pattern.quote(binding.getParameter()));
|
||||
regex.append("['\"]?"); // potential quotation char (as in { foo : '?0' }).
|
||||
}
|
||||
|
||||
return Pattern.compile(regex.substring(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the placeholder stripping any trailing trailing quotation mark that might have resulted from the
|
||||
* {@link #createReplacementPattern(List) pattern} used.
|
||||
*
|
||||
* @param groupName The actual {@link Matcher#group() group}.
|
||||
* @return
|
||||
*/
|
||||
private Placeholder extractPlaceholder(String groupName) {
|
||||
|
||||
return !groupName.endsWith("'") && !groupName.endsWith("\"") ? //
|
||||
Placeholder.of(groupName, false) : //
|
||||
Placeholder.of(groupName.substring(0, groupName.length() - 1), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.9
|
||||
*/
|
||||
static class BindingContext {
|
||||
|
||||
final MongoParameters parameters;
|
||||
final List<ParameterBinding> bindings;
|
||||
final Map<Placeholder, ParameterBinding> bindings;
|
||||
|
||||
/**
|
||||
* Creates new {@link BindingContext}.
|
||||
*
|
||||
*
|
||||
* @param parameters
|
||||
* @param bindings
|
||||
*/
|
||||
public BindingContext(MongoParameters parameters, List<ParameterBinding> bindings) {
|
||||
|
||||
this.parameters = parameters;
|
||||
this.bindings = bindings;
|
||||
this.bindings = mapBindings(bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,21 +280,70 @@ class ExpressionEvaluatingParameterBinder {
|
||||
|
||||
/**
|
||||
* Get unmodifiable list of {@link ParameterBinding}s.
|
||||
*
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public List<ParameterBinding> getBindings() {
|
||||
return Collections.unmodifiableList(bindings);
|
||||
return new ArrayList<ParameterBinding>(bindings.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the concrete {@link ParameterBinding} for a given {@literal placeholder}.
|
||||
*
|
||||
* @param placeholder must not be {@literal null}.
|
||||
* @return
|
||||
* @throws java.util.NoSuchElementException
|
||||
* @since 1.10
|
||||
*/
|
||||
ParameterBinding getBindingFor(Placeholder placeholder) {
|
||||
|
||||
if (!bindings.containsKey(placeholder)) {
|
||||
throw new NoSuchElementException(String.format("Could not to find binding for placeholder '%s'.", placeholder));
|
||||
}
|
||||
|
||||
return bindings.get(placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated {@link MongoParameters}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public MongoParameters getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static Map<Placeholder, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
|
||||
|
||||
Map<Placeholder, ParameterBinding> map = new LinkedHashMap<Placeholder, ParameterBinding>(bindings.size(), 1);
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
map.put(Placeholder.of(binding.getParameter(), binding.isQuoted()), binding);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates a quoted/unquoted parameter placeholder.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.9
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
static class Placeholder {
|
||||
|
||||
private final String parameter;
|
||||
private final boolean quoted;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return quoted ? String.format("'%s'", parameter) : parameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -47,6 +47,7 @@ import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Custom query creator to create Mongo criterias.
|
||||
@@ -200,7 +201,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
case CONTAINING:
|
||||
return createContainingCriteria(part, property, criteria, parameters);
|
||||
case NOT_CONTAINING:
|
||||
return createContainingCriteria(part, property, criteria, parameters).not();
|
||||
return createContainingCriteria(part, property, criteria.not(), parameters);
|
||||
case REGEX:
|
||||
return criteria.regex(parameters.next().toString());
|
||||
case EXISTS:
|
||||
@@ -367,8 +368,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T nextAs(Iterator<Object> iterator, Class<T> type) {
|
||||
|
||||
Object parameter = iterator.next();
|
||||
if (parameter.getClass().isAssignableFrom(type)) {
|
||||
|
||||
if (ClassUtils.isAssignable(type, parameter.getClass())) {
|
||||
return (T) parameter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.repository.query;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
@@ -110,7 +112,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
|
||||
try {
|
||||
|
||||
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
|
||||
BasicQuery result = new BasicQuery(query.getQueryObject(), (DBObject) JSON.parse(fieldSpec));
|
||||
result.setSortObject(query.getSortObject());
|
||||
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Support class responsible for creating {@link MongoEntityInformation} instances for a given
|
||||
* {@link MongoPersistentEntity}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
final class MongoEntityInformationSupport {
|
||||
|
||||
private MongoEntityInformationSupport() {}
|
||||
|
||||
/**
|
||||
* Factory method for creating {@link MongoEntityInformation}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param idType can be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T, ID extends Serializable> MongoEntityInformation<T, ID> entityInformationFor(
|
||||
MongoPersistentEntity<?> entity, Class<?> idType) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
|
||||
MappingMongoEntityInformation<T, ID> entityInformation = new MappingMongoEntityInformation<T, ID>(
|
||||
(MongoPersistentEntity<T>) entity, (Class<ID>) idType);
|
||||
|
||||
return ClassUtils.isAssignable(Persistable.class, entity.getType())
|
||||
? new PersistableMongoEntityInformation<T, ID>(entityInformation) : entityInformation;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static org.springframework.data.querydsl.QueryDslUtils.*;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
@@ -42,6 +43,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Factory to create {@link MongoRepository} instances.
|
||||
@@ -123,8 +125,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
|
||||
String.format("Could not lookup mapping metadata for domain class %s!", domainClass.getName()));
|
||||
}
|
||||
|
||||
return new MappingMongoEntityInformation<T, ID>((MongoPersistentEntity<T>) entity,
|
||||
information != null ? (Class<ID>) information.getIdType() : null);
|
||||
return MongoEntityInformationSupport.<T, ID> entityInformationFor(entity,
|
||||
information != null ? information.getIdType() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
|
||||
/**
|
||||
* {@link MongoEntityInformation} implementation wrapping an existing {@link MongoEntityInformation} considering
|
||||
* {@link Persistable} types by delegating {@link #isNew(Object)} and {@link #getId(Object)} to the corresponding
|
||||
* {@link Persistable#isNew()} and {@link Persistable#getId()} implementations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class PersistableMongoEntityInformation<T, ID extends Serializable> implements MongoEntityInformation<T, ID> {
|
||||
|
||||
private final @NonNull MongoEntityInformation<T, ID> delegate;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
|
||||
*/
|
||||
@Override
|
||||
public String getCollectionName() {
|
||||
return delegate.getCollectionName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getIdAttribute()
|
||||
*/
|
||||
@Override
|
||||
public String getIdAttribute() {
|
||||
return delegate.getIdAttribute();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.EntityInformation#isNew(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isNew(T t) {
|
||||
|
||||
if (t instanceof Persistable) {
|
||||
return ((Persistable<ID>) t).isNew();
|
||||
}
|
||||
|
||||
return delegate.isNew(t);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ID getId(T t) {
|
||||
|
||||
if (t instanceof Persistable) {
|
||||
return (ID) ((Persistable<ID>) t).getId();
|
||||
}
|
||||
|
||||
return delegate.getId(t);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
|
||||
*/
|
||||
@Override
|
||||
public Class<ID> getIdType() {
|
||||
return delegate.getIdType();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.EntityMetadata#getJavaType()
|
||||
*/
|
||||
@Override
|
||||
public Class<T> getJavaType() {
|
||||
return delegate.getJavaType();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 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.
|
||||
@@ -27,7 +27,7 @@ import com.querydsl.mongodb.AbstractMongodbQuery;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
|
||||
public class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
|
||||
|
||||
private final MongoOperations operations;
|
||||
|
||||
|
||||
@@ -26,9 +26,11 @@ import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.querydsl.core.types.Constant;
|
||||
import com.querydsl.core.types.Path;
|
||||
import com.querydsl.core.types.PathMetadata;
|
||||
import com.querydsl.core.types.PathType;
|
||||
@@ -72,6 +74,20 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
this.mapper = new QueryMapper(converter);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.querydsl.mongodb.MongodbSerializer#visit(com.querydsl.core.types.Constant, java.lang.Void)
|
||||
*/
|
||||
@Override
|
||||
public Object visit(Constant<?> expr, Void context) {
|
||||
|
||||
if (!ClassUtils.isAssignable(Enum.class, expr.getType())) {
|
||||
return super.visit(expr, context);
|
||||
}
|
||||
|
||||
return converter.convertToMongoType(expr.getConstant());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.querydsl.mongodb.MongodbSerializer#getKeyForPath(com.querydsl.core.types.Path, com.querydsl.core.types.PathMetadata)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2017 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,16 +28,28 @@ public class MongoClientVersion {
|
||||
|
||||
private static final boolean IS_MONGO_30 = ClassUtils.isPresent("com.mongodb.binding.SingleServerBinding",
|
||||
MongoClientVersion.class.getClassLoader());
|
||||
|
||||
private static final boolean IS_MONGO_34 = ClassUtils.isPresent("org.bson.types.Decimal128",
|
||||
MongoClientVersion.class.getClassLoader());
|
||||
|
||||
private static final boolean IS_ASYNC_CLIENT = ClassUtils.isPresent("com.mongodb.async.client.MongoClient",
|
||||
MongoClientVersion.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* @return |literal true} if MongoDB Java driver version 3.0 or later is on classpath.
|
||||
* @return {@literal true} if MongoDB Java driver version 3.0 or later is on classpath.
|
||||
*/
|
||||
public static boolean isMongo3Driver() {
|
||||
return IS_MONGO_30;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if MongoDB Java driver version 3.4 or later is on classpath.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static boolean isMongo34Driver() {
|
||||
return IS_MONGO_34;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {lliteral true} if MongoDB Java driver is on classpath.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2016 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.
|
||||
@@ -19,8 +19,6 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -30,8 +28,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.mongodb.core.AuditablePerson;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@@ -123,22 +119,20 @@ public class AuditingViaJavaConfigRepositoriesTests {
|
||||
|
||||
@Configuration
|
||||
@EnableMongoRepositories
|
||||
@EnableMongoAuditing
|
||||
static class SimpleConfigWithRepositories {
|
||||
|
||||
@Bean
|
||||
public MongoTemplate mongoTemplate() throws UnknownHostException {
|
||||
return new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "database"));
|
||||
}
|
||||
}
|
||||
static class SimpleConfigWithRepositories extends SimpleConfig {}
|
||||
|
||||
@Configuration
|
||||
@EnableMongoAuditing
|
||||
static class SimpleConfig {
|
||||
static class SimpleConfig extends AbstractMongoConfiguration {
|
||||
|
||||
@Bean
|
||||
public MongoTemplate mongoTemplate() throws UnknownHostException {
|
||||
return new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "database"));
|
||||
@Override
|
||||
public Mongo mongo() throws Exception {
|
||||
return new MongoClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
return "database";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Ignore;
|
||||
@@ -37,6 +38,13 @@ import com.mongodb.CommandResult;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoNamespaceReplicaSetTests {
|
||||
@@ -70,10 +78,13 @@ public class MongoNamespaceReplicaSetTests {
|
||||
|
||||
assertThat(replicaSetSeeds, is(notNullValue()));
|
||||
assertThat(replicaSetSeeds, hasSize(3));
|
||||
assertThat(
|
||||
replicaSetSeeds,
|
||||
hasItems(new ServerAddress("192.168.174.130", 27017), new ServerAddress("192.168.174.130", 27018),
|
||||
new ServerAddress("192.168.174.130", 27019)));
|
||||
|
||||
List<Integer> ports = new ArrayList<Integer>();
|
||||
for (ServerAddress replicaSetSeed : replicaSetSeeds) {
|
||||
ports.add(replicaSetSeed.getPort());
|
||||
}
|
||||
|
||||
assertThat(ports, hasItems(27017, 27018, 27019));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.mongodb.WriteConcern;
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -270,6 +271,25 @@ public class DefaultBulkOperationsIntegrationTests {
|
||||
assertThat(result.getRemovedCount(), is(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1534
|
||||
*/
|
||||
@Test
|
||||
public void insertShouldConsiderInheritance() {
|
||||
|
||||
SpecialDoc specialDoc = new SpecialDoc();
|
||||
specialDoc.id = "id-special";
|
||||
specialDoc.value = "normal-value";
|
||||
specialDoc.specialValue = "special-value";
|
||||
|
||||
createBulkOps(BulkMode.ORDERED).insert(Arrays.asList(specialDoc)).execute();
|
||||
|
||||
BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME);
|
||||
|
||||
assertThat(doc, notNullValue());
|
||||
assertThat(doc, instanceOf(SpecialDoc.class));
|
||||
}
|
||||
|
||||
private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) {
|
||||
|
||||
BulkOperations bulkOps = createBulkOps(mode);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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,18 +17,28 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.data.mongodb.core.ReflectiveDBCollectionInvoker.*;
|
||||
import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.index.Index;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.CommandResult;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
@@ -42,6 +52,9 @@ import com.mongodb.DBObject;
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class DefaultIndexOperationsIntegrationTests {
|
||||
|
||||
private static final Version THREE_DOT_TWO = new Version(3, 2);
|
||||
private static Version mongoVersion;
|
||||
|
||||
static final DBObject GEO_SPHERE_2D = new BasicDBObject("loaction", "2dsphere");
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
@@ -51,6 +64,7 @@ public class DefaultIndexOperationsIntegrationTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
queryMongoVersionIfNecessary();
|
||||
String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class);
|
||||
|
||||
this.collection = this.template.getDb().getCollection(collectionName);
|
||||
@@ -59,6 +73,14 @@ public class DefaultIndexOperationsIntegrationTests {
|
||||
this.indexOps = new DefaultIndexOperations(template, collectionName);
|
||||
}
|
||||
|
||||
private void queryMongoVersionIfNecessary() {
|
||||
|
||||
if (mongoVersion == null) {
|
||||
CommandResult result = template.executeCommand("{ buildInfo: 1 }");
|
||||
mongoVersion = Version.parse(result.get("version").toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1008
|
||||
*/
|
||||
@@ -71,6 +93,78 @@ public class DefaultIndexOperationsIntegrationTests {
|
||||
assertThat(info.getIndexFields().get(0).isGeo(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1467
|
||||
*/
|
||||
@Test
|
||||
public void shouldApplyPartialFilterCorrectly() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
|
||||
|
||||
IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC)
|
||||
.partial(of(where("q-t-y").gte(10)));
|
||||
|
||||
indexOps.ensureIndex(id);
|
||||
|
||||
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-criteria");
|
||||
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"q-t-y\" : { \"$gte\" : 10}}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1467
|
||||
*/
|
||||
@Test
|
||||
public void shouldApplyPartialFilterWithMappedPropertyCorrectly() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
|
||||
|
||||
IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC)
|
||||
.partial(of(where("quantity").gte(10)));
|
||||
|
||||
indexOps.ensureIndex(id);
|
||||
|
||||
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-mapped-criteria");
|
||||
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1467
|
||||
*/
|
||||
@Test
|
||||
public void shouldApplyPartialDBOFilterCorrectly() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
|
||||
|
||||
IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC)
|
||||
.partial(of(new BasicDBObject("qty", new BasicDBObject("$gte", 10))));
|
||||
|
||||
indexOps.ensureIndex(id);
|
||||
|
||||
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-dbo");
|
||||
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1467
|
||||
*/
|
||||
@Test
|
||||
public void shouldFavorExplicitMappingHintViaClass() {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
|
||||
|
||||
IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC)
|
||||
.partial(of(where("age").gte(10)));
|
||||
|
||||
indexOps = new DefaultIndexOperations(template,
|
||||
this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class),
|
||||
MappingToSameCollection.class);
|
||||
|
||||
indexOps.ensureIndex(id);
|
||||
|
||||
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-inheritance");
|
||||
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"a_g_e\" : { \"$gte\" : 10}}")));
|
||||
}
|
||||
|
||||
private IndexInfo findAndReturnIndexInfo(DBObject keys) {
|
||||
return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys);
|
||||
}
|
||||
@@ -89,5 +183,15 @@ public class DefaultIndexOperationsIntegrationTests {
|
||||
throw new AssertionError(String.format("Index with %s was not found", name));
|
||||
}
|
||||
|
||||
static class DefaultIndexOperationsIntegrationTestsSample {}
|
||||
@Document(collection = "default-index-operations-tests")
|
||||
static class DefaultIndexOperationsIntegrationTestsSample {
|
||||
|
||||
@Field("qty") Integer quantity;
|
||||
}
|
||||
|
||||
@Document(collection = "default-index-operations-tests")
|
||||
static class MappingToSameCollection extends DefaultIndexOperationsIntegrationTestsSample {
|
||||
|
||||
@Field("a_g_e") Integer age;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -190,4 +190,12 @@ public class DefaultScriptOperationsTests {
|
||||
public void scriptNamesShouldReturnEmptySetWhenNoScriptRegistered() {
|
||||
assertThat(scriptOps.getScriptNames(), is(empty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1465
|
||||
*/
|
||||
@Test
|
||||
public void executeShouldNotQuoteStrings() {
|
||||
assertThat(scriptOps.execute(EXECUTABLE_SCRIPT, "spring-data"), is((Object) "spring-data"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.springframework.data.mongodb.core.query.Update.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -33,6 +37,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -43,6 +48,7 @@ import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -70,13 +76,18 @@ import org.springframework.data.mongodb.core.index.IndexField;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
|
||||
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -111,9 +122,12 @@ public class MongoTemplateTests {
|
||||
.parse("2.4");
|
||||
private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version
|
||||
.parse("2.8");
|
||||
private static final org.springframework.data.util.Version THREE_DOT_FOUR = org.springframework.data.util.Version
|
||||
.parse("3.4");
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
@Autowired MongoDbFactory factory;
|
||||
@Autowired ConfigurableApplicationContext context;
|
||||
|
||||
MongoTemplate mappingTemplate;
|
||||
org.springframework.data.util.Version mongoVersion;
|
||||
@@ -123,8 +137,8 @@ public class MongoTemplateTests {
|
||||
@Autowired
|
||||
public void setMongo(Mongo mongo) throws Exception {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(DateToDateTimeConverter.INSTANCE,
|
||||
DateTimeToDateConverter.INSTANCE));
|
||||
CustomConversions conversions = new CustomConversions(
|
||||
Arrays.asList(DateToDateTimeConverter.INSTANCE, DateTimeToDateConverter.INSTANCE));
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
mappingContext.setInitialEntitySet(new HashSet<Class<?>>(Arrays.asList(PersonWith_idPropertyOfTypeObjectId.class,
|
||||
@@ -2571,7 +2585,7 @@ public class MongoTemplateTests {
|
||||
doc.dbRefAnnotatedList = Arrays.asList( //
|
||||
sample1, //
|
||||
sample2 //
|
||||
);
|
||||
);
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList", doc.dbRefAnnotatedList.get(1));
|
||||
@@ -2603,7 +2617,7 @@ public class MongoTemplateTests {
|
||||
doc.dbRefAnnotatedList = Arrays.asList( //
|
||||
sample1, //
|
||||
sample2 //
|
||||
);
|
||||
);
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList.id", "2");
|
||||
@@ -2677,8 +2691,8 @@ public class MongoTemplateTests {
|
||||
@Test
|
||||
public void testUpdateShouldWorkForPathsOnInterfaceMethods() {
|
||||
|
||||
DocumentWithCollection document = new DocumentWithCollection(Arrays.<Model> asList(new ModelA("spring"),
|
||||
new ModelA("data")));
|
||||
DocumentWithCollection document = new DocumentWithCollection(
|
||||
Arrays.<Model> asList(new ModelA("spring"), new ModelA("data")));
|
||||
|
||||
template.save(document);
|
||||
|
||||
@@ -3164,6 +3178,62 @@ public class MongoTemplateTests {
|
||||
assertThat(template.findOne(query(where("id").is(wgj.id)), WithGeoJson.class).point, is(equalTo(wgj.point)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1513
|
||||
*/
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void populatesIdsAddedByEventListener() {
|
||||
|
||||
context.addApplicationListener(new AbstractMongoEventListener<Document>() {
|
||||
|
||||
@Override
|
||||
public void onBeforeSave(BeforeSaveEvent<Document> event) {
|
||||
event.getDBObject().put("_id", UUID.randomUUID().toString());
|
||||
}
|
||||
});
|
||||
|
||||
Document document = new Document();
|
||||
|
||||
template.insertAll(Arrays.asList(document));
|
||||
|
||||
assertThat(document.id, is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1517
|
||||
*/
|
||||
@Test
|
||||
public void decimal128TypeShouldBeSavedAndLoadedCorrectly()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
|
||||
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR), is(true));
|
||||
assumeThat(MongoClientVersion.isMongo34Driver(), is(true));
|
||||
|
||||
Class<?> decimal128Type = ClassUtils.resolveClassName("org.bson.types.Decimal128", null);
|
||||
|
||||
WithObjectTypeProperty source = new WithObjectTypeProperty();
|
||||
source.id = "decimal128-property-value";
|
||||
source.value = decimal128Type.getConstructor(BigDecimal.class).newInstance(new BigDecimal(100));
|
||||
|
||||
template.save(source);
|
||||
|
||||
WithObjectTypeProperty loaded = template.findOne(query(where("id").is(source.id)), WithObjectTypeProperty.class);
|
||||
assertThat(loaded.getValue(), instanceOf(decimal128Type));
|
||||
}
|
||||
|
||||
static class TypeWithNumbers {
|
||||
|
||||
@Id String id;
|
||||
Integer intVal;
|
||||
Float floatVal;
|
||||
Long longVal;
|
||||
Double doubleVal;
|
||||
BigDecimal bigDeciamVal;
|
||||
BigInteger bigIntegerVal;
|
||||
Byte byteVal;
|
||||
}
|
||||
|
||||
static class DoucmentWithNamedIdField {
|
||||
|
||||
@Id String someIdKey;
|
||||
@@ -3215,11 +3285,11 @@ public class MongoTemplateTests {
|
||||
|
||||
@Id public String id;
|
||||
|
||||
@Field("db_ref_list")/** @see DATAMONGO-1058 */
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
@Field("db_ref_list") /** @see DATAMONGO-1058 */
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef //
|
||||
public List<Sample> dbRefAnnotatedList;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef //
|
||||
public Sample dbRefProperty;
|
||||
}
|
||||
|
||||
@@ -3514,4 +3584,10 @@ public class MongoTemplateTests {
|
||||
GeoJsonPoint point;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class WithObjectTypeProperty {
|
||||
|
||||
@Id String id;
|
||||
Object value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2017 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.
|
||||
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.data.domain.Sort.Direction;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Aggregation}.
|
||||
@@ -204,6 +206,47 @@ public class AggregationUnitTests {
|
||||
assertThat(id.get("ruleType"), is((Object) "$rules.ruleType"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingBySyntheticAndExposedGroupFields() {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
group("cmsParameterId").addToSet("title").as("titles"), //
|
||||
sort(Direction.ASC, "cmsParameterId", "titles") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(notNullValue()));
|
||||
|
||||
DBObject sort = ((List<DBObject>) agg.get("pipeline")).get(1);
|
||||
|
||||
assertThat(getAsDBObject(sort, "$sort"), is(JSON.parse("{ \"_id.cmsParameterId\" : 1 , \"titles\" : 1}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingByProjectedFields() {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
project("cmsParameterId") //
|
||||
.and(SystemVariable.CURRENT + ".titles").as("titles") //
|
||||
.and("field").as("alias"), //
|
||||
sort(Direction.ASC, "cmsParameterId", "titles", "alias") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(notNullValue()));
|
||||
|
||||
DBObject sort = ((List<DBObject>) agg.get("pipeline")).get(1);
|
||||
|
||||
assertThat(getAsDBObject(sort, "$sort"),
|
||||
isBsonObject().containing("cmsParameterId", 1) //
|
||||
.containing("titles", 1) //
|
||||
.containing("alias", 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-924
|
||||
*/
|
||||
@@ -248,19 +291,20 @@ public class AggregationUnitTests {
|
||||
DBObject agg = newAggregation( //
|
||||
project().and("a").as("aa") //
|
||||
) //
|
||||
.withOptions(aggregationOptions) //
|
||||
.withOptions(aggregationOptions) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg.toString(), is("{ \"aggregate\" : \"foo\" , " //
|
||||
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
|
||||
+ "\"allowDiskUse\" : true , " //
|
||||
+ "\"explain\" : true , " //
|
||||
+ "\"cursor\" : { \"foo\" : 1}}" //
|
||||
));
|
||||
assertThat(agg.toString(),
|
||||
is("{ \"aggregate\" : \"foo\" , " //
|
||||
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
|
||||
+ "\"allowDiskUse\" : true , " //
|
||||
+ "\"explain\" : true , " //
|
||||
+ "\"cursor\" : { \"foo\" : 1}}" //
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-954
|
||||
* @see DATAMONGO-954, DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportReferencingSystemVariables() {
|
||||
@@ -269,16 +313,16 @@ public class AggregationUnitTests {
|
||||
project("someKey") //
|
||||
.and("a").as("a1") //
|
||||
.and(Aggregation.CURRENT + ".a").as("a2") //
|
||||
, sort(Direction.DESC, "a") //
|
||||
, sort(Direction.DESC, "a1") //
|
||||
, group("someKey").first(Aggregation.ROOT).as("doc") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject projection0 = extractPipelineElement(agg, 0, "$project");
|
||||
assertThat(projection0, is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a")
|
||||
.append("a2", "$$CURRENT.a")));
|
||||
assertThat(projection0,
|
||||
is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a").append("a2", "$$CURRENT.a")));
|
||||
|
||||
DBObject sort = extractPipelineElement(agg, 1, "$sort");
|
||||
assertThat(sort, is((DBObject) new BasicDBObject("a", -1)));
|
||||
assertThat(sort, is((DBObject) new BasicDBObject("a1", -1)));
|
||||
|
||||
DBObject group = extractPipelineElement(agg, 2, "$group");
|
||||
assertThat(group,
|
||||
@@ -296,7 +340,7 @@ public class AggregationUnitTests {
|
||||
.and("tags").minus(10).as("tags_count")//
|
||||
, group("date")//
|
||||
.sum("tags_count").as("count")//
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject group = extractPipelineElement(agg, 1, "$group");
|
||||
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
|
||||
@@ -313,7 +357,7 @@ public class AggregationUnitTests {
|
||||
.andExpression("tags-10")//
|
||||
, group("date")//
|
||||
.sum("tags_count").as("count")//
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject group = extractPipelineElement(agg, 1, "$group");
|
||||
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2017 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.
|
||||
@@ -46,11 +46,17 @@ public class ProjectionOperationUnitTests {
|
||||
static final String DIVIDE = "$divide";
|
||||
static final String PROJECT = "$project";
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullFields() {
|
||||
new ProjectionOperation(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void declaresBackReferenceCorrectly() {
|
||||
|
||||
@@ -62,6 +68,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(projectClause.get("prop"), is((Object) Fields.UNDERSCORE_ID_REF));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void alwaysUsesExplicitReference() {
|
||||
|
||||
@@ -74,6 +83,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(projectClause.get("bar"), is((Object) "$foobar"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void aliasesSimpleFieldProjection() {
|
||||
|
||||
@@ -85,6 +97,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(projectClause.get("bar"), is((Object) "$foo"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void aliasesArithmeticProjection() {
|
||||
|
||||
@@ -100,6 +115,10 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(addClause.get(1), is((Object) 41));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationWithoutAlias() {
|
||||
|
||||
String fieldName = "a";
|
||||
@@ -112,6 +131,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationPlus() {
|
||||
|
||||
@@ -126,6 +148,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationMinus() {
|
||||
|
||||
@@ -140,6 +165,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(oper.get(SUBTRACT), is((Object) Arrays.<Object> asList("$a", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationMultiply() {
|
||||
|
||||
@@ -154,6 +182,9 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(oper.get(MULTIPLY), is((Object) Arrays.<Object> asList("$a", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationDivide() {
|
||||
|
||||
@@ -168,12 +199,18 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(oper.get(DIVIDE), is((Object) Arrays.<Object> asList("$a", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void arithmenticProjectionOperationDivideByZeroException() {
|
||||
|
||||
new ProjectionOperation().and("a").divide(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-586
|
||||
*/
|
||||
@Test
|
||||
public void arithmenticProjectionOperationMod() {
|
||||
|
||||
@@ -273,9 +310,8 @@ public class ProjectionOperationUnitTests {
|
||||
.and("foo").as("bar"); //
|
||||
|
||||
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
assertThat(
|
||||
dbObject.toString(),
|
||||
is("{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
|
||||
assertThat(dbObject.toString(), is(
|
||||
"{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,10 +366,8 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(dbObject, is(notNullValue()));
|
||||
|
||||
DBObject projected = exctractOperation("$project", dbObject);
|
||||
assertThat(
|
||||
projected.get("dayOfYearPlus1Day"),
|
||||
is((Object) new BasicDBObject("$dayOfYear", Arrays.asList(new BasicDBObject("$add", Arrays.<Object> asList(
|
||||
"$date", 86400000))))));
|
||||
assertThat(projected.get("dayOfYearPlus1Day"), is((Object) new BasicDBObject("$dayOfYear",
|
||||
Arrays.asList(new BasicDBObject("$add", Arrays.<Object> asList("$date", 86400000))))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 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,7 +17,9 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -171,6 +173,44 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
assertThat(dbo.get("cursor"), is((Object) new BasicDBObject("foo", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void rendersSortOfProjectedFieldCorrectly() {
|
||||
|
||||
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
|
||||
TypedAggregation<MeterData> agg = newAggregation(MeterData.class, project().and("counterName").as("counter"), //
|
||||
sort(Direction.ASC, "counter"));
|
||||
|
||||
DBObject dbo = agg.toDbObject("meterData", context);
|
||||
DBObject sort = getPipelineElementFromAggregationAt(dbo, 1);
|
||||
|
||||
DBObject definition = (DBObject) sort.get("$sort");
|
||||
assertThat(definition.get("counter"), is(equalTo((Object) 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1586
|
||||
*/
|
||||
@Test
|
||||
public void rendersFieldAliasingProjectionCorrectly() {
|
||||
|
||||
AggregationOperationContext context = getContext(FooPerson.class);
|
||||
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
|
||||
project() //
|
||||
.and("name").as("person_name") //
|
||||
.and("age.value").as("age"));
|
||||
|
||||
DBObject dbo = agg.toDbObject("person", context);
|
||||
|
||||
DBObject projection = getPipelineElementFromAggregationAt(dbo, 0);
|
||||
assertThat(getAsDBObject(projection, "$project"),
|
||||
isBsonObject() //
|
||||
.containing("person_name", "$name") //
|
||||
.containing("age", "$age.value"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1133
|
||||
*/
|
||||
@@ -190,14 +230,15 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
* @see DATAMONGO-1326, DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void lookupShouldInheritFieldsFromInheritingAggregationOperation() {
|
||||
|
||||
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
|
||||
TypedAggregation<MeterData> agg = newAggregation(MeterData.class,
|
||||
lookup("OtherCollection", "resourceId", "otherId", "lookup"), sort(Direction.ASC, "resourceId"));
|
||||
lookup("OtherCollection", "resourceId", "otherId", "lookup"), //
|
||||
sort(Direction.ASC, "resourceId", "counterName"));
|
||||
|
||||
DBObject dbo = agg.toDbObject("meterData", context);
|
||||
DBObject sort = getPipelineElementFromAggregationAt(dbo, 1);
|
||||
@@ -205,6 +246,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
DBObject definition = (DBObject) sort.get("$sort");
|
||||
|
||||
assertThat(definition.get("resourceId"), is(equalTo((Object) 1)));
|
||||
assertThat(definition.get("counter_name"), is(equalTo((Object) 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,6 +101,20 @@ public class DBObjectAccessorUnitTests {
|
||||
assertThat(nestedA.get("c"), is((Object) "c"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1471
|
||||
*/
|
||||
@Test
|
||||
public void exposesAvailabilityOfFields() {
|
||||
|
||||
DBObjectAccessor accessor = new DBObjectAccessor(new BasicDBObject("a", new BasicDBObject("c", "d")));
|
||||
MongoPersistentEntity<?> entity = context.getPersistentEntity(ProjectingType.class);
|
||||
|
||||
assertThat(accessor.hasValue(entity.getPersistentProperty("foo")), is(false));
|
||||
assertThat(accessor.hasValue(entity.getPersistentProperty("a")), is(true));
|
||||
assertThat(accessor.hasValue(entity.getPersistentProperty("name")), is(false));
|
||||
}
|
||||
|
||||
static class ProjectingType {
|
||||
|
||||
String name;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.LazyLoadingException;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LazyLoadingInterceptor}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LazyLoadingInterceptorUnitTests {
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Mock MongoPersistentProperty propertyMock;
|
||||
@Mock DBRef dbrefMock;
|
||||
@Mock DbRefResolverCallback callbackMock;
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1437
|
||||
*/
|
||||
@Test
|
||||
public void shouldPreserveCauseForNonTranslatableExceptions() throws Throwable {
|
||||
|
||||
NullPointerException npe = new NullPointerException("Some Exception we did not think about.");
|
||||
when(callbackMock.resolve(propertyMock)).thenThrow(npe);
|
||||
|
||||
exception.expect(LazyLoadingException.class);
|
||||
exception.expectCause(is(equalTo(npe)));
|
||||
|
||||
new LazyLoadingInterceptor(propertyMock, dbrefMock, new NullExceptionTranslator(), callbackMock).intercept(null,
|
||||
LazyLoadingProxy.class.getMethod("getTarget"), null, null);
|
||||
}
|
||||
|
||||
static class NullExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2066,6 +2066,37 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(target.map.get(FooBarEnum.FOO), is("spring"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1471
|
||||
*/
|
||||
@Test
|
||||
public void readsDocumentWithPrimitiveIdButNoValue() {
|
||||
assertThat(converter.read(ClassWithIntId.class, new BasicDBObject()), is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1497
|
||||
*/
|
||||
@Test
|
||||
public void readsPropertyFromNestedFieldCorrectly() {
|
||||
|
||||
DBObject source = new BasicDBObject("nested", new BasicDBObject("sample", "value"));
|
||||
TypeWithPropertyInNestedField result = converter.read(TypeWithPropertyInNestedField.class, source);
|
||||
|
||||
assertThat(result.sample, is("value"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1525
|
||||
*/
|
||||
@Test
|
||||
public void readsEmptyEnumSet() {
|
||||
|
||||
DBObject source = new BasicDBObject("enumSet", new BasicDBList());
|
||||
|
||||
assertThat(converter.read(ClassWithEnumProperty.class, source).enumSet, is(EnumSet.noneOf(SampleEnum.class)));
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
@@ -2412,4 +2443,8 @@ public class MappingMongoConverterUnitTests {
|
||||
throw new ConversionNotSupportedException(source, String.class, null);
|
||||
}
|
||||
}
|
||||
|
||||
static class TypeWithPropertyInNestedField {
|
||||
@Field("nested.sample") String sample;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.geo.Box;
|
||||
@@ -27,8 +29,12 @@ import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicIntegerToIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicLongToLongConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.CurrencyToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.IntegerToAtomicIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.LongToAtomicLongConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToCurrencyConverter;
|
||||
import org.springframework.data.mongodb.core.geo.Sphere;
|
||||
@@ -140,4 +146,36 @@ public class MongoConvertersUnitTests {
|
||||
public void convertsStringToCurrencyCorrectly() {
|
||||
assertThat(StringToCurrencyConverter.INSTANCE.convert("USD"), is(Currency.getInstance("USD")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1416
|
||||
*/
|
||||
@Test
|
||||
public void convertsAtomicLongToLongCorrectly() {
|
||||
assertThat(AtomicLongToLongConverter.INSTANCE.convert(new AtomicLong(100L)), is(100L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1416
|
||||
*/
|
||||
@Test
|
||||
public void convertsAtomicIntegerToIntegerCorrectly() {
|
||||
assertThat(AtomicIntegerToIntegerConverter.INSTANCE.convert(new AtomicInteger(100)), is(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1416
|
||||
*/
|
||||
@Test
|
||||
public void convertsLongToAtomicLongCorrectly() {
|
||||
assertThat(LongToAtomicLongConverter.INSTANCE.convert(100L), is(instanceOf(AtomicLong.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1416
|
||||
*/
|
||||
@Test
|
||||
public void convertsIntegerToAtomicIntegerCorrectly() {
|
||||
assertThat(IntegerToAtomicIntegerConverter.INSTANCE.convert(100), is(instanceOf(AtomicInteger.class)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -35,6 +35,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
@@ -54,6 +55,7 @@ import org.springframework.data.mongodb.core.mapping.TextScore;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -68,6 +70,7 @@ import com.mongodb.QueryBuilder;
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QueryMapperUnitTests {
|
||||
@@ -595,6 +598,28 @@ public class QueryMapperUnitTests {
|
||||
assertThat(dbo.toString(), equalTo("{ \"embedded\" : { \"$in\" : [ { \"_id\" : \"1\"} , { \"_id\" : \"2\"}]}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1406
|
||||
*/
|
||||
@Test
|
||||
public void shouldMapQueryForNestedCustomizedPropertiesUsingConfiguredFieldNames() {
|
||||
|
||||
EmbeddedClass embeddedClass = new EmbeddedClass();
|
||||
embeddedClass.customizedField = "hello";
|
||||
|
||||
Foo foo = new Foo();
|
||||
foo.listOfItems = Arrays.asList(embeddedClass);
|
||||
|
||||
Query query = new Query(Criteria.where("listOfItems") //
|
||||
.elemMatch(new Criteria(). //
|
||||
andOperator(Criteria.where("customizedField").is(embeddedClass.customizedField))));
|
||||
|
||||
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class));
|
||||
|
||||
assertThat(dbo, isBsonObject().containing("my_items.$elemMatch.$and",
|
||||
new BasicDbListBuilder().add(new BasicDBObject("fancy_custom_name", embeddedClass.customizedField)).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-647
|
||||
*/
|
||||
@@ -792,8 +817,7 @@ public class QueryMapperUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* <<<<<<< HEAD
|
||||
*
|
||||
*
|
||||
* @see DATAMONGO-1269
|
||||
*/
|
||||
@Test
|
||||
@@ -859,10 +883,15 @@ public class QueryMapperUnitTests {
|
||||
public class Foo {
|
||||
@Id private ObjectId id;
|
||||
EmbeddedClass embedded;
|
||||
|
||||
@Field("my_items")
|
||||
List<EmbeddedClass> listOfItems;
|
||||
}
|
||||
|
||||
public class EmbeddedClass {
|
||||
public String id;
|
||||
|
||||
@Field("fancy_custom_name") public String customizedField;
|
||||
}
|
||||
|
||||
class IdWrapper {
|
||||
|
||||
@@ -887,6 +887,50 @@ public class UpdateMapperUnitTests {
|
||||
assertThat($set.get("primIntValue"), Is.<Object> is(10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1423
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mappingShouldConsiderCustomConvertersForEnumMapKeys() {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(
|
||||
Arrays.asList(AllocationToStringConverter.INSTANCE, StringToAllocationConverter.INSTANCE));
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
|
||||
mappingContext.afterPropertiesSet();
|
||||
|
||||
MappingMongoConverter converter = new MappingMongoConverter(mock(DbRefResolver.class), mappingContext);
|
||||
converter.setCustomConversions(conversions);
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
UpdateMapper mapper = new UpdateMapper(converter);
|
||||
|
||||
Update update = new Update().set("enumAsMapKey", Collections.singletonMap(Allocation.AVAILABLE, 100));
|
||||
DBObject result = mapper.getMappedObject(update.getUpdateObject(),
|
||||
mappingContext.getPersistentEntity(ClassWithEnum.class));
|
||||
|
||||
assertThat(result, isBsonObject().containing("$set.enumAsMapKey.V", 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1486
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldConvertMapKeysToString() {
|
||||
|
||||
Update update = new Update().set("map", Collections.singletonMap(25, "#StarTrek50"));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObjectMap.class));
|
||||
|
||||
DBObject mapToSet = getAsDBObject(getAsDBObject(mappedUpdate, "$set"), "map");
|
||||
|
||||
for (Object key : mapToSet.keySet()) {
|
||||
assertThat(key, is(instanceOf(String.class)));
|
||||
}
|
||||
}
|
||||
|
||||
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
|
||||
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
|
||||
}
|
||||
@@ -1113,6 +1157,7 @@ public class UpdateMapperUnitTests {
|
||||
static class ClassWithEnum {
|
||||
|
||||
Allocation allocation;
|
||||
Map<Allocation, String> enumAsMapKey;
|
||||
|
||||
static enum Allocation {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 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;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
|
||||
import org.springframework.data.mongodb.core.CollectionCallback;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
@@ -43,11 +45,15 @@ import org.springframework.data.mongodb.core.index.GeospatialIndex;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
@@ -317,6 +323,66 @@ public class GeoJsonTests {
|
||||
assertThat(venues.size(), is(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1453
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertPointRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() {
|
||||
|
||||
this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class),
|
||||
new CollectionCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
|
||||
BasicDBObject pointRepresentation = new BasicDBObject();
|
||||
pointRepresentation.put("type", "Point");
|
||||
pointRepresentation.put("coordinates", new BasicDbListBuilder().add(0).add(0).get());
|
||||
|
||||
BasicDBObject document = new BasicDBObject();
|
||||
document.append("_id", "datamongo-1453");
|
||||
document.append("geoJsonPoint", pointRepresentation);
|
||||
|
||||
return collection.save(document);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(template.findOne(query(where("id").is("datamongo-1453")),
|
||||
DocumentWithPropertyUsingGeoJsonType.class).geoJsonPoint, is(equalTo(new GeoJsonPoint(0D, 0D))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1453
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertLineStringRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() {
|
||||
|
||||
this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class),
|
||||
new CollectionCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
|
||||
BasicDBObject lineStringRepresentation = new BasicDBObject();
|
||||
lineStringRepresentation.put("type", "LineString");
|
||||
lineStringRepresentation.put("coordinates",
|
||||
new BasicDbListBuilder().add(new BasicDbListBuilder().add(0).add(0).get())
|
||||
.add(new BasicDbListBuilder().add(1).add(1).get()).get());
|
||||
|
||||
BasicDBObject document = new BasicDBObject();
|
||||
document.append("_id", "datamongo-1453");
|
||||
document.append("geoJsonLineString", lineStringRepresentation);
|
||||
|
||||
return collection.save(document);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(
|
||||
template.findOne(query(where("id").is("datamongo-1453")),
|
||||
DocumentWithPropertyUsingGeoJsonType.class).geoJsonLineString,
|
||||
is(equalTo(new GeoJsonLineString(new Point(0D, 0D), new Point(1, 1)))));
|
||||
}
|
||||
|
||||
private void addVenues() {
|
||||
|
||||
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -27,7 +28,9 @@ import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
@@ -101,5 +104,7 @@ public class AuditingEventListenerUnitTests {
|
||||
static class Sample {
|
||||
|
||||
@Id String id;
|
||||
@CreatedDate Date created;
|
||||
@LastModifiedDate Date modified;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 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.
|
||||
@@ -74,7 +74,7 @@ public class PerformanceTests {
|
||||
private static final int ITERATIONS = 50;
|
||||
private static final StopWatch watch = new StopWatch();
|
||||
private static final Collection<String> IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE",
|
||||
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED");
|
||||
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED", "W2", "W3");
|
||||
private static final int COLLECTION_SIZE = 1024 * 1024 * 256; // 256 MB
|
||||
private static final Collection<String> COLLECTION_NAMES = Arrays.asList("template", "driver", "person");
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder;
|
||||
@@ -86,8 +87,10 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
dave = new Person("Dave", "Matthews", 42);
|
||||
oliver = new Person("Oliver August", "Matthews", 4);
|
||||
carter = new Person("Carter", "Beauford", 49);
|
||||
carter.setSkills(Arrays.asList("Drums", "percussion", "vocals"));
|
||||
Thread.sleep(10);
|
||||
boyd = new Person("Boyd", "Tinsley", 45);
|
||||
boyd.setSkills(Arrays.asList("Violin", "Electric Violin", "Viola", "Mandolin", "Vocals", "Guitar"));
|
||||
stefan = new Person("Stefan", "Lessard", 34);
|
||||
leroi = new Person("Leroi", "Moore", 41);
|
||||
|
||||
@@ -270,6 +273,18 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result, hasItem(dave));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1588
|
||||
public void findsPeopleByLocationNearUsingGeoJsonType() {
|
||||
|
||||
GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868);
|
||||
dave.setLocation(point);
|
||||
repository.save(dave);
|
||||
|
||||
List<Person> result = repository.findByLocationNear(point);
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result, hasItem(dave));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsPeopleByLocationWithinCircle() {
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
@@ -1261,4 +1276,37 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result.size(), is(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1425
|
||||
*/
|
||||
@Test
|
||||
public void findsPersonsByFirstnameNotContains() throws Exception {
|
||||
|
||||
List<Person> result = repository.findByFirstnameNotContains("Boyd");
|
||||
assertThat(result.size(), is((int) (repository.count() - 1)));
|
||||
assertThat(result, not(hasItem(boyd)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1425
|
||||
*/
|
||||
@Test
|
||||
public void findBySkillsContains() throws Exception {
|
||||
|
||||
List<Person> result = repository.findBySkillsContains(Arrays.asList("Drums"));
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result, hasItem(carter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1425
|
||||
*/
|
||||
@Test
|
||||
public void findBySkillsNotContains() throws Exception {
|
||||
|
||||
List<Person> result = repository.findBySkillsNotContains(Arrays.asList("Drums"));
|
||||
assertThat(result.size(), is((int) (repository.count() - 1)));
|
||||
assertThat(result, not(hasItem(carter)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,8 +89,14 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
*/
|
||||
List<Person> findByFirstnameLike(String firstname);
|
||||
|
||||
List<Person> findByFirstnameNotContains(String firstname);
|
||||
|
||||
List<Person> findByFirstnameLikeOrderByLastnameAsc(String firstname, Sort sort);
|
||||
|
||||
List<Person> findBySkillsContains(List<String> skills);
|
||||
|
||||
List<Person> findBySkillsNotContains(List<String> skills);
|
||||
|
||||
@Query("{'age' : { '$lt' : ?0 } }")
|
||||
List<Person> findByAgeLessThan(int age, Sort sort);
|
||||
|
||||
@@ -309,7 +315,8 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Query("{lastname:?0, address.street:{$in:?1}}")
|
||||
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames, Pageable page);
|
||||
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames,
|
||||
Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-950
|
||||
@@ -334,19 +341,19 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
*/
|
||||
@Query("{ firstname : { $in : ?0 }}")
|
||||
Stream<Person> findByCustomQueryWithStreamingCursorByFirstnames(List<String> firstnames);
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-990
|
||||
*/
|
||||
@Query("{ firstname : ?#{[0]}}")
|
||||
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly(String firstname);
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-990
|
||||
*/
|
||||
@Query("{ firstname : ?#{[0]}, email: ?#{principal.email} }")
|
||||
List<Person> findWithSpelByFirstnameAndCurrentUserWithCustomQuery(String firstname);
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-990
|
||||
*/
|
||||
|
||||
@@ -44,6 +44,8 @@ import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
@@ -69,8 +71,6 @@ import com.mongodb.DBObject;
|
||||
*/
|
||||
public class MongoQueryCreatorUnitTests {
|
||||
|
||||
Method findByFirstname, findByFirstnameAndFriend, findByFirstnameNotNull;
|
||||
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
|
||||
MongoConverter converter;
|
||||
|
||||
@@ -454,6 +454,7 @@ public class MongoQueryCreatorUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1075
|
||||
* @see DATAMONGO-1425
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() {
|
||||
@@ -462,14 +463,14 @@ public class MongoQueryCreatorUnitTests {
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex(".*thew.*").not())));
|
||||
assertThat(query.getQueryObject(), is(query(where("username").not().regex(".*thew.*")).getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1139
|
||||
*/
|
||||
@Test
|
||||
public void createsNonShericalNearForDistanceWithDefaultMetric() {
|
||||
public void createsNonSphericalNearForDistanceWithDefaultMetric() {
|
||||
|
||||
Point point = new Point(1.0, 1.0);
|
||||
Distance distance = new Distance(1.0);
|
||||
@@ -668,6 +669,35 @@ public class MongoQueryCreatorUnitTests {
|
||||
assertThat(query, is(query(where("emailAddresses").in((Object) null))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1588
|
||||
*/
|
||||
@Test // DATAMONGO-1588
|
||||
public void queryShouldAcceptSubclassOfDeclaredArgument() {
|
||||
|
||||
PartTree tree = new PartTree("findByLocationNear", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonPoint(-74.044502D, 40.689247D));
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
assertThat(query.getQueryObject().containsField("location"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1588
|
||||
*/
|
||||
@Test
|
||||
public void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() {
|
||||
|
||||
expection.expect(IllegalArgumentException.class);
|
||||
expection.expectMessage("Expected parameter type of " + Point.class);
|
||||
|
||||
PartTree tree = new PartTree("findByLocationNear", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter,
|
||||
new GeoJsonLineString(new Point(-74.044502D, 40.689247D), new Point(-73.997330D, 40.730824D)));
|
||||
|
||||
new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
|
||||
@@ -686,16 +716,21 @@ public class MongoQueryCreatorUnitTests {
|
||||
Address address;
|
||||
|
||||
Address2dSphere address2dSphere;
|
||||
|
||||
Point location;
|
||||
}
|
||||
|
||||
static class Address {
|
||||
|
||||
String street;
|
||||
|
||||
Point geo;
|
||||
}
|
||||
|
||||
static class Address2dSphere {
|
||||
|
||||
String street;
|
||||
|
||||
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.query.IsTextQuery.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -40,6 +41,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.TextCriteria;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
@@ -192,6 +194,18 @@ public class PartTreeMongoQueryUnitTests {
|
||||
assertThat(fields.get("age"), is((Object) 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1500
|
||||
*/
|
||||
@Test
|
||||
public void shouldLeaveParameterConversionToQueryMapper() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findBySex", Sex.FEMALE);
|
||||
|
||||
assertThat(query.getQueryObject().get("sex"), is((Object) Sex.FEMALE));
|
||||
assertThat(query.getFieldsObject().get("firstname"), is((Object) 1));
|
||||
}
|
||||
|
||||
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) {
|
||||
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
@@ -249,6 +263,9 @@ public class PartTreeMongoQueryUnitTests {
|
||||
PersonDto findPersonDtoByAge(Integer age);
|
||||
|
||||
<T> T findDynamicallyProjectedBy(Class<T> type);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 1 }")
|
||||
List<Person> findBySex(Sex sex);
|
||||
}
|
||||
|
||||
interface PersonProjection {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -50,10 +50,12 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
|
||||
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StringBasedMongoQuery}.
|
||||
@@ -84,9 +86,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void bindsSimplePropertyCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -98,13 +100,13 @@ public class StringBasedMongoQueryUnitTests {
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class);
|
||||
|
||||
Address address = new Address("Foo", "0123", "Bar");
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
converter.write(address, dbObject);
|
||||
dbObject.removeField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
BasicDBObject queryObject = new BasicDBObject("address", dbObject);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
|
||||
|
||||
@@ -117,7 +119,7 @@ public class StringBasedMongoQueryUnitTests {
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class);
|
||||
|
||||
Address address = new Address("Foo", "0123", "Bar");
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
|
||||
|
||||
DBObject addressDbObject = new BasicDBObject();
|
||||
converter.write(address, addressDbObject);
|
||||
@@ -126,7 +128,7 @@ public class StringBasedMongoQueryUnitTests {
|
||||
DBObject reference = new BasicDBObject("address", addressDbObject);
|
||||
reference.put("lastname", "Matthews");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(), is(reference));
|
||||
}
|
||||
|
||||
@@ -178,8 +180,8 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
|
||||
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1) });
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1));
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", DBObject.class,
|
||||
Map.class);
|
||||
|
||||
@@ -227,10 +229,10 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -242,10 +244,10 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -258,9 +260,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -303,10 +305,10 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void shouldSupportExpressionsInCustomQueries() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -318,11 +320,11 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
|
||||
String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
@@ -334,11 +336,11 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Test
|
||||
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
|
||||
boolean.class, String.class, String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
|
||||
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
|
||||
|
||||
@@ -352,16 +354,126 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
|
||||
|
||||
byte[] binaryData = "Matthews".getBytes("UTF-8");
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
|
||||
+ DatatypeConverter.printBase64Binary(binaryData) + "', '$type' : " + BSON.B_GENERAL + "}}");
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
BasicDBList or = new BasicDBList();
|
||||
or.add(new BasicDBObject("age", 3));
|
||||
or.add(new BasicDBObject("displayAge", "3"));
|
||||
BasicDBObject queryObject = new BasicDBObject("$or", or);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
|
||||
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
|
||||
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
|
||||
"nothing-special");
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldQuoteStringReplacementCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews', password: 'foo")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews\", password: \"foo")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
||||
"\"Dave Matthews\", password: 'foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", "\"Dave Matthews\", password: 'foo")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldQuoteComplexQueryStringCorreclty() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", new BasicDBObject("$ne", "calamity"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1565
|
||||
*/
|
||||
@Test
|
||||
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
||||
"{ $ne : \"\\\"calamity\\\"\" }");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", new BasicDBObject("$ne", "\"calamity\""))));
|
||||
}
|
||||
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
@@ -420,5 +532,11 @@ public class StringBasedMongoQueryUnitTests {
|
||||
|
||||
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
|
||||
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
|
||||
|
||||
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
|
||||
boolean findByAgeQuotedAndUnquoted(int age);
|
||||
|
||||
@Query("{ 'arg0' : ?0, 'arg1' : ?1 }")
|
||||
List<Person> findByStringWithWildcardChar(String arg0, String arg1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
|
||||
/**
|
||||
* Tests for {@link PersistableMongoEntityInformation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PersistableMappingMongoEntityInformationUnitTests {
|
||||
|
||||
@Mock MongoPersistentEntity<TypeImplementingPersistable> persistableImplementingEntityTypeInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
when(persistableImplementingEntityTypeInfo.getType()).thenReturn(TypeImplementingPersistable.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1590
|
||||
public void considersPersistableIsNew() {
|
||||
|
||||
PersistableMongoEntityInformation<TypeImplementingPersistable, Long> information = new PersistableMongoEntityInformation<TypeImplementingPersistable, Long>(
|
||||
new MappingMongoEntityInformation<TypeImplementingPersistable, Long>(persistableImplementingEntityTypeInfo));
|
||||
|
||||
assertThat(information.isNew(new TypeImplementingPersistable(100L, false)), is(false));
|
||||
}
|
||||
|
||||
@Value
|
||||
static class TypeImplementingPersistable implements Persistable<Long> {
|
||||
|
||||
private static final long serialVersionUID = -1619090149320971099L;
|
||||
|
||||
Long id;
|
||||
boolean isNew;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2016 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.
|
||||
@@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
@@ -26,11 +28,15 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.mongodb.repository.QAddress;
|
||||
import org.springframework.data.mongodb.repository.QPerson;
|
||||
|
||||
@@ -172,10 +178,52 @@ public class SpringDataMongodbSerializerUnitTests {
|
||||
assertThat($in, Matchers.<Object> arrayContaining(firstId, secondId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1485
|
||||
*/
|
||||
@Test
|
||||
public void takesCustomConversionForEnumsIntoAccount() {
|
||||
|
||||
MongoMappingContext context = new MongoMappingContext();
|
||||
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbFactory, context);
|
||||
converter.setCustomConversions(new CustomConversions(Collections.singletonList(new SexTypeWriteConverter())));
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
this.converter = converter;
|
||||
this.serializer = new SpringDataMongodbSerializer(this.converter);
|
||||
|
||||
Object mappedPredicate = this.serializer.handle(QPerson.person.sex.eq(Sex.FEMALE));
|
||||
|
||||
assertThat(mappedPredicate, is(instanceOf(DBObject.class)));
|
||||
assertThat(((DBObject) mappedPredicate).get("sex"), is((Object) "f"));
|
||||
}
|
||||
|
||||
class Address {
|
||||
String id;
|
||||
String street;
|
||||
@Field("zip_code") String zipCode;
|
||||
@Field("bar") String[] foo;
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
public class SexTypeWriteConverter implements Converter<Sex, String> {
|
||||
|
||||
@Override
|
||||
public String convert(Sex source) {
|
||||
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (source) {
|
||||
case MALE:
|
||||
return "m";
|
||||
case FEMALE:
|
||||
return "f";
|
||||
default:
|
||||
throw new IllegalArgumentException("o_O");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Da
|
||||
:toc-placement!:
|
||||
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
|
||||
|
||||
(C) 2008-2015 The original authors.
|
||||
(C) 2008-2017 The original authors.
|
||||
|
||||
NOTE: _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._
|
||||
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
|
||||
The Spring Data MongoDB project applies core Spring concepts to the development of solutions using the MongoDB document style data store. We provide a "template" as a high-level abstraction for storing and querying documents. You will notice similarities to the JDBC support in the Spring Framework.
|
||||
|
||||
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various stores namespaces.
|
||||
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various store namespaces.
|
||||
|
||||
This section provides some basic introduction to Spring and Document database. The rest of the document refers only to Spring Data Document features and assumes the user is familiar with document databases such as MongoDB and CouchDB as well as Spring concepts.
|
||||
This section provides some basic introduction to Spring and Document databases. The rest of the document refers only to Spring Data MongoDB features and assumes the user is familiar with MongoDB and Spring concepts.
|
||||
|
||||
[[get-started:first-steps:spring]]
|
||||
== Knowing Spring
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
|
||||
The core functionality of the MongoDB and CouchDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data document, such as the repository support, you will need to configure some parts of the library using Spring.
|
||||
The core functionality of the MongoDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data MongoDB, such as the repository support, you will need to configure some parts of the library using Spring.
|
||||
|
||||
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page ] for more information.
|
||||
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page] for more information.
|
||||
|
||||
[[get-started:first-steps:nosql]]
|
||||
== Knowing NoSQL and Document databases
|
||||
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worth even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with the stores supported by DATADOC. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
|
||||
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worse even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with MongoDB. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
|
||||
|
||||
The jumping off ground for learning about MongoDB is http://www.mongodb.org/[www.mongodb.org]. Here is a list of other useful resources:
|
||||
|
||||
@@ -36,7 +36,7 @@ In terms of document stores, http://www.mongodb.org/[MongoDB] at least 2.6.
|
||||
|
||||
== Additional Help Resources
|
||||
|
||||
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data Document module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
|
||||
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data MongoDB module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
|
||||
|
||||
[[get-started:help]]
|
||||
=== Support
|
||||
@@ -56,4 +56,4 @@ Professional, from-the-source support, with guaranteed response time, is availab
|
||||
[[get-started:up-to-date]]
|
||||
=== Following Development
|
||||
|
||||
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the SpringSource Data http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).
|
||||
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the Spring http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).
|
||||
|
||||
@@ -157,7 +157,7 @@ Finally, you need to configure your project to use MongoDB and also configure th
|
||||
[[mongodb_cross-store-application]]
|
||||
== Writing the Cross Store Application
|
||||
|
||||
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 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 `@RelatedDocument` 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 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`.
|
||||
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 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 `@RelatedDocument` 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 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 `@RelatedDocument`.
|
||||
|
||||
.Example of Entity with @RelatedDocument
|
||||
====
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
== Document Structure
|
||||
|
||||
This part of the reference documentation explains the core functionality offered by Spring Data Document.
|
||||
This part of the reference documentation explains the core functionality offered by Spring Data MongoDB.
|
||||
|
||||
<<mongo.core>> introduces the MongoDB module feature set.
|
||||
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
[[mapping-chapter]]
|
||||
= Mapping
|
||||
|
||||
Rich mapping support is provided by the `MongoMappingConverter`. `MongoMappingConverter` has a rich metadata model that provides a full feature set of functionality to map domain objects to MongoDB documents.The mapping metadata model is populated using annotations on your domain objects. However, the infrastructure is not limited to using annotations as the only source of metadata information. The `MongoMappingConverter` also allows you to map objects to documents without providing any additional metadata, by following a set of conventions.
|
||||
Rich mapping support is provided by the `MappingMongoConverter`. `MappingMongoConverter` has a rich metadata model that provides a full feature set of functionality to map domain objects to MongoDB documents.The mapping metadata model is populated using annotations on your domain objects. However, the infrastructure is not limited to using annotations as the only source of metadata information. The `MappingMongoConverter` also allows you to map objects to documents without providing any additional metadata, by following a set of conventions.
|
||||
|
||||
In this section we will describe the features of the `MongoMappingConverter`. How to use conventions for mapping objects to documents and how to override those conventions with annotation based mapping metadata.
|
||||
In this section we will describe the features of the `MappingMongoConverter`. How to use conventions for mapping objects to documents and how to override those conventions with annotation based mapping metadata.
|
||||
|
||||
NOTE: `SimpleMongoConverter` has been deprecated in Spring Data MongoDB M3 as all of its functionality has been subsumed into `MappingMongoConverter`.
|
||||
|
||||
[[mapping-conventions]]
|
||||
== Convention based Mapping
|
||||
|
||||
`MongoMappingConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided. The conventions are:
|
||||
`MappingMongoConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided. The conventions are:
|
||||
|
||||
* The short Java class name is mapped to the collection name in the following manner. The class '`com.bigbank.SavingsAccount`' maps to '`savingsAccount`' collection name.
|
||||
* The short Java class name is mapped to the collection name in the following manner. The class `com.bigbank.SavingsAccount` maps to `savingsAccount` collection name.
|
||||
* All nested objects are stored as nested objects in the document and *not* as DBRefs
|
||||
* The converter will use any Spring Converters registered with it to override the default mapping of object properties to document field/values.
|
||||
* The fields of an object are used to convert to and from fields in the document. Public JavaBean properties are not used.
|
||||
* 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. Otherwise the zero arg constructor will be used. if there is more than one non-zero argument constructor an exception will be thrown.
|
||||
|
||||
[[mapping.conventions.id-field]]
|
||||
=== How the '_id' field is handled in the mapping layer
|
||||
=== How the `_id` field is handled in the mapping layer
|
||||
|
||||
MongoDB requires that you have an '_id' field for all documents. If you don't provide one the driver will assign a ObjectId with a generated value. The "_id" field can be of any type the, other than arrays, so long as it is unique. The driver naturally supports all primitive types and Dates. When using the `MongoMappingConverter` there are certain rules that govern how properties from the Java class is mapped to this '_id' field.
|
||||
MongoDB requires that you have an `_id` field for all documents. If you don't provide one the driver will assign a ObjectId with a generated value. The "_id" field can be of any type the, other than arrays, so long as it is unique. The driver naturally supports all primitive types and Dates. When using the `MappingMongoConverter` there are certain rules that govern how properties from the Java class is mapped to this `_id` field.
|
||||
|
||||
The following outlines what field will be mapped to the '_id' document field:
|
||||
The following outlines what field will be mapped to the `_id` document field:
|
||||
|
||||
* A field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the '_id' field.
|
||||
* A field without an annotation but named 'id' will be mapped to the '_id' field.
|
||||
* The default field name for identifiers is '_id' and can be customized via the `@Field` annotation.
|
||||
* A field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the `_id` field.
|
||||
* A field without an annotation but named `id` will be mapped to the `_id` field.
|
||||
* The default field name for identifiers is `_id` and can be customized via the `@Field` annotation.
|
||||
|
||||
[cols="1,2", options="header"]
|
||||
.Examples for the translation of '_id'-field definitions
|
||||
.Examples for the translation of `_id` field definitions
|
||||
|===
|
||||
| Field definition
|
||||
| Resulting Id-Fieldname in MongoDB
|
||||
@@ -41,30 +41,218 @@ The following outlines what field will be mapped to the '_id' document field:
|
||||
| `@Field` `String` id
|
||||
| `_id`
|
||||
|
||||
| `@Field('x')` `String` id
|
||||
| `@Field("x")` `String` id
|
||||
| `x`
|
||||
|
||||
| `@Id` `String` x
|
||||
| `_id`
|
||||
|
||||
| `@Field('x')` `@Id` `String` x
|
||||
| `@Field("x")` `@Id` `String` x
|
||||
| `_id`
|
||||
|===
|
||||
|
||||
The following outlines what type conversion, if any, will be done on the property mapped to the _id document field.
|
||||
|
||||
* If a field named 'id' is declared as a String or BigInteger in 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 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.
|
||||
* If a field named ' id' id field is not declared as a String, BigInteger, or ObjectID in the Java class then you should assign it a value in your application so it can be stored 'as-is' in the document's _id field.
|
||||
* If no field named 'id' is present in the Java class then an implicit '_id' file will be generated by the driver but not mapped to a property or field of the Java class.
|
||||
* If a field named `id` is declared as a String or BigInteger in 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 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.
|
||||
* If a field named `id` id field is not declared as a String, BigInteger, or ObjectID in the Java class then you should assign it a value in your application so it can be stored 'as-is' in the document's _id field.
|
||||
* If no field named `id` is present in the Java class then an implicit `_id` file will be generated by the driver but not mapped to a property or field of the Java class.
|
||||
|
||||
When querying and updating `MongoTemplate` will use the converter to handle conversions of the `Query` and `Update` objects that correspond to the above rules for saving documents so field names and types used in your queries will be able to match what is in your domain classes.
|
||||
|
||||
[[mapping-conversion]]
|
||||
== Data mapping and type conversion
|
||||
|
||||
This section explain how types are mapped to a MongoDB representation and vice versa. Spring Data MongoDB supports all types that can be represented as BSON, MongoDB's internal document format.
|
||||
In addition to these types, Spring Data MongoDB provides a set of built-in converters to map additional types. You can provide your own converters to adjust type conversion, see <<mapping-explicit-converters>> for further details.
|
||||
|
||||
[cols="3,1,6", options="header"]
|
||||
.Type
|
||||
|===
|
||||
| Type
|
||||
| Type conversion
|
||||
| Sample
|
||||
|
||||
| `String`
|
||||
| native
|
||||
| `{"firstname" : "Dave"}`
|
||||
|
||||
| `double`, `Double`, `float`, `Float`
|
||||
| native
|
||||
| `{"weight" : 42.5}`
|
||||
|
||||
| `int`, `Integer`, `short`, `Short`
|
||||
| native +
|
||||
32-bit integer
|
||||
| `{"height" : 42}`
|
||||
|
||||
| `long`, `Long`
|
||||
| native +
|
||||
64-bit integer
|
||||
| `{"height" : 42}`
|
||||
|
||||
| `Date`, `Timestamp`
|
||||
| native
|
||||
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
|
||||
|
||||
| `byte[]`
|
||||
| native
|
||||
| `{"bin" : { "$binary" : "AQIDBA==", "$type" : "00" }}`
|
||||
|
||||
| `java.util.UUID` (Legacy UUID)
|
||||
| native
|
||||
| `{"uuid" : { "$binary" : "MEaf1CFQ6lSphaa3b9AtlA==", "$type" : "03" }}`
|
||||
|
||||
| `Date`
|
||||
| native
|
||||
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
|
||||
|
||||
| `ObjectId`
|
||||
| native
|
||||
| `{"_id" : ObjectId("5707a2690364aba3136ab870")}`
|
||||
|
||||
| Array, `List`, `BasicDBList`
|
||||
| native
|
||||
| `{"cookies" : [ … ]}`
|
||||
|
||||
| `boolean`, `Boolean`
|
||||
| native
|
||||
| `{"active" : true}`
|
||||
|
||||
| `null`
|
||||
| native
|
||||
| `{"value" : null}`
|
||||
|
||||
| `DBObject`
|
||||
| native
|
||||
| `{"value" : { … }}`
|
||||
|
||||
| `Decimal128`
|
||||
| native
|
||||
| `{"value" : NumberDecimal(…)}`
|
||||
|
||||
| `AtomicInteger` +
|
||||
calling `get()` before the actual conversion
|
||||
| converter +
|
||||
32-bit integer
|
||||
| `{"value" : "741" }`
|
||||
|
||||
| `AtomicLong` +
|
||||
calling `get()` before the actual conversion
|
||||
| converter +
|
||||
64-bit integer
|
||||
| `{"value" : "741" }`
|
||||
|
||||
| `BigInteger`
|
||||
| converter +
|
||||
`String`
|
||||
| `{"value" : "741" }`
|
||||
|
||||
| `BigDecimal`
|
||||
| converter +
|
||||
`String`
|
||||
| `{"value" : "741.99" }`
|
||||
|
||||
| `URL`
|
||||
| converter
|
||||
| `{"website" : "http://projects.spring.io/spring-data-mongodb/" }`
|
||||
|
||||
| `Locale`
|
||||
| converter
|
||||
| `{"locale : "en_US" }`
|
||||
|
||||
| `char`, `Character`
|
||||
| converter
|
||||
| `{"char" : "a" }`
|
||||
|
||||
| `NamedMongoScript`
|
||||
| converter +
|
||||
`Code`
|
||||
| `{"_id" : "script name", value: (some javascript code)`}
|
||||
|
||||
| `java.util.Currency`
|
||||
| converter
|
||||
| `{"currencyCode" : "EUR"}`
|
||||
|
||||
| `LocalDate` +
|
||||
(Joda, Java 8, JSR310-BackPort)
|
||||
| converter
|
||||
| `{"date" : ISODate("2019-11-12T00:00:00.000Z")}`
|
||||
|
||||
| `LocalDateTime`, `LocalTime`, `Instant` +
|
||||
(Joda, Java 8, JSR310-BackPort)
|
||||
| converter
|
||||
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
|
||||
|
||||
| `DateTime` (Joda)
|
||||
| converter
|
||||
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
|
||||
|
||||
| `DateMidnight` (Joda)
|
||||
| converter
|
||||
| `{"date" : ISODate("2019-11-12T00:00:00.000Z")}`
|
||||
|
||||
| `ZoneId` (Java 8, JSR310-BackPort)
|
||||
| converter
|
||||
| `{"zoneId" : "ECT - Europe/Paris"}`
|
||||
|
||||
| `Box`
|
||||
| converter
|
||||
| `{"box" : { "first" : { "x" : 1.0 , "y" : 2.0} , "second" : { "x" : 3.0 , "y" : 4.0}}`
|
||||
|
||||
| `Polygon`
|
||||
| converter
|
||||
| `{"polygon" : { "points" : [ { "x" : 1.0 , "y" : 2.0} , { "x" : 3.0 , "y" : 4.0} , { "x" : 4.0 , "y" : 5.0}]}}`
|
||||
|
||||
| `Circle`
|
||||
| converter
|
||||
| `{"circle" : { "center" : { "x" : 1.0 , "y" : 2.0} , "radius" : 3.0 , "metric" : "NEUTRAL"}}`
|
||||
|
||||
| `Point`
|
||||
| converter
|
||||
| `{"point" : { "x" : 1.0 , "y" : 2.0}}`
|
||||
|
||||
| `GeoJsonPoint`
|
||||
| converter
|
||||
| `{"point" : { "type" : "Point" , "coordinates" : [3.0 , 4.0] }}`
|
||||
|
||||
| `GeoJsonMultiPoint`
|
||||
| converter
|
||||
| `{"geoJsonLineString" : {"type":"MultiPoint", "coordinates": [ [ 0 , 0 ], [ 0 , 1 ], [ 1 , 1 ] ] }}`
|
||||
|
||||
| `Sphere`
|
||||
| converter
|
||||
| `{"sphere" : { "center" : { "x" : 1.0 , "y" : 2.0} , "radius" : 3.0 , "metric" : "NEUTRAL"}}`
|
||||
|
||||
| `GeoJsonPolygon`
|
||||
| converter
|
||||
| `{"polygon" : { "type" : "Polygon", "coordinates" : [[ [ 0 , 0 ], [ 3 , 6 ], [ 6 , 1 ], [ 0 , 0 ] ]] }}`
|
||||
|
||||
| `GeoJsonMultiPolygon`
|
||||
| converter
|
||||
| `{"geoJsonMultiPolygon" : { "type" : "MultiPolygon", "coordinates" : [
|
||||
[ [ [ -73.958 , 40.8003 ] , [ -73.9498 , 40.7968 ] ] ],
|
||||
[ [ [ -73.973 , 40.7648 ] , [ -73.9588 , 40.8003 ] ] ]
|
||||
] }}`
|
||||
|
||||
| `GeoJsonLineString`
|
||||
| converter
|
||||
| `{ "geoJsonLineString" : { "type" : "LineString", "coordinates" : [ [ 40 , 5 ], [ 41 , 6 ] ] }}`
|
||||
|
||||
| `GeoJsonMultiLineString`
|
||||
| converter
|
||||
| `{"geoJsonLineString" : { "type" : "MultiLineString", coordinates: [
|
||||
[ [ -73.97162 , 40.78205 ], [ -73.96374 , 40.77715 ] ],
|
||||
[ [ -73.97880 , 40.77247 ], [ -73.97036 , 40.76811 ] ]
|
||||
] }}`
|
||||
|===
|
||||
|
||||
|
||||
[[mapping-configuration]]
|
||||
== Mapping Configuration
|
||||
|
||||
Unless explicitly configured, an instance of `MongoMappingConverter` is created by default when creating a `MongoTemplate`. You can create your own instance of the `MappingMongoConverter` so as to tell it where to scan the classpath at startup your domain classes in order to extract metadata and construct indexes. Also, by creating your own instance you can register Spring converters to use for mapping specific classes to and from the database.
|
||||
Unless explicitly configured, an instance of `MappingMongoConverter` is created by default when creating a `MongoTemplate`. You can create your own instance of the `MappingMongoConverter` so as to tell it where to scan the classpath at startup your domain classes in order to extract metadata and construct indexes. Also, by creating your own instance you can register Spring converters to use for mapping specific classes to and from the database.
|
||||
|
||||
You can configure the `MongoMappingConverter` as well as `com.mongodb.Mongo` and MongoTemplate either using Java or XML based metadata. Here is an example using Spring's Java based configuration
|
||||
You can configure the `MappingMongoConverter` as well as `com.mongodb.Mongo` and MongoTemplate either using Java or XML based metadata. Here is an example using Spring's Java based configuration
|
||||
|
||||
.@Configuration class to configure MongoDB mapping support
|
||||
====
|
||||
@@ -108,11 +296,11 @@ public class GeoSpatialAppConfig extends AbstractMongoConfiguration {
|
||||
----
|
||||
====
|
||||
|
||||
`AbstractMongoConfiguration` requires you to implement methods that define a `com.mongodb.Mongo` as well as provide a database name. `AbstractMongoConfiguration` also has a method you can override named '`getMappingBasePackage`' which tells the converter where to scan for classes annotated with the `@org.springframework.data.mongodb.core.mapping.Document` annotation.
|
||||
`AbstractMongoConfiguration` requires you to implement methods that define a `com.mongodb.Mongo` as well as provide a database name. `AbstractMongoConfiguration` also has a method you can override named `getMappingBasePackage(…)` which tells the converter where to scan for classes annotated with the `@Document` annotation.
|
||||
|
||||
You can add additional converters to the converter by overriding the method afterMappingMongoConverterCreation. Also shown in the above example is a `LoggingEventListener` which logs `MongoMappingEvent`s that are posted onto Spring's `ApplicationContextEvent` infrastructure.
|
||||
You can add additional converters to the converter by overriding the method afterMappingMongoConverterCreation. Also shown in the above example is a `LoggingEventListener` which logs `MongoMappingEvent` s that are posted onto Spring's `ApplicationContextEvent` infrastructure.
|
||||
|
||||
NOTE: AbstractMongoConfiguration will create a MongoTemplate instance and registered with the container under the name 'mongoTemplate'.
|
||||
NOTE: AbstractMongoConfiguration will create a MongoTemplate instance and registered with the container under the name `mongoTemplate`.
|
||||
|
||||
You can also override the method `UserCredentials getUserCredentials()` to provide the username and password information to connect to the database.
|
||||
|
||||
@@ -165,7 +353,7 @@ The `base-package` property tells it where to scan for classes annotated with th
|
||||
[[mapping-usage]]
|
||||
== Metadata based Mapping
|
||||
|
||||
To take full advantage of the object mapping functionality inside the Spring Data/MongoDB support, you should annotate your mapped objects with the `@org.springframework.data.mongodb.core.mapping.Document` annotation. Although it is not necessary for the mapping framework to have this annotation (your POJOs will be mapped correctly, even without any annotations), it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. If you don't use this annotation, your application will take a slight performance hit the first time you store a domain object because the mapping framework needs to build up its internal metadata model so it knows about the properties of your domain object and how to persist them.
|
||||
To take full advantage of the object mapping functionality inside the Spring Data/MongoDB support, you should annotate your mapped objects with the `@Document` annotation. Although it is not necessary for the mapping framework to have this annotation (your POJOs will be mapped correctly, even without any annotations), it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. If you don't use this annotation, your application will take a slight performance hit the first time you store a domain object because the mapping framework needs to build up its internal metadata model so it knows about the properties of your domain object and how to persist them.
|
||||
|
||||
.Example domain object
|
||||
====
|
||||
@@ -271,7 +459,7 @@ public class Person<T extends Address> {
|
||||
return ssn;
|
||||
}
|
||||
|
||||
// other getters/setters ommitted
|
||||
// other getters/setters omitted
|
||||
----
|
||||
|
||||
[[mapping-custom-object-construction]]
|
||||
@@ -281,7 +469,7 @@ The mapping subsystem allows the customization of the object construction by ann
|
||||
|
||||
* If a parameter is annotated with the `@Value` annotation, the given expression is evaluated and the result is used as the parameter value.
|
||||
* If the Java type has a property whose name matches the given field of the input document, then it's property information is used to select the appropriate constructor parameter to pass the input field value to. This works only if the parameter name information is present in the java `.class` files which can be achieved by compiling the source with debug information or using the new `-parameters` command-line switch for javac in Java 8.
|
||||
* Otherwise an `MappingException` will be thrown indicating that the given constructor parameter could not be bound.
|
||||
* Otherwise a `MappingException` will be thrown indicating that the given constructor parameter could not be bound.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -313,7 +501,7 @@ Additional examples for using the `@PersistenceConstructor` annotation can be fo
|
||||
[[mapping-usage-indexes.compound-index]]
|
||||
=== Compound Indexes
|
||||
|
||||
Compound indexes are also supported. They are defined at the class level, rather than on indidividual properties.
|
||||
Compound indexes are also supported. They are defined at the class level, rather than on individual properties.
|
||||
|
||||
NOTE: Compound indexes are very important to improve the performance of queries that involve criteria on multiple fields
|
||||
|
||||
@@ -346,7 +534,7 @@ public class Person {
|
||||
|
||||
NOTE: The text index feature is disabled by default for mongodb v.2.4.
|
||||
|
||||
Creating a text index allows to accumulate several fields into a searchable full text index. It is only possible to have one text index per collection so all fields marked with `@TextIndexed` are combined into this index. Properties can be weighted to influence document score for ranking results. The default language for the text index is english, to change the default language set `@Document(language="spanish")` to any language you want. Using a property called `language` or `@Language` allows to define a language override on a per document base.
|
||||
Creating a text index allows accumulating several fields into a searchable full text index. It is only possible to have one text index per collection so all fields marked with `@TextIndexed` are combined into this index. Properties can be weighted to influence document score for ranking results. The default language for the text index is english, to change the default language set `@Document(language="spanish")` to any language you want. Using a property called `language` or `@Language` allows to define a language override on a per document base.
|
||||
|
||||
.Example Text Index Usage
|
||||
====
|
||||
@@ -401,7 +589,7 @@ public class Person {
|
||||
----
|
||||
====
|
||||
|
||||
There's no need to use something like `@OneToMany` because the mapping framework sees that you're wanting a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the `Account` objects themselves.
|
||||
There's no need to use something like `@OneToMany` because the mapping framework sees that you want a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the `Account` objects themselves.
|
||||
|
||||
IMPORTANT: The mapping framework does not handle cascading saves. If you change an `Account` object that is referenced by a `Person` object, you must save the Account object separately. Calling `save` on the `Person` object will not automatically save the `Account` objects in the property `accounts`.
|
||||
|
||||
@@ -415,7 +603,7 @@ Simply declaring these beans in your Spring ApplicationContext will cause them t
|
||||
[[mapping-explicit-converters]]
|
||||
=== Overriding Mapping with explicit Converters
|
||||
|
||||
When storing and querying your objects it is convenient to have a `MongoConverter` instance handle the mapping of all Java types to DBObjects. However, sometimes you may want the `MongoConverter`'s do most of the work but allow you to selectively handle the conversion for a particular type or to optimize performance.
|
||||
When storing and querying your objects it is convenient to have a `MongoConverter` instance handle the mapping of all Java types to DBObjects. However, sometimes you may want the `MongoConverter` s do most of the work but allow you to selectively handle the conversion for a particular type or to optimize performance.
|
||||
|
||||
To selectively handle the conversion yourself, register one or more one or more `org.springframework.core.convert.converter.Converter` instances with the MongoConverter.
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ This section covers additional things to keep in mind when using the 3.0 driver.
|
||||
|
||||
* `IndexOperations.resetIndexCache()` is no longer supported.
|
||||
* Any `MapReduceOptions.extraOption` is silently ignored.
|
||||
* `WriteResult` does not longer hold error informations but throws an Exception.
|
||||
* `WriteResult` does not longer hold error information but throws an Exception.
|
||||
* `MongoOperations.executeInSession(…)` no longer calls `requestStart` / `requestDone`.
|
||||
* Index name generation has become a driver internal operations, still we use the 2.x schema to generate names.
|
||||
* Some Exception messages differ between the generation 2 and 3 servers as well as between _MMap.v1_ and _WiredTiger_ storage engine.
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Person {
|
||||
----
|
||||
====
|
||||
|
||||
We have a quite simple domain object here. Note that it has a property named `id` of type`ObjectId`. The default serialization mechanism used in `MongoTemplate` (which is backing the repository support) regards properties named id as document id. Currently we support`String`, `ObjectId` and `BigInteger` as id-types.
|
||||
We have a quite simple domain object here. Note that it has a property named `id` of type `ObjectId`. The default serialization mechanism used in `MongoTemplate` (which is backing the repository support) regards properties named id as document id. Currently we support `String`, `ObjectId` and `BigInteger` as id-types.
|
||||
|
||||
.Basic repository interface to persist Person entities
|
||||
====
|
||||
@@ -99,7 +99,7 @@ class ApplicationConfig extends AbstractMongoConfiguration {
|
||||
----
|
||||
====
|
||||
|
||||
As our domain repository extends `PagingAndSortingRepository` it provides you with CRUD operations as well as methods for paginated and sorted access to the entities. Working with the repository instance is just a matter of dependency injecting it into a client. So accessing the second page of `Person`s at a page size of 10 would simply look something like this:
|
||||
As our domain repository extends `PagingAndSortingRepository` it provides you with CRUD operations as well as methods for paginated and sorted access to the entities. Working with the repository instance is just a matter of dependency injecting it into a client. So accessing the second page of `Person` s at a page size of 10 would simply look something like this:
|
||||
|
||||
.Paging access to Person entities
|
||||
====
|
||||
@@ -139,17 +139,17 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str
|
||||
Page<Person> findByFirstname(String firstname, Pageable pageable); <2>
|
||||
|
||||
Person findByShippingAddresses(Address address); <3>
|
||||
|
||||
|
||||
Stream<Person> findAllBy(); <4>
|
||||
}
|
||||
----
|
||||
<1> The method shows a query for all people with the given lastname. The query will be derived parsing the method name for constraints which can be concatenated with `And` and `Or`. Thus the method name will result in a query expression of `{"lastname" : lastname}`.
|
||||
<2> Applies pagination to a query. Just equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and we will automatically page the query accordingly.
|
||||
<3> Shows that you can query based on properties which are not a primitive type.
|
||||
<3> Shows that you can query based on properties which are not a primitive type.
|
||||
<4> Uses a Java 8 `Stream` which reads and converts individual elements while iterating the stream.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
||||
NOTE: Note that for version 1.0 we currently don't support referring to parameters that are mapped as `DBRef` in the domain class.
|
||||
|
||||
@@ -212,10 +212,18 @@ NOTE: Note that for version 1.0 we currently don't support referring to paramete
|
||||
| `findByFirstnameContaining(String name)`
|
||||
| `{"firstname" : name} (name as regex)`
|
||||
|
||||
| `NotContaining` on String
|
||||
| `findByFirstnameNotContaining(String name)`
|
||||
| `{"firstname" : { "$not" : name}} (name as regex)`
|
||||
|
||||
| `Containing` on Collection
|
||||
| `findByAddressesContaining(Address address)`
|
||||
| `{"addresses" : { "$in" : address}}`
|
||||
|
||||
| `NotContaining` on Collection
|
||||
| `findByAddressesNotContaining(Address address)`
|
||||
| `{"addresses" : { "$not" : { "$in" : address}}}`
|
||||
|
||||
| `Regex`
|
||||
| `findByFirstnameRegex(String firstname)`
|
||||
| `{"firstname" : {"$regex" : firstname }}`
|
||||
@@ -264,7 +272,7 @@ NOTE: Note that for version 1.0 we currently don't support referring to paramete
|
||||
[[mongodb.repositories.queries.delete]]
|
||||
=== Repository delete queries
|
||||
|
||||
The above keywords can be used in conjunciton with `delete…By` or `remove…By` to create queries deleting matching documents.
|
||||
The above keywords can be used in conjunction with `delete…By` or `remove…By` to create queries deleting matching documents.
|
||||
|
||||
.`Delete…By` Query
|
||||
====
|
||||
@@ -284,7 +292,7 @@ Using return type `List` will retrieve and return all matching documents before
|
||||
[[mongodb.repositories.queries.geo-spatial]]
|
||||
=== Geo-spatial repository queries
|
||||
|
||||
As you've just seen there are a few keywords triggering geo-spatial operations within a MongoDB query. The `Near` keyword allows some further modification. Let's have look at some examples:
|
||||
As you've just seen there are a few keywords triggering geo-spatial operations within a MongoDB query. The `Near` keyword allows some further modification. Let's have a look at some examples:
|
||||
|
||||
.Advanced `Near` queries
|
||||
====
|
||||
@@ -328,7 +336,7 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
@@ -428,9 +436,9 @@ We think you will find this an extremely powerful tool for writing MongoDB queri
|
||||
|
||||
[[mongodb.repositories.queries.full-text]]
|
||||
=== Full-text search queries
|
||||
MongoDBs full text search feature is very store specic and therefore can rather be found on `MongoRepository` than on the more general `CrudRepository`. What we need is a document with a full-text index defined for (Please see section <<mapping-usage-indexes.text-index>> for creating).
|
||||
MongoDBs full text search feature is very store specific and therefore can rather be found on `MongoRepository` than on the more general `CrudRepository`. What we need is a document with a full-text index defined for (Please see section <<mapping-usage-indexes.text-index>> for creating).
|
||||
|
||||
Additional methods on `MongoRepository` take `TextCriteria` as input parameter. In addition to those explicit methods, it is also possible to add a `TextCriteria` derived repository method. The criteria will added as an additional `AND` criteria. Once the entity contains a `@TextScore` annotated property the documents full-text score will be retrieved. Furthermore the `@TextScore` annotated property will also make it possible to sort by the documents score.
|
||||
Additional methods on `MongoRepository` take `TextCriteria` as input parameter. In addition to those explicit methods, it is also possible to add a `TextCriteria` derived repository method. The criteria will be added as an additional `AND` criteria. Once the entity contains a `@TextScore` annotated property the documents full-text score will be retrieved. Furthermore the `@TextScore` annotated property will also make it possible to sort by the documents score.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -489,7 +497,7 @@ class MongoTemplateProducer {
|
||||
}
|
||||
----
|
||||
|
||||
The Spring Data MongoDB CDI extension will pick up the `MongoTemplate` available as CDI bean and create a proxy for a Spring Data repository whenever an bean of a repository type is requested by the container. Thus obtaining an instance of a Spring Data repository is a matter of declaring an `@Inject`-ed property:
|
||||
The Spring Data MongoDB CDI extension will pick up the `MongoTemplate` available as CDI bean and create a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus obtaining an instance of a Spring Data repository is a matter of declaring an `@Inject`-ed property:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -12,11 +12,11 @@ The MongoDB support contains a wide range of features which are summarized below
|
||||
* Java based Query, Criteria, and Update DSLs
|
||||
* Automatic implementation of Repository interfaces including support for custom finder methods.
|
||||
* QueryDSL integration to support type-safe queries.
|
||||
* Cross-store persistance - support for JPA Entities with fields transparently persisted/retrieved using MongoDB
|
||||
* Cross-store persistence - support for JPA Entities with fields transparently persisted/retrieved using MongoDB
|
||||
* Log4j log appender
|
||||
* GeoSpatial integration
|
||||
|
||||
For most tasks you will find yourself using `MongoTemplate` or the Repository support that both leverage the rich mapping functionality. MongoTemplate is the place to look for accessing functionality such as incrementing counters or ad-hoc CRUD operations. MongoTemplate also provides callback methods so that it is easy for you to get a hold of the low level API artifacts such as `org.mongo.DB` to communicate directly with MongoDB. The goal with naming conventions on various API artifacts is to copy those in the base MongoDB Java driver so you can easily map your existing knowledge onto the Spring APIs.
|
||||
For most tasks you will find yourself using `MongoTemplate` or the Repository support that both leverage the rich mapping functionality. `MongoTemplate` is the place to look for accessing functionality such as incrementing counters or ad-hoc CRUD operations. `MongoTemplate` also provides callback methods so that it is easy for you to get a hold of the low level API artifacts such as `com.mongo.DB` to communicate directly with MongoDB. The goal with naming conventions on various API artifacts is to copy those in the base MongoDB Java driver so you can easily map your existing knowledge onto the Spring APIs.
|
||||
|
||||
[[mongodb-getting-started]]
|
||||
== Getting Started
|
||||
@@ -51,7 +51,7 @@ Also change the version of Spring in the pom.xml to be
|
||||
<spring.framework.version>{springVersion}</spring.framework.version>
|
||||
----
|
||||
|
||||
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 <dependencies/> element
|
||||
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 `<dependencies/>` element
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -66,11 +66,11 @@ You will also need to add the location of the Spring Milestone repository for ma
|
||||
|
||||
The repository is also http://repo.spring.io/milestone/org/springframework/data/[browseable here].
|
||||
|
||||
You may also want to set the logging level to `DEBUG` to see some additional information, edit the log4j.properties file to have
|
||||
You may also want to set the logging level to `DEBUG` to see some additional information, edit the `log4j.properties` file to have
|
||||
|
||||
[source]
|
||||
----
|
||||
log4j.category.org.springframework.data.document.mongodb=DEBUG
|
||||
log4j.category.org.springframework.data.mongodb=DEBUG
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n
|
||||
----
|
||||
|
||||
@@ -155,7 +155,7 @@ Even in this simple example, there are few things to take notice of
|
||||
|
||||
* You can instantiate the central helper class of Spring Mongo, <<mongo-template,`MongoTemplate`>>, using the standard `com.mongodb.Mongo` object and the name of the database to use.
|
||||
* The mapper works against standard POJO objects without the need for any additional metadata (though you can optionally provide that information. See <<mongo.mapping,here>>.).
|
||||
* Conventions are used for handling the id field, converting it to be a ObjectId when stored in the database.
|
||||
* Conventions are used for handling the id field, converting it to be a `ObjectId` when stored in the database.
|
||||
* Mapping conventions can use field access. Notice the Person class has only getters.
|
||||
* If the constructor argument names match the field names of the stored document, they will be used to instantiate the object
|
||||
|
||||
@@ -195,7 +195,7 @@ public class AppConfig {
|
||||
|
||||
This approach allows you to use the standard `com.mongodb.Mongo` API that you may already be used to using but also pollutes the code with the UnknownHostException checked exception. The use of the checked exception is not desirable as Java based bean metadata uses methods as a means to set object dependencies, making the calling code cluttered.
|
||||
|
||||
An alternative is to register an instance of `com.mongodb.Mongo` instance with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.Mongo` instance directly, the FactoryBean approach does not throw a checked exception and has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annoated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features].
|
||||
An alternative is to register an instance of `com.mongodb.Mongo` instance with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.Mongo` instance directly, the FactoryBean approach does not throw a checked exception and has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features].
|
||||
|
||||
An example of a Java based bean metadata that supports exception translation on `@Repository` annotated classes is shown below:
|
||||
|
||||
@@ -223,7 +223,7 @@ To access the `com.mongodb.Mongo` object created by the `MongoClientFactoryBean`
|
||||
[[mongo.mongo-xml-config]]
|
||||
=== Registering a Mongo instance using XML based metadata
|
||||
|
||||
While you can use Spring's traditional `<beans/>` XML namespace to register an instance of `com.mongodb.Mongo` with the container, the XML can be quite verbose as it is general purpose. XML namespaces are a better alternative to configuring commonly used objects such as the Mongo instance. The mongo namespace alows you to create a Mongo instance server location, replica-sets, and options.
|
||||
While you can use Spring's traditional `<beans/>` XML namespace to register an instance of `com.mongodb.Mongo` with the container, the XML can be quite verbose as it is general purpose. XML namespaces are a better alternative to configuring commonly used objects such as the Mongo instance. The mongo namespace allows you to create a Mongo instance server location, replica-sets, and options.
|
||||
|
||||
To use the Mongo namespace elements you will need to reference the Mongo schema:
|
||||
|
||||
@@ -250,7 +250,7 @@ To use the Mongo namespace elements you will need to reference the Mongo schema:
|
||||
----
|
||||
====
|
||||
|
||||
A more advanced configuration with MongoOptions is shown below (note these are not recommended values)
|
||||
A more advanced configuration with `MongoOptions` is shown below (note these are not recommended values)
|
||||
|
||||
.XML schema to configure a com.mongodb.Mongo object with MongoOptions
|
||||
====
|
||||
@@ -301,9 +301,9 @@ public interface MongoDbFactory {
|
||||
}
|
||||
----
|
||||
|
||||
The following sections show how you can use the container with either Java or the XML based metadata to configure an instance of the `MongoDbFactory` interface. In turn, you can use the `MongoDbFactory` instance to configure MongoTemplate.
|
||||
The following sections show how you can use the container with either Java or the XML based metadata to configure an instance of the `MongoDbFactory` interface. In turn, you can use the `MongoDbFactory` instance to configure `MongoTemplate`.
|
||||
|
||||
The class `org.springframework.data.mongodb.core.SimpleMongoDbFactory` provides implements the MongoDbFactory interface and is created with a standard `com.mongodb.Mongo` instance, the database name and an optional `org.springframework.data.authentication.UserCredentials` constructor argument.
|
||||
The class `org.springframework.data.mongodb.core.SimpleMongoDbFactory` provides implements the `MongoDbFactory` interface and is created with a standard `com.mongodb.Mongo` instance, the database name and an optional `org.springframework.data.authentication.UserCredentials` constructor argument.
|
||||
|
||||
Instead of using the IoC container to create an instance of MongoTemplate, you can just use them in standard Java code as shown below.
|
||||
|
||||
@@ -366,7 +366,7 @@ public class MongoConfiguration {
|
||||
[[mongo.mongo-db-factory-xml]]
|
||||
=== Registering a MongoDbFactory instance using XML based metadata
|
||||
|
||||
The mongo namespace provides a convient way to create a `SimpleMongoDbFactory` as compared to using the`<beans/>` namespace. Simple usage is shown below
|
||||
The mongo namespace provides a convenient way to create a `SimpleMongoDbFactory` as compared to using the `<beans/>` namespace. Simple usage is shown below
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -387,7 +387,21 @@ You can also provide the host and port for the underlying `com.mongodb.Mongo` in
|
||||
password="secret"/>
|
||||
----
|
||||
|
||||
If you need to configure additional options on the `com.mongodb.Mongo` instance that is used to create a `SimpleMongoDbFactory` you can refer to an existing bean using the `mongo-ref` attribute as shown below. To show another common usage pattern, this listing show the use of a property placeholder to parameterise the configuration and creating `MongoTemplate`.
|
||||
If your MongoDB authentication database differs from the target database, use the `authentication-dbname` attribute, as shown below.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<mongo:db-factory id="anotherMongoDbFactory"
|
||||
host="localhost"
|
||||
port="27017"
|
||||
dbname="database"
|
||||
username="joe"
|
||||
password="secret"
|
||||
authentication-dbname="admin"
|
||||
/>
|
||||
----
|
||||
|
||||
If you need to configure additional options on the `com.mongodb.Mongo` instance that is used to create a `SimpleMongoDbFactory` you can refer to an existing bean using the `mongo-ref` attribute as shown below. To show another common usage pattern, this listing shows the use of a property placeholder to parametrise the configuration and creating `MongoTemplate`.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -418,30 +432,30 @@ If you need to configure additional options on the `com.mongodb.Mongo` instance
|
||||
[[mongo-template]]
|
||||
== Introduction to MongoTemplate
|
||||
|
||||
The class `MongoTemplate`, located in the package `org.springframework.data.document.mongodb`, is the central class of the Spring's MongoDB support providing a rich feature set to interact with the database. The template offers convenience operations to create, update, delete and query for MongoDB documents and provides a mapping between your domain objects and MongoDB documents.
|
||||
The class `MongoTemplate`, located in the package `org.springframework.data.mongodb.core`, is the central class of the Spring's MongoDB support providing a rich feature set to interact with the database. The template offers convenience operations to create, update, delete and query for MongoDB documents and provides a mapping between your domain objects and MongoDB documents.
|
||||
|
||||
NOTE: Once configured, `MongoTemplate` is thread-safe and can be reused across multiple instances.
|
||||
|
||||
The mapping between MongoDB documents and domain classes is done by delegating to an implementation of the interface `MongoConverter`. Spring provides two implementations, `SimpleMappingConverter` and `MongoMappingConverter`, but you can also write your own converter. Please refer to the section on MongoConverters for more detailed information.
|
||||
The mapping between MongoDB documents and domain classes is done by delegating to an implementation of the interface `MongoConverter`. Spring provides two implementations, `SimpleMappingConverter` and `MappingMongoConverter`, but you can also write your own converter. Please refer to the section on MongoConverters for more detailed information.
|
||||
|
||||
The `MongoTemplate` class implements the interface `MongoOperations`. In as much as possible, the methods on `MongoOperations` are named after methods available on the MongoDB driver `Collection` object as as to make the API familiar to existing MongoDB developers who are used to the driver API. For example, you will find methods such as "find", "findAndModify", "findOne", "insert", "remove", "save", "update" and "updateMulti". The design goal was to make it as easy as possible to transition between the use of the base MongoDB driver and `MongoOperations`. A major difference in between the two APIs is that MongoOperations can be passed domain objects instead of `DBObject` and there are fluent APIs for `Query`, `Criteria`, and `Update` operations instead of populating a `DBObject` to specify the parameters for those operations.
|
||||
The `MongoTemplate` class implements the interface `MongoOperations`. In as much as possible, the methods on `MongoOperations` are named after methods available on the MongoDB driver `Collection` object to make the API familiar to existing MongoDB developers who are used to the driver API. For example, you will find methods such as "find", "findAndModify", "findOne", "insert", "remove", "save", "update" and "updateMulti". The design goal was to make it as easy as possible to transition between the use of the base MongoDB driver and `MongoOperations`. A major difference in between the two APIs is that MongoOperations can be passed domain objects instead of `DBObject` and there are fluent APIs for `Query`, `Criteria`, and `Update` operations instead of populating a `DBObject` to specify the parameters for those operations.
|
||||
|
||||
NOTE: The preferred way to reference the operations on `MongoTemplate` instance is via its interface `MongoOperations`.
|
||||
|
||||
The default converter implementation used by `MongoTemplate` is MongoMappingConverter. While the `MongoMappingConverter` can make use of additional metadata to specify the mapping of objects to documents it is also capable of converting objects that contain no additional metadata by using some conventions for the mapping of IDs and collection names. These conventions as well as the use of mapping annotations is explained in the <<mongo.mapping,Mapping chapter>>.
|
||||
The default converter implementation used by `MongoTemplate` is MappingMongoConverter. While the `MappingMongoConverter` can make use of additional metadata to specify the mapping of objects to documents it is also capable of converting objects that contain no additional metadata by using some conventions for the mapping of IDs and collection names. These conventions as well as the use of mapping annotations is explained in the <<mongo.mapping,Mapping chapter>>.
|
||||
|
||||
NOTE: In the M2 release `SimpleMappingConverter`, was the default and this class is now deprecated as its functionality has been subsumed by the MongoMappingConverter.
|
||||
NOTE: In the M2 release `SimpleMappingConverter`, was the default and this class is now deprecated as its functionality has been subsumed by the `MappingMongoConverter`.
|
||||
|
||||
Another central feature of MongoTemplate is exception translation of exceptions thrown in the MongoDB Java driver into Spring's portable Data Access Exception hierarchy. Refer to the section on <<mongo.exception,exception translation>> for more information.
|
||||
|
||||
While there are many convenience methods on `MongoTemplate` to help you easily perform common tasks if you should need to access the MongoDB driver API directly to access functionality not explicitly exposed by the MongoTemplate you can use one of several Execute callback methods to access underlying driver APIs. The execute callbacks will give you a reference to either a `com.mongodb.Collection` or a `com.mongodb.DB` object. Please see the section mongo.executioncallback[Execution Callbacks] for more information.
|
||||
|
||||
Now let's look at a examples of how to work with the `MongoTemplate` in the context of the Spring container.
|
||||
Now let's look at an example of how to work with the `MongoTemplate` in the context of the Spring container.
|
||||
|
||||
[[mongo-template.instantiating]]
|
||||
=== Instantiating MongoTemplate
|
||||
|
||||
You can use Java to create and register an instance of MongoTemplate as shown below.
|
||||
You can use Java to create and register an instance of `MongoTemplate` as shown below.
|
||||
|
||||
.Registering a com.mongodb.Mongo object and enabling Spring's exception translation support
|
||||
====
|
||||
@@ -487,7 +501,7 @@ NOTE: The preferred way to reference the operations on `MongoTemplate` instance
|
||||
[[mongo-template.writeresultchecking]]
|
||||
=== WriteResultChecking Policy
|
||||
|
||||
When in development it is very handy to either log or throw an exception if the `com.mongodb.WriteResult` returned from any MongoDB operation contains an error. It is quite common to forget to do this during development and then end up with an application that looks like it runs successfully but in fact the database was not modified according to your expectations. Set MongoTemplate's property to an enum with the following values, LOG, EXCEPTION, or NONE to either log the error, throw and exception or do nothing. The default is to use a `WriteResultChecking` value of NONE.
|
||||
When in development it is very handy to either log or throw an exception if the `com.mongodb.WriteResult` returned from any MongoDB operation contains an error. It is quite common to forget to do this during development and then end up with an application that looks like it runs successfully but in fact the database was not modified according to your expectations. Set MongoTemplate's property to an enum with the following values, `LOG`, `EXCEPTION`, or `NONE` to either log the error, throw and exception or do nothing. The default is to use a `WriteResultChecking` value of `NONE`.
|
||||
|
||||
[[mongo-template.writeconcern]]
|
||||
=== WriteConcern
|
||||
@@ -643,21 +657,21 @@ NOTE: This example is meant to show the use of save, update and remove operation
|
||||
The query syntax used in the example is explained in more detail in the section <<mongo.query,Querying Documents>>.
|
||||
|
||||
[[mongo-template.id-handling]]
|
||||
=== How the '_id' field is handled in the mapping layer
|
||||
=== How the `_id` field is handled in the mapping layer
|
||||
|
||||
MongoDB requires that you have an '_id' field for all documents. If you don't provide one the driver will assign a `ObjectId` with a generated value. When using the `MongoMappingConverter` there are certain rules that govern how properties from the Java class is mapped to this '_id' field.
|
||||
MongoDB requires that you have an `_id` field for all documents. If you don't provide one the driver will assign a `ObjectId` with a generated value. When using the `MappingMongoConverter` there are certain rules that govern how properties from the Java class is mapped to this `_id` field.
|
||||
|
||||
The following outlines what property will be mapped to the '_id' document field:
|
||||
The following outlines what property will be mapped to the `_id` document field:
|
||||
|
||||
* A property or field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the '_id' field.
|
||||
* A property or field without an annotation but named `id` will be mapped to the '_id' field.
|
||||
* A property or field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the `_id` field.
|
||||
* A property or field without an annotation but named `id` will be mapped to the `_id` field.
|
||||
|
||||
The following outlines what type conversion, if any, will be done on the property mapped to the _id document field when using the `MappingMongoConverter`, the default for `MongoTemplate`.
|
||||
|
||||
* An id property or field declared as a String in the Java class will be converted to and stored as an `ObjectId` if possible using a Spring `Converter<String, ObjectId>`. Valid conversion rules are delegated to the MongoDB Java driver. If it cannot be converted to an ObjectId, then the value will be stored as a string in the database.
|
||||
* An id property or field declared as `BigInteger` in the Java class will be converted to and stored as an `ObjectId` using a Spring `Converter<BigInteger, ObjectId>`.
|
||||
|
||||
If no field or property specified above is present in the Java class then an implicit '_id' file will be generated by the driver but not mapped to a property or field of the Java class.
|
||||
If no field or property specified above is present in the Java class then an implicit `_id` file will be generated by the driver but not mapped to a property or field of the Java class.
|
||||
|
||||
When querying and updating `MongoTemplate` will use the converter to handle conversions of the `Query` and `Update` objects that correspond to the above rules for saving documents so field names and types used in your queries will be able to match what is in your domain classes.
|
||||
|
||||
@@ -666,7 +680,7 @@ When querying and updating `MongoTemplate` will use the converter to handle conv
|
||||
|
||||
As MongoDB collections can contain documents that represent instances of a variety of types. A great example here is if you store a hierarchy of classes or simply have a class with a property of type `Object`. In the latter case the values held inside that property have to be read in correctly when retrieving the object. Thus we need a mechanism to store type information alongside the actual document.
|
||||
|
||||
To achieve that the `MappingMongoConverter` uses a `MongoTypeMapper` abstraction with `DefaultMongoTypeMapper` as it's main implementation. It's default behaviour is storing the fully qualified classname under `_class` inside the document for the top-level document as well as for every value if it's a complex type and a subtype of the property type declared.
|
||||
To achieve that the `MappingMongoConverter` uses a `MongoTypeMapper` abstraction with `DefaultMongoTypeMapper` as it's main implementation. Its default behavior is storing the fully qualified classname under `_class` inside the document for the top-level document as well as for every value if it's a complex type and a subtype of the property type declared.
|
||||
|
||||
.Type mapping
|
||||
====
|
||||
@@ -769,11 +783,11 @@ Note that we are extending the `AbstractMongoConfiguration` class and override t
|
||||
[[mongo-template.save-insert]]
|
||||
=== Methods for saving and inserting documents
|
||||
|
||||
There are several convenient methods on `MongoTemplate` for saving and inserting your objects. To have more fine grained control over the conversion process you can register Spring converters with the `MappingMongoConverter`, for example `Converter<Person, DBObject>` and `Converter<DBObject, Person>`.
|
||||
There are several convenient methods on `MongoTemplate` for saving and inserting your objects. To have more fine-grained control over the conversion process you can register Spring converters with the `MappingMongoConverter`, for example `Converter<Person, DBObject>` and `Converter<DBObject, Person>`.
|
||||
|
||||
NOTE: The difference between insert and save operations is that a save operation will perform an insert if the object is not already present.
|
||||
|
||||
The simple case of using the save operation is to save a POJO. In this case the collection name will be determined by name (not fully qualfied) of the class. You may also call the save operation with a specific collection name. The collection to store the object can be overriden using mapping metadata.
|
||||
The simple case of using the save operation is to save a POJO. In this case the collection name will be determined by name (not fully qualified) of the class. You may also call the save operation with a specific collection name. The collection to store the object can be overridden using mapping metadata.
|
||||
|
||||
When inserting or saving, if the Id property is not set, the 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 `String`, `ObjectId`, or `BigInteger`.
|
||||
|
||||
@@ -815,7 +829,7 @@ There are two ways to manage the collection name that is used for operating on t
|
||||
The MongoDB driver supports inserting a collection of documents in one operation. The methods in the MongoOperations interface that support this functionality are listed below
|
||||
|
||||
* *insert* inserts an object. If there is an existing document with the same id then an error is generated.
|
||||
* *insertAll* takes a `Collection `of objects as the first parameter. This method inspects each object and inserts it to the appropriate collection based on the rules specified above.
|
||||
* *insertAll* takes a `Collection` of objects as the first parameter. This method inspects each object and inserts it to the appropriate collection based on the rules specified above.
|
||||
* *save* saves the object overwriting any object that might exist with the same id.
|
||||
|
||||
[[mongo-template.save-insert.batch]]
|
||||
@@ -823,12 +837,12 @@ The MongoDB driver supports inserting a collection of documents in one operation
|
||||
|
||||
The MongoDB driver supports inserting a collection of documents in one operation. The methods in the MongoOperations interface that support this functionality are listed below
|
||||
|
||||
* *insert*` methods that take a `Collection` as the first argument. This inserts a list of objects in a single batch write to the database.
|
||||
* *insert* methods that take a `Collection` as the first argument. This inserts a list of objects in a single batch write to the database.
|
||||
|
||||
[[mongodb-template-update]]
|
||||
=== Updating documents in a collection
|
||||
|
||||
For updates we can elect to update the first document found using `MongoOperation`'s method `updateFirst` or we can update all documents that were found to match the query using the method `updateMulti`. Here is an example of an update of all SAVINGS accounts where we are adding a one time $50.00 bonus to the balance using the `$inc` operator.
|
||||
For updates we can elect to update the first document found using `MongoOperation` 's method `updateFirst` or we can update all documents that were found to match the query using the method `updateMulti`. Here is an example of an update of all SAVINGS accounts where we are adding a one-time $50.00 bonus to the balance using the `$inc` operator.
|
||||
|
||||
.Updating documents using the MongoTemplate
|
||||
====
|
||||
@@ -921,7 +935,7 @@ p = template.findAndModify(query, update, new FindAndModifyOptions().returnNew(t
|
||||
assertThat(p.getAge(), is(25));
|
||||
----
|
||||
|
||||
The `FindAndModifyOptions` lets you set the options of returnNew, upsert, and remove. An example extending off the previous code snippit is shown below
|
||||
The `FindAndModifyOptions` lets you set the options of returnNew, upsert, and remove. An example extending off the previous code snippet is shown below
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -975,7 +989,7 @@ IMPORTANT: Using MongoDB driver version 3 requires to set the `WriteConcern` to
|
||||
[[mongo.query]]
|
||||
== Querying Documents
|
||||
|
||||
You can express your queries using the `Query` and `Criteria` classes which have method names that mirror the native MongoDB operator names such as `lt`, `lte`, `is`, and others. The `Query` and `Criteria` classes follow a fluent API style so that you can easily chain together multiple method criteria and queries while having easy to understand code. Static imports in Java are used to help remove the need to see the 'new' keyword for creating `Query` and `Criteria` instances so as to improve readability. If you like to create `Query` instances from a plain JSON String use `BasicQuery`.
|
||||
You can express your queries using the `Query` and `Criteria` classes which have method names that mirror the native MongoDB operator names such as `lt`, `lte`, `is`, and others. The `Query` and `Criteria` classes follow a fluent API style so that you can easily chain together multiple method criteria and queries while having easy to understand the code. Static imports in Java are used to help remove the need to see the 'new' keyword for creating `Query` and `Criteria` instances so as to improve readability. If you like to create `Query` instances from a plain JSON String use `BasicQuery`.
|
||||
|
||||
.Creating a Query instance from a plain JSON String
|
||||
====
|
||||
@@ -1045,7 +1059,7 @@ There are also methods on the Criteria class for geospatial queries. Here is a l
|
||||
* `Criteria` *within* `(Circle circle)` Creates a geospatial criterion using `$geoWithin $center` operators.
|
||||
* `Criteria` *within* `(Box box)` Creates a geospatial criterion using a `$geoWithin $box` operation.
|
||||
* `Criteria` *withinSphere* `(Circle circle)` Creates a geospatial criterion using `$geoWithin $center` operators.
|
||||
* `Criteria` *near* `(Point point)` Creates a geospatial criterion using a `$near `operation
|
||||
* `Criteria` *near* `(Point point)` Creates a geospatial criterion using a `$near` operation
|
||||
* `Criteria` *nearSphere* `(Point point)` Creates a geospatial criterion using `$nearSphere$center` operations. This is only available for MongoDB 1.7 and higher.
|
||||
* `Criteria` *minDistance* `(double minDistance)` Creates a geospatial criterion using the `$minDistance` operation, for use with $near.
|
||||
* `Criteria` *maxDistance* `(double maxDistance)` Creates a geospatial criterion using the `$maxDistance` operation, for use with $near.
|
||||
@@ -1077,7 +1091,7 @@ The query methods need to specify the target type T that will be returned and th
|
||||
|
||||
MongoDB supports GeoSpatial queries through the use of operators such as `$near`, `$within`, `geoWithin` and `$nearSphere`. Methods specific to geospatial queries are available on the `Criteria` class. There are also a few shape classes, `Box`, `Circle`, and `Point` that are used in conjunction with geospatial related `Criteria` methods.
|
||||
|
||||
To understand how to perform GeoSpatial queries we will use the following Venue class taken from the integration tests.which relies on using the rich `MappingMongoConverter`.
|
||||
To understand how to perform GeoSpatial queries we will use the following Venue class taken from the integration tests which relies on using the rich `MappingMongoConverter`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1162,7 +1176,7 @@ List<Venue> venues =
|
||||
template.find(new Query(Criteria.where("location").near(point).minDistance(0.01).maxDistance(100)), Venue.class);
|
||||
----
|
||||
|
||||
To find venues near a `Point` using spherical coordines the following query can be used
|
||||
To find venues near a `Point` using spherical coordinates the following query can be used
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1188,7 +1202,7 @@ GeoResults<Restaurant> = operations.geoNear(query, Restaurant.class);
|
||||
|
||||
As you can see we use the `NearQuery` builder API to set up a query to return all `Restaurant` instances surrounding the given `Point` by 10 miles maximum. The `Metrics` enum used here actually implements an interface so that other metrics could be plugged into a distance as well. A `Metric` is backed by a multiplier to transform the distance value of the given metric into native distances. The sample shown here would consider the 10 to be miles. Using one of the pre-built in metrics (miles and kilometers) will automatically trigger the spherical flag to be set on the query. If you want to avoid that, simply hand in plain `double` values into `maxDistance(…)`. For more information see the JavaDoc of `NearQuery` and `Distance`.
|
||||
|
||||
The geo near operations return a `GeoResults` wrapper object that encapsulates `GeoResult` instances. The wrapping `GeoResults` allows to access the average distance of all results. A single `GeoResult` object simply carries the entity found plus its distance from the origin.
|
||||
The geo near operations return a `GeoResults` wrapper object that encapsulates `GeoResult` instances. The wrapping `GeoResults` allows accessing the average distance of all results. A single `GeoResult` object simply carries the entity found plus its distance from the origin.
|
||||
|
||||
[[mongo.geo-json]]
|
||||
=== GeoJSON Support
|
||||
@@ -1385,7 +1399,7 @@ Executing this will result in a collection as shown below.
|
||||
{ "_id" : "d", "value" : 1 }
|
||||
----
|
||||
|
||||
Assuming that the map and reduce functions are located in map.js and reduce.js and bundled in your jar so they are available on the classpath, you can execute a map-reduce operation and obtain the results as shown below
|
||||
Assuming that the map and reduce functions are located in `map.js` and `reduce.js` and bundled in your jar so they are available on the classpath, you can execute a map-reduce operation and obtain the results as shown below
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1463,7 +1477,7 @@ Note that you can specify additional limit and sort values as well on the query
|
||||
[[mongo.server-side-scripts]]
|
||||
== Script Operations
|
||||
|
||||
MongoDB allows to execute JavaScript functions on the server by either directly sending the script or calling a stored one. `ScriptOperations` can be accessed via `MongoTemplate` and provides basic abstraction for `JavaScript` usage.
|
||||
MongoDB allows executing JavaScript functions on the server by either directly sending the script or calling a stored one. `ScriptOperations` can be accessed via `MongoTemplate` and provides basic abstraction for `JavaScript` usage.
|
||||
|
||||
=== Example Usage
|
||||
|
||||
@@ -1486,14 +1500,14 @@ scriptOps.call("echo", "execute script via name"); <3>
|
||||
[[mongo.group]]
|
||||
== Group Operations
|
||||
|
||||
As an alternative to using Map-Reduce to perform data aggregation, you can use the http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group[`group` operation] which feels similar to using SQL's group by query style, so it may feel more approachable vs. using Map-Reduce. Using the group operations does have some limitations, for example it is not supported in a shareded environment and it returns the full result set in a single BSON object, so the result should be small, less than 10,000 keys.
|
||||
As an alternative to using Map-Reduce to perform data aggregation, you can use the http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group[`group` operation] which feels similar to using SQL's group by query style, so it may feel more approachable vs. using Map-Reduce. Using the group operations does have some limitations, for example it is not supported in a shared environment and it returns the full result set in a single BSON object, so the result should be small, less than 10,000 keys.
|
||||
|
||||
Spring provides integration with MongoDB's group operation by 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 http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/resources.html[Resource abstraction] 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 easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing JavaScript code in files if often preferable to embedding them as Java strings in your code. Note that you can still pass JavaScript code as Java strings if you prefer.
|
||||
|
||||
[[mongo.group.example]]
|
||||
=== Example Usage
|
||||
|
||||
In order to understand how group operations work the following 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.
|
||||
In order to understand how group operations work the following 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.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -1505,7 +1519,7 @@ In order to understand how group operations work the following example is used,
|
||||
{ "_id" : ObjectId("4ec1d25d41421e2015da64f6"), "x" : 3 }
|
||||
----
|
||||
|
||||
We would like to group by the only field in each row, the 'x' field and aggregate the number of times each specific value of 'x' occurs. To do this we need to create an initial document that contains our count variable and also a reduce function which will increment it each time it is encountered. The Java code to execute the group operation is shown below
|
||||
We would like to group by the only field in each row, the `x` field and aggregate the number of times each specific value of `x` occurs. To do this we need to create an initial document that contains our count variable and also a reduce function which will increment it each time it is encountered. The Java code to execute the group operation is shown below
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1594,7 +1608,7 @@ The Aggregation Framework support in Spring Data MongoDB is based on the followi
|
||||
|
||||
* `Aggregation`
|
||||
+
|
||||
An Aggregation represents a MongoDB `aggregate` operation and holds the description of the aggregation pipline instructions. Aggregations are created by inoking the appropriate `newAggregation(…)` static factory Method of the `Aggregation` class which takes the list of `AggregateOperation` as a parameter next to the optional input class.
|
||||
An Aggregation represents a MongoDB `aggregate` operation and holds the description of the aggregation pipeline instructions. Aggregations are created by invoking the appropriate `newAggregation(…)` static factory Method of the `Aggregation` class which takes the list of `AggregateOperation` as a parameter next to the optional input class.
|
||||
+
|
||||
The actual aggregate operation is executed by the `aggregate` method of the `MongoTemplate` which also takes the desired output class as parameter.
|
||||
+
|
||||
@@ -1664,22 +1678,40 @@ Note that the aggregation operations not listed here are currently not supported
|
||||
[[mongo.aggregation.projection]]
|
||||
=== Projection Expressions
|
||||
|
||||
Projection expressions are used to define the fields that are the outcome of a particular aggregation step. Projection expressions can be defined via the `project` method of the `Aggregate` class either by passing a list of `String`s or an aggregation framework `Fields` object. The projection can be extended with additional fields through a fluent API via the `and(String)` method and aliased via the `as(String)` method.
|
||||
Note that one can also define fields with aliases via the static factory method `Fields.field` of the aggregation framework that can then be used to construct a new `Fields` instance.
|
||||
Projection expressions are used to define the fields that are the outcome of a particular aggregation step. Projection expressions can be defined via the `project` method of the `Aggregate` class either by passing a list of ``String``'s or an aggregation framework `Fields` object. The projection can be extended with additional fields through a fluent API via the `and(String)` method and aliased via the `as(String)` method.
|
||||
Note that one can also define fields with aliases via the static factory method `Fields.field` of the aggregation framework that can then be used to construct a new `Fields` instance. References to projected fields in later aggregation stages are only valid by using the field name of included fields or their alias of aliased or newly defined fields. Fields not included in the projection cannot be referenced in later aggregation stages.
|
||||
|
||||
.Projection expression examples
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
project("name", "netPrice") // will generate {$project: {name: 1, netPrice: 1}}
|
||||
project().and("foo").as("bar") // will generate {$project: {bar: $foo}}
|
||||
project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, bar: $foo}}
|
||||
// will generate {$project: {name: 1, netPrice: 1}}
|
||||
project("name", "netPrice")
|
||||
|
||||
// will generate {$project: {bar: $foo}}
|
||||
project().and("foo").as("bar")
|
||||
|
||||
// will generate {$project: {a: 1, b: 1, bar: $foo}}
|
||||
project("a","b").and("foo").as("bar")
|
||||
----
|
||||
====
|
||||
|
||||
Note that more examples for project operations can be found in the `AggregationTests` class.
|
||||
.Multi-Stage Aggregation using Projection and Sorting
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
// will generate {$project: {name: 1, netPrice: 1}}, {$sort: {name: 1}}
|
||||
project("name", "netPrice"), sort(ASC, "name")
|
||||
|
||||
Note that further details regarding the projection expressions can be found in the http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project[corresponding section] of the MongoDB Aggregation Framework reference documentation.
|
||||
// will generate {$project: {bar: $foo}}, {$sort: {bar: 1}}
|
||||
project().and("foo").as("bar"), sort(ASC, "bar")
|
||||
|
||||
// this will not work
|
||||
project().and("foo").as("bar"), sort(ASC, "foo")
|
||||
----
|
||||
====
|
||||
|
||||
More examples for project operations can be found in the `AggregationTests` class. Note that further details regarding the projection expressions can be found in the http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project[corresponding section] of the MongoDB Aggregation Framework reference documentation.
|
||||
|
||||
[[mongo.aggregation.projection.expressions]]
|
||||
==== Spring Expression Support in Projection Expressions
|
||||
@@ -1749,7 +1781,7 @@ List<TagCount> tagCount = results.getMappedResults();
|
||||
* In the forth step we use the `group` operation to define a group for each `"tags"`-value for which we aggregate the occurrence count via the `count` aggregation operator and collect the result in a new field called `"n"`.
|
||||
* As a fifth step we select the field `"n"` and create an alias for the id-field generated from the previous group operation (hence the call to `previousOperation()`) with the name `"tag"`.
|
||||
* As the sixth step we sort the resulting list of tags by their occurrence count in descending order via the `sort` operation.
|
||||
* Finally we call the `aggregate` Method on the MongoTemplate in order to let MongoDB perform the acutal aggregation operation with the created `Aggregation` as an argument.
|
||||
* Finally we call the `aggregate` Method on the MongoTemplate in order to let MongoDB perform the actual aggregation operation with the created `Aggregation` as an argument.
|
||||
|
||||
Note that the input collection is explicitly specified as the `"tags"` parameter to the `aggregate` Method. If the name of the input collection is not specified explicitly, it is derived from the input-class passed as first parameter to the `newAggreation` Method.
|
||||
|
||||
@@ -1808,10 +1840,10 @@ ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);
|
||||
----
|
||||
|
||||
* The class `ZipInfo` maps the structure of the given input-collection. The class `ZipInfoStats` defines the structure in the desired output format.
|
||||
* As a first step we use the `group` operation to define a group from the input-collection. The grouping criteria is the combination of the fields `"state"` and `"city" `which forms the id structure of the group. We aggregate the value of the `"population"` property from the grouped elements with by using the `sum` operator saving the result in the field `"pop"`.
|
||||
* In a second step we use the `sort` operation to sort the intermediate-result by the fields `"pop"`, `"state"` and `"city"` in ascending order, such that the smallest city is at the top and the biggest city is at the bottom of the result. Note that the sorting on "state" and `"city"` is implicitly performed against the group id fields which Spring Data MongoDB took care of.
|
||||
* As a first step we use the `group` operation to define a group from the input-collection. The grouping criteria is the combination of the fields `"state"` and `"city"` which forms the id structure of the group. We aggregate the value of the `"population"` property from the grouped elements with by using the `sum` operator saving the result in the field `"pop"`.
|
||||
* In a second step we use the `sort` operation to sort the intermediate-result by the fields `"pop"`, `"state"` and `"city"` in ascending order, such that the smallest city is at the top and the biggest city is at the bottom of the result. Note that the sorting on `"state"` and `"city"` is implicitly performed against the group id fields which Spring Data MongoDB took care of.
|
||||
* In the third step we use a `group` operation again to group the intermediate result by `"state"`. Note that `"state"` again implicitly references an group-id field. We select the name and the population count of the biggest and smallest city with calls to the `last(…)` and `first(...)` operator respectively via the `project` operation.
|
||||
* As the forth step we select the `"state"` field from the previous `group` operation. Note that `"state"` again implicitly references an group-id field. As we do not want an implicit generated id to appear, we exclude the id from the previous operation via `and(previousOperation()).exclude()`. As we want to populate the nested `City` structures in our output-class accordingly we have to emit appropriate sub-documents with the nested method.
|
||||
* As the forth step we select the `"state"` field from the previous `group` operation. Note that `"state"` again implicitly references an group-id field. As we do not want an implicitly generated id to appear, we exclude the id from the previous operation via `and(previousOperation()).exclude()`. As we want to populate the nested `City` structures in our output-class accordingly we have to emit appropriate sub-documents with the nested method.
|
||||
* Finally as the fifth step we sort the resulting list of `StateStats` by their state name in ascending order via the `sort` operation.
|
||||
|
||||
Note that we derive the name of the input-collection from the `ZipInfo`-class passed as first parameter to the `newAggregation`-Method.
|
||||
@@ -1923,7 +1955,7 @@ List<DBObject> resultList = result.getMappedResults();
|
||||
|
||||
This example demonstrates the use of complex arithmetic operations derived from SpEL Expressions in the projection operation.
|
||||
|
||||
Note: The additional parameters passed to the `addExpression` Method can be referenced via indexer expressions according to their position. In this example we reference the parameter which is the first parameter of the parameters array via `[0]`. External parameter expressions are replaced with their respective values when the SpEL expression is transformed into a MongoDB aggregation framework expression.
|
||||
Note: The additional parameters passed to the `addExpression` Method can be referenced via indexer expressions according to their position. In this example we reference the parameter which is the first parameter of the parameters array via `[0]`. External parameter expressions are replaced with their respective values when the SpEL expression is transformed into a MongoDB aggregation framework expression.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1955,7 +1987,7 @@ Note that we can also refer to other fields of the document within the SpEL expr
|
||||
[[mongo.custom-converters]]
|
||||
== Overriding default mapping with custom converters
|
||||
|
||||
In order to have more fine grained control over the mapping process you can register Spring converters with the `MongoConverter` implementations such as the `MappingMongoConverter`.
|
||||
In order to have more fine-grained control over the mapping process you can register Spring converters with the `MongoConverter` implementations such as the `MappingMongoConverter`.
|
||||
|
||||
The `MappingMongoConverter` checks to see if there are any Spring converters that can handle a specific class before attempting to map the object itself. To 'hijack' the normal mapping strategies of the `MappingMongoConverter`, perhaps for increased performance or other custom mapping needs, you first need to create an implementation of the Spring `Converter` interface and then register it with the MappingConverter.
|
||||
|
||||
@@ -1988,7 +2020,7 @@ public class PersonWriteConverter implements Converter<Person, DBObject> {
|
||||
[[mongo.custom-converters.reader]]
|
||||
=== Reading using a Spring Converter
|
||||
|
||||
An example implementation of a Converter that converts from a DBObject ot a Person object is shownn below
|
||||
An example implementation of a Converter that converts from a DBObject to a Person object is shown below.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -2005,7 +2037,7 @@ public class PersonReadConverter implements Converter<DBObject, Person> {
|
||||
[[mongo.custom-converters.xml]]
|
||||
=== Registering Spring Converters with the MongoConverter
|
||||
|
||||
The Mongo Spring namespace provides a convenience way to register Spring `Converter`s with the `MappingMongoConverter`. The configuration snippet below shows how to manually register converter beans as well as configuring the wrapping `MappingMongoConverter` into a `MongoTemplate`.
|
||||
The Mongo Spring namespace provides a convenience way to register Spring `Converter` s with the `MappingMongoConverter`. The configuration snippet below shows how to manually register converter beans as well as configuring the wrapping `MappingMongoConverter` into a `MongoTemplate`.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -2051,7 +2083,7 @@ class MyConverter implements Converter<Person, String> { … }
|
||||
class MyConverter implements Converter<String, Person> { … }
|
||||
----
|
||||
|
||||
In case you write a `Converter` whose source and target type are native Mongo types there's no way for us to determine whether we should consider it as reading or writing converter. Registering the converter instance as both might lead to unwanted results then. E.g. a `Converter<String, Long>` is ambiguous although it probably does not make sense to try to convert all `String` instances into `Long` instances when writing. To be generally able to force the infrastructure to register a converter for one way only we provide `@ReadingConverter` as well as `@WritingConverter` to be used at the converter implementation.
|
||||
In case you write a `Converter` whose source and target type are native Mongo types there's no way for us to determine whether we should consider it as reading or writing converter. Registering the converter instance as both might lead to unwanted results then. E.g. a `Converter<String, Long>` is ambiguous although it probably does not make sense to try to convert all `String` instances into `Long` instances when writing. To be generally able to force the infrastructure to register a converter for one way only we provide `@ReadingConverter` as well as `@WritingConverter` to be used in the converter implementation.
|
||||
|
||||
[[mongo-template.index-and-collections]]
|
||||
== Index and Collection management
|
||||
@@ -2100,7 +2132,7 @@ mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"))
|
||||
[[mongo-template.index-and-collections.access]]
|
||||
=== Accessing index information
|
||||
|
||||
The IndexOperations interface has the method getIndexInfo that returns a list of IndexInfo objects. This contains all the indexes defined on the collectcion. Here is an example that defines an index on the Person class that has age property.
|
||||
The IndexOperations interface has the method getIndexInfo that returns a list of IndexInfo objects. This contains all the indexes defined on the collection. Here is an example that defines an index on the Person class that has age property.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -2151,7 +2183,7 @@ You can also get at the MongoDB driver's `DB.command( )` method using the `execu
|
||||
[[mongodb.mapping-usage.events]]
|
||||
== Lifecycle Events
|
||||
|
||||
Built into the MongoDB mapping framework are several `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`. By being based off Spring's ApplicationContext event infastructure this enables other products, such as Spring Integration, to easily receive these events as they are a well known eventing mechanism in Spring based applications.
|
||||
Built into the MongoDB mapping framework are several `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`. By being based off Spring's ApplicationContext event infrastructure this enables other products, such as Spring Integration, to easily receive these events as they are a well known eventing mechanism in Spring based applications.
|
||||
|
||||
To intercept an object before it goes through the conversion process (which turns your domain object into a `com.mongodb.DBObject`), you'd register a subclass of `AbstractMongoEventListener` that overrides the `onBeforeConvert` method. When the event is dispatched, your listener will be called and passed the domain object before it goes into the converter.
|
||||
|
||||
@@ -2191,6 +2223,8 @@ The list of callback methods that are present in AbstractMappingEventListener ar
|
||||
* `onAfterLoad` - called in MongoTemplate find, findAndRemove, findOne and getCollection methods after the DBObject is retrieved from the database.
|
||||
* `onAfterConvert` - called in MongoTemplate find, findAndRemove, findOne and getCollection methods after the DBObject retrieved from the database was converted to a POJO.
|
||||
|
||||
NOTE: Lifecycle events are only emitted for root level types. Complex types used as properties within a document root are not subject of event publication unless they are document references annotated with `@DBRef`.
|
||||
|
||||
[[mongo.exception]]
|
||||
== Exception Translation
|
||||
|
||||
@@ -2203,7 +2237,7 @@ Some of the mappings performed by the `MongoExceptionTranslator` are: com.mongod
|
||||
[[mongo.executioncallback]]
|
||||
== Execution callbacks
|
||||
|
||||
One common design feature of all Spring template classes is that all functionality is routed into one of the templates execute callback methods. This helps ensure that exceptions and any resource management that maybe required are performed consistency. While this was of much greater need in the case of JDBC and JMS than with MongoDB, it still offers a single spot for exception translation and logging to occur. As such, using thexe execute callback is the preferred way to access the MongoDB driver's `DB` and `DBCollection` objects to perform uncommon operations that were not exposed as methods on `MongoTemplate`.
|
||||
One common design feature of all Spring template classes is that all functionality is routed into one of the templates execute callback methods. This helps ensure that exceptions and any resource management that maybe required are performed consistency. While this was of much greater need in the case of JDBC and JMS than with MongoDB, it still offers a single spot for exception translation and logging to occur. As such, using these execute callback is the preferred way to access the MongoDB driver's `DB` and `DBCollection` objects to perform uncommon operations that were not exposed as methods on `MongoTemplate`.
|
||||
|
||||
Here is a list of execute callback methods.
|
||||
|
||||
@@ -2215,7 +2249,7 @@ Here is a list of execute callback methods.
|
||||
|
||||
* `<T> T` *execute* `(String collectionName, DbCallback<T> action)` Executes a DbCallback on the collection of the given name translating any exceptions as necessary.
|
||||
|
||||
* `<T> T` *executeInSession* `(DbCallback<T> action) ` Executes the given DbCallback within the same connection to the database so as to ensure consistency in a write heavy environment where you may read the data that you wrote.
|
||||
* `<T> T` *executeInSession* `(DbCallback<T> action)` Executes the given DbCallback within the same connection to the database so as to ensure consistency in a write heavy environment where you may read the data that you wrote.
|
||||
|
||||
Here is an example that uses the `CollectionCallback` to return information about an index
|
||||
|
||||
@@ -2306,7 +2340,7 @@ class GridFsClient {
|
||||
----
|
||||
====
|
||||
|
||||
The `store(…)` operations take an `InputStream`, a filename and optionally metadata information about the file to store. The metadata can be an arbitrary object which will be marshalled by the `MongoConverter` configured with the `GridFsTemplate`. Alternatively you can also provide a `DBObject` as well.
|
||||
The `store(…)` operations take an `InputStream`, a filename and optionally metadata information about the file to store. The metadata can be an arbitrary object which will be marshaled by the `MongoConverter` configured with the `GridFsTemplate`. Alternatively you can also provide a `DBObject` as well.
|
||||
|
||||
Reading files from the filesystem can either be achieved through the `find(…)` or `getResources(…)` methods. Let's have a look at the `find(…)` methods first. You can either find a single file matching a `Query` or multiple ones. To easily define file queries we provide the `GridFsCriteria` helper class. It provides static factory methods to encapsulate default metadata fields (e.g. `whereFilename()`, `whereContentType()`) or the custom one through `whereMetaData()`.
|
||||
|
||||
|
||||
@@ -1,6 +1,220 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.9.7.RELEASE (2017-01-26)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1596 - Reference to wrong annotation in cross-store reference documentation.
|
||||
* DATAMONGO-1590 - Entity new detection doesn't consider Persistable.isNew().
|
||||
* DATAMONGO-1588 - Repository will not accept Point subclass in spatial query.
|
||||
* DATAMONGO-1586 - TypeBasedAggregationOperationContext.getReferenceFor(…) exposes fields with their leaf property name.
|
||||
* DATAMONGO-1585 - Aggregation sort references target field of projected and aliased fields.
|
||||
* DATAMONGO-1578 - Add missing @Test annotation to ProjectionOperationUnitTests.
|
||||
* DATAMONGO-1577 - Fix Reference and JavaDoc spelling issues.
|
||||
* DATAMONGO-1576 - AbstractMongoEventListener methods not called when working with member fields.
|
||||
* DATAMONGO-1573 - Release 1.9.7 (Hopper SR7).
|
||||
* DATAMONGO-1508 - Documentation lacking for db-factory authentication-dbname.
|
||||
|
||||
|
||||
Changes in version 1.10.0.RELEASE (2017-01-26)
|
||||
----------------------------------------------
|
||||
* DATAMONGO-1596 - Reference to wrong annotation in cross-store reference documentation.
|
||||
* DATAMONGO-1594 - Update "what’s new" section in reference documentation.
|
||||
* DATAMONGO-1590 - Entity new detection doesn't consider Persistable.isNew().
|
||||
* DATAMONGO-1589 - Update project documentation with the CLA tool integration.
|
||||
* DATAMONGO-1588 - Repository will not accept Point subclass in spatial query.
|
||||
* DATAMONGO-1587 - Migrate ticket references in test code to Spring Framework style.
|
||||
* DATAMONGO-1586 - TypeBasedAggregationOperationContext.getReferenceFor(…) exposes fields with their leaf property name.
|
||||
* DATAMONGO-1585 - Aggregation sort references target field of projected and aliased fields.
|
||||
* DATAMONGO-1578 - Add missing @Test annotation to ProjectionOperationUnitTests.
|
||||
* DATAMONGO-1577 - Fix Reference and JavaDoc spelling issues.
|
||||
* DATAMONGO-1576 - AbstractMongoEventListener methods not called when working with member fields.
|
||||
* DATAMONGO-1575 - Treat String replacement values in StringBased queries as such unless they are SpEL expressions.
|
||||
* DATAMONGO-1574 - Release 1.10 GA (Ingalls).
|
||||
* DATAMONGO-1508 - Documentation lacking for db-factory authentication-dbname.
|
||||
|
||||
|
||||
Changes in version 1.9.6.RELEASE (2016-12-21)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1565 - Placeholders in manually defined queries not escaped properly.
|
||||
* DATAMONGO-1534 - Type hint is missing when using BulkOperations.insert.
|
||||
* DATAMONGO-1525 - Reading empty EnumSet fails.
|
||||
* DATAMONGO-1522 - Release 1.9.6 (Hopper SR6).
|
||||
|
||||
|
||||
Changes in version 1.10.0.RC1 (2016-12-21)
|
||||
------------------------------------------
|
||||
* DATAMONGO-1567 - Upgrade to a newer JDK version on TravisCI.
|
||||
* DATAMONGO-1566 - Adapt API in RepositoryFactoryBeanSupport implementation.
|
||||
* DATAMONGO-1565 - Placeholders in manually defined queries not escaped properly.
|
||||
* DATAMONGO-1564 - Split up AggregationExpressions.
|
||||
* DATAMONGO-1558 - Upgrade travis-ci profile to MongoDB 3.4.
|
||||
* DATAMONGO-1552 - Add $facet, $bucket and $bucketAuto aggregation stages.
|
||||
* DATAMONGO-1551 - Add $graphLookup aggregation stage.
|
||||
* DATAMONGO-1550 - Add $replaceRoot aggregation stage.
|
||||
* DATAMONGO-1549 - Add $count aggregation stage.
|
||||
* DATAMONGO-1548 - Add new MongoDB 3.4 aggregation operators.
|
||||
* DATAMONGO-1547 - Register repository factory in spring.factories for multi-store support.
|
||||
* DATAMONGO-1546 - Switch to new way of registering custom Jackson modules.
|
||||
* DATAMONGO-1542 - Refactor CondOperator and IfNullOperator to children of AggregationExpressions.
|
||||
* DATAMONGO-1540 - Add support for $map to aggregation.
|
||||
* DATAMONGO-1539 - Add dedicated annotations for manually declared count and delete queries.
|
||||
* DATAMONGO-1538 - Add support for $let to aggregation.
|
||||
* DATAMONGO-1536 - Add missing aggregation operators.
|
||||
* DATAMONGO-1534 - Type hint is missing when using BulkOperations.insert.
|
||||
* DATAMONGO-1533 - Add support for SpEL in GroupOperations (aggregation).
|
||||
* DATAMONGO-1530 - Support missing aggregation pipeline operators in expression support.
|
||||
* DATAMONGO-1525 - Reading empty EnumSet fails.
|
||||
* DATAMONGO-1521 - Aggregation.skip(...) expects int but new SkipOperation(...) supports long.
|
||||
* DATAMONGO-1520 - Aggregation.match should accept CriteriaDefinition.
|
||||
* DATAMONGO-1514 - SpringDataMongodbQuery should be public.
|
||||
* DATAMONGO-1513 - Non-ObjectId identifiers generated by event listeners are not populated if documents are inserted as batch.
|
||||
* DATAMONGO-1504 - Assert compatibility with MongoDB 3.4 server and driver.
|
||||
* DATAMONGO-1500 - RuntimeException for query methods with fields declaration and Pageable parameters.
|
||||
* DATAMONGO-1498 - MongoMappingContext doesn't know about types usually auto-detected (JodaTime, JDK 8 date time types).
|
||||
* DATAMONGO-1493 - Typos in reference documentation.
|
||||
* DATAMONGO-1492 - Interface AggregationExpression in package org.springframework.data.mongodb.core.aggregation should be public.
|
||||
* DATAMONGO-1491 - Add support for $filter to aggregation.
|
||||
* DATAMONGO-1490 - Change the XML data type of boolean flags to String.
|
||||
* DATAMONGO-1486 - Changes to MappingMongoConverter Result in Class Cast Exception.
|
||||
* DATAMONGO-1485 - Querydsl MongodbSerializer does not take registered converters for Enums into account.
|
||||
* DATAMONGO-1480 - Add support for noCursorTimeout in Query.
|
||||
* DATAMONGO-1479 - MappingMongoConverter.convertToMongoType causes StackOverflowError for parameterized map value types.
|
||||
* DATAMONGO-1476 - New stream method only partially makes use of collection name.
|
||||
* DATAMONGO-1471 - MappingMongoConverter attempts to set null value on potentially primitive identifier.
|
||||
* DATAMONGO-1470 - AbstractMongoConfiguraton should allow multiple base package for @Document scanning.
|
||||
* DATAMONGO-1469 - Release 1.10 RC1 (Ingalls).
|
||||
* DATAMONGO-1467 - Support partial filter expressions for indexing introduced in MongoDB 3.2.
|
||||
* DATAMONGO-1465 - String arguments passed to DefaultScriptOperations.execute() appear quoted in script.
|
||||
* DATAMONGO-1454 - Add support for exists projection in repository query derivation.
|
||||
* DATAMONGO-1406 - Query mapper does not use @Field field name when querying nested fields in combination with nested keywords.
|
||||
* DATAMONGO-1328 - Add support for mongodb 3.2 specific arithmetic operators to aggregation.
|
||||
* DATAMONGO-1327 - Add support for $stdDevSamp and $stdDevPop to aggregation ($group stage).
|
||||
* DATAMONGO-1299 - Add support for date aggregations.
|
||||
* DATAMONGO-1141 - Add support for $push $sort in Update.
|
||||
* DATAMONGO-861 - Add support for $cond and $ifNull operators in aggregation operation.
|
||||
* DATAMONGO-784 - Add support for $cmp in group or project aggregation.
|
||||
|
||||
|
||||
Changes in version 2.0.0.M1 (2016-11-23)
|
||||
----------------------------------------
|
||||
* DATAMONGO-1527 - Release 2.0 M1 (Kay).
|
||||
* DATAMONGO-1509 - Inconsistent type alias placement in list of classes.
|
||||
* DATAMONGO-1461 - Upgrade Hibernate/JPA dependencies to match Spring 5 baseline.
|
||||
* DATAMONGO-1448 - Set up 2.0 development.
|
||||
* DATAMONGO-1444 - Reactive support in Spring Data MongoDB.
|
||||
* DATAMONGO-1176 - Use org.bson types instead of com.mongodb.
|
||||
* DATAMONGO-563 - Upgrade to MongoDB driver 2.9.2 as it fixes a serious regression introduced in 2.9.0.
|
||||
* DATAMONGO-562 - Cannot create entity with OptimisticLocking (@Version) and initial id.
|
||||
|
||||
|
||||
Changes in version 1.9.5.RELEASE (2016-11-03)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1521 - Aggregation.skip(...) expects int but new SkipOperation(...) supports long.
|
||||
* DATAMONGO-1514 - SpringDataMongodbQuery should be public.
|
||||
* DATAMONGO-1513 - Non-ObjectId identifiers generated by event listeners are not populated if documents are inserted as batch.
|
||||
* DATAMONGO-1504 - Assert compatibility with MongoDB 3.4 server and driver.
|
||||
* DATAMONGO-1502 - Release 1.9.5 (Hopper SR5).
|
||||
* DATAMONGO-1500 - RuntimeException for query methods with fields declaration and Pageable parameters.
|
||||
|
||||
|
||||
Changes in version 1.9.4.RELEASE (2016-09-29)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1498 - MongoMappingContext doesn't know about types usually auto-detected (JodaTime, JDK 8 date time types).
|
||||
* DATAMONGO-1497 - MappingMongoConverter's check for whether a value is available for a property should use DbObjectAccessor.
|
||||
* DATAMONGO-1495 - Release 1.9.4 (Hopper SR4).
|
||||
|
||||
|
||||
Changes in version 1.8.6.RELEASE (2016-09-29)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1499 - Release 1.8.6 (Gosling SR6).
|
||||
* DATAMONGO-1497 - MappingMongoConverter's check for whether a value is available for a property should use DbObjectAccessor.
|
||||
|
||||
|
||||
Changes in version 1.8.5.RELEASE (2016-09-20)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1494 - Release 1.8.5 (Gosling SR5).
|
||||
* DATAMONGO-1492 - Interface AggregationExpression in package org.springframework.data.mongodb.core.aggregation should be public.
|
||||
* DATAMONGO-1485 - Querydsl MongodbSerializer does not take registered converters for Enums into account.
|
||||
* DATAMONGO-1479 - MappingMongoConverter.convertToMongoType causes StackOverflowError for parameterized map value types.
|
||||
* DATAMONGO-1471 - MappingMongoConverter attempts to set null value on potentially primitive identifier.
|
||||
* DATAMONGO-1465 - String arguments passed to DefaultScriptOperations.execute() appear quoted in script.
|
||||
* DATAMONGO-1453 - Parse error into GeoJsonPoint if coordinates are "integers".
|
||||
* DATAMONGO-1449 - Replace legacy for loop with foreach in MappingMongoConverter.
|
||||
* DATAMONGO-1445 - @Id annotated attribute of type BigInteger does not work with query methods.
|
||||
* DATAMONGO-1437 - DefaultDbRefResolver swallows cause of non DataAccessException translatable Exception.
|
||||
* DATAMONGO-1425 - NOT_CONTAINS keyword issues CONTAINS query.
|
||||
* DATAMONGO-1423 - Nested document update doesn't apply converters on embedded maps.
|
||||
* DATAMONGO-1412 - Document mapping rules for Java types to MongoDB representation.
|
||||
* DATAMONGO-1406 - Query mapper does not use @Field field name when querying nested fields in combination with nested keywords.
|
||||
* DATAMONGO-1401 - GeoJsonPoint error on update.
|
||||
|
||||
|
||||
Changes in version 1.9.3.RELEASE (2016-09-20)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1493 - Typos in reference documentation.
|
||||
* DATAMONGO-1492 - Interface AggregationExpression in package org.springframework.data.mongodb.core.aggregation should be public.
|
||||
* DATAMONGO-1486 - Changes to MappingMongoConverter Result in Class Cast Exception.
|
||||
* DATAMONGO-1485 - Querydsl MongodbSerializer does not take registered converters for Enums into account.
|
||||
* DATAMONGO-1479 - MappingMongoConverter.convertToMongoType causes StackOverflowError for parameterized map value types.
|
||||
* DATAMONGO-1471 - MappingMongoConverter attempts to set null value on potentially primitive identifier.
|
||||
* DATAMONGO-1465 - String arguments passed to DefaultScriptOperations.execute() appear quoted in script.
|
||||
* DATAMONGO-1463 - Upgrade to MongoDB Java driver 2.14.3.
|
||||
* DATAMONGO-1453 - Parse error into GeoJsonPoint if coordinates are "integers".
|
||||
* DATAMONGO-1450 - Release 1.9.3 (Hopper SR3).
|
||||
* DATAMONGO-1406 - Query mapper does not use @Field field name when querying nested fields in combination with nested keywords.
|
||||
|
||||
|
||||
Changes in version 1.10.0.M1 (2016-07-27)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-1464 - Pagination - Optimize out the count query for paging.
|
||||
* DATAMONGO-1463 - Upgrade to MongoDB Java driver 2.14.3.
|
||||
* DATAMONGO-1462 - Integrate version badge from spring.io.
|
||||
* DATAMONGO-1460 - User placeholder property for JSR-303 API.
|
||||
* DATAMONGO-1459 - Add support for any-match mode in query-by-example.
|
||||
* DATAMONGO-1457 - Add support for $slice in projection stage of aggregation.
|
||||
* DATAMONGO-1456 - Add support for $diacriticInsensitivity to text search.
|
||||
* DATAMONGO-1455 - Add support for $caseSensitive to text search.
|
||||
* DATAMONGO-1453 - Parse error into GeoJsonPoint if coordinates are "integers".
|
||||
* DATAMONGO-1449 - Replace legacy for loop with foreach in MappingMongoConverter.
|
||||
* DATAMONGO-1437 - DefaultDbRefResolver swallows cause of non DataAccessException translatable Exception.
|
||||
* DATAMONGO-1431 - Add overload of MongoOperations.stream(…) to take an explicit collection name.
|
||||
* DATAMONGO-1425 - NOT_CONTAINS keyword issues CONTAINS query.
|
||||
* DATAMONGO-1424 - Add support for "notLike" keyword in derived queries.
|
||||
* DATAMONGO-1423 - Nested document update doesn't apply converters on embedded maps.
|
||||
* DATAMONGO-1420 - Update Spring Data MongoDB version in Github readme.
|
||||
* DATAMONGO-1419 - Remove deprecations in AbstractMongoEventListener.
|
||||
* DATAMONGO-1418 - Add support for $out operand for Aggregation.
|
||||
* DATAMONGO-1416 - Standard bootstrap issues warning in converter registration.
|
||||
* DATAMONGO-1412 - Document mapping rules for Java types to MongoDB representation.
|
||||
* DATAMONGO-1411 - Enable MongoDB build on TravisCI.
|
||||
* DATAMONGO-1409 - Release 1.10 M1 (Ingalls).
|
||||
* DATAMONGO-1404 - Add support of $max and $min update operations.
|
||||
* DATAMONGO-1403 - Add maxExecutionTimeMs alias for @Meta(maxExcecutionTime).
|
||||
* DATAMONGO-1399 - Allow adding hole to GeoJson Polygon.
|
||||
* DATAMONGO-1394 - References not handled correctly when using QueryDSL.
|
||||
* DATAMONGO-1391 - Support Mongo 3.2 syntax for $unwind in aggregation.
|
||||
* DATAMONGO-1271 - Provide read lifecycle events when loading DBRefs.
|
||||
* DATAMONGO-1194 - Improve DBRef resolution for collections.
|
||||
* DATAMONGO-832 - Add support for $slice in Update.push.
|
||||
|
||||
|
||||
Changes in version 1.9.2.RELEASE (2016-06-15)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1449 - Replace legacy for loop with foreach in MappingMongoConverter.
|
||||
* DATAMONGO-1437 - DefaultDbRefResolver swallows cause of non DataAccessException translatable Exception.
|
||||
* DATAMONGO-1425 - NOT_CONTAINS keyword issues CONTAINS query.
|
||||
* DATAMONGO-1423 - Nested document update doesn't apply converters on embedded maps.
|
||||
* DATAMONGO-1416 - Standard bootstrap issues warning in converter registration.
|
||||
* DATAMONGO-1412 - Document mapping rules for Java types to MongoDB representation.
|
||||
* DATAMONGO-1411 - Enable MongoDB build on TravisCI.
|
||||
* DATAMONGO-1410 - Release 1.9.2 (Hopper SR2).
|
||||
|
||||
|
||||
Changes in version 1.9.1.RELEASE (2016-04-06)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1408 - Release 1.9.1 (Hopper SR1).
|
||||
|
||||
|
||||
Changes in version 1.9.0.RELEASE (2016-04-06)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1407 - Add pull request template.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.9 GA
|
||||
Spring Data MongoDB 1.9.7
|
||||
Copyright (c) [2010-2015] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
Reference in New Issue
Block a user