Compare commits
42 Commits
3.2.4
...
1.7.2.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
144000ed46 | ||
|
|
9f10921a48 | ||
|
|
c2127760cc | ||
|
|
d2f90bae5d | ||
|
|
b25fde4ff3 | ||
|
|
e23ca446a2 | ||
|
|
4c3b1951e2 | ||
|
|
3474f7e037 | ||
|
|
5308b8f446 | ||
|
|
e267f329fb | ||
|
|
234009cf53 | ||
|
|
ab4fb8477d | ||
|
|
76bec5e42d | ||
|
|
42beaaf47e | ||
|
|
f32afed779 | ||
|
|
b442abeef9 | ||
|
|
377c421052 | ||
|
|
fdef631c6e | ||
|
|
7ec61c9360 | ||
|
|
a95b9577da | ||
|
|
a02fe28644 | ||
|
|
d620546a3e | ||
|
|
f1621364b0 | ||
|
|
02578a0168 | ||
|
|
4bab7b32f2 | ||
|
|
0bda096e61 | ||
|
|
17139dcf3b | ||
|
|
bebfea0d33 | ||
|
|
885f0f4b70 | ||
|
|
a6000ee771 | ||
|
|
d4792cd680 | ||
|
|
949833a7db | ||
|
|
5abf1147e4 | ||
|
|
d65be17338 | ||
|
|
d1f915f702 | ||
|
|
58d50aa1ff | ||
|
|
0f30ffa090 | ||
|
|
496331d755 | ||
|
|
7658e1f1d3 | ||
|
|
221e03947b | ||
|
|
f0aed498d5 | ||
|
|
a62f6b8043 |
11
pom.xml
11
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,8 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.6.0.RELEASE</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
<version>1.6.2.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -29,7 +28,7 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.10.0.RELEASE</springdata.commons>
|
||||
<springdata.commons>1.10.2.RELEASE</springdata.commons>
|
||||
<mongo>2.13.0</mongo>
|
||||
<mongo.osgi>2.13.0</mongo.osgi>
|
||||
</properties>
|
||||
@@ -108,7 +107,7 @@
|
||||
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.13.0-SNAPSHOT</mongo>
|
||||
<mongo>2.14.0-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -124,7 +123,7 @@
|
||||
|
||||
<id>mongo3</id>
|
||||
<properties>
|
||||
<mongo>3.0.0-beta3</mongo>
|
||||
<mongo>3.0.2</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.RELEASE</version>
|
||||
<version>1.7.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@ package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -28,10 +31,13 @@ import com.mongodb.MongoCredential;
|
||||
* Parse a {@link String} to a Collection of {@link MongoCredential}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private static final Pattern GROUP_PATTERN = Pattern.compile("(\\\\?')(.*?)\\1");
|
||||
|
||||
private static final String AUTH_MECHANISM_KEY = "uri.authMechanism";
|
||||
private static final String USERNAME_PASSWORD_DELIMINATOR = ":";
|
||||
private static final String DATABASE_DELIMINATOR = "@";
|
||||
@@ -51,11 +57,7 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
|
||||
|
||||
for (String credentialString : text.split(",")) {
|
||||
|
||||
if (!text.contains(USERNAME_PASSWORD_DELIMINATOR) || !text.contains(DATABASE_DELIMINATOR)) {
|
||||
throw new IllegalArgumentException("Credentials need to be in format 'username:password@database'!");
|
||||
}
|
||||
for (String credentialString : extractCredentialsString(text)) {
|
||||
|
||||
String[] userNameAndPassword = extractUserNameAndPassword(credentialString);
|
||||
String database = extractDB(credentialString);
|
||||
@@ -68,43 +70,83 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
String authMechanism = options.getProperty(AUTH_MECHANISM_KEY);
|
||||
|
||||
if (MongoCredential.GSSAPI_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUserNamePresent(userNameAndPassword);
|
||||
credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0]));
|
||||
} else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUserNamePresent(userNameAndPassword);
|
||||
credentials.add(MongoCredential.createMongoX509Credential(userNameAndPassword[0]));
|
||||
} else if (MongoCredential.PLAIN_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createPlainCredential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else if (MongoCredential.SCRAM_SHA_1_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createScramSha1Credential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot create MongoCredentials for unknown auth mechanism '%s'!", authMechanism));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Cannot create MongoCredentials for unknown auth mechanism '%s'!", authMechanism));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
credentials.add(MongoCredential.createCredential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(
|
||||
MongoCredential.createCredential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray()));
|
||||
}
|
||||
}
|
||||
|
||||
setValue(credentials);
|
||||
}
|
||||
|
||||
private List<String> extractCredentialsString(String source) {
|
||||
|
||||
Matcher matcher = GROUP_PATTERN.matcher(source);
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
while (matcher.find()) {
|
||||
|
||||
String value = StringUtils.trimLeadingCharacter(matcher.group(), '\'');
|
||||
list.add(StringUtils.trimTrailingCharacter(value, '\''));
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
return Arrays.asList(source.split(","));
|
||||
}
|
||||
|
||||
private static String[] extractUserNameAndPassword(String text) {
|
||||
|
||||
int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
String userNameAndPassword = text.substring(0, dbSeperationIndex);
|
||||
return userNameAndPassword.split(USERNAME_PASSWORD_DELIMINATOR);
|
||||
int index = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
|
||||
index = index != -1 ? index : text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
|
||||
return index == -1 ? new String[] {} : text.substring(0, index).split(USERNAME_PASSWORD_DELIMINATOR);
|
||||
}
|
||||
|
||||
private static String extractDB(String text) {
|
||||
|
||||
int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
|
||||
if (dbSeperationIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String tmp = text.substring(dbSeperationIndex + 1);
|
||||
int optionsSeperationIndex = tmp.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
|
||||
@@ -129,4 +171,28 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static void verifyUsernameAndPasswordPresent(String[] source) {
|
||||
|
||||
verifyUserNamePresent(source);
|
||||
|
||||
if (source.length != 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Credentials need to specify username and password like in 'username:password@database'!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDatabasePresent(String source) {
|
||||
|
||||
if (!StringUtils.hasText(source)) {
|
||||
throw new IllegalArgumentException("Credentials need to specify database like in 'username:password@database'!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyUserNamePresent(String[] source) {
|
||||
|
||||
if (source.length == 0 || !StringUtils.hasText(source[0])) {
|
||||
throw new IllegalArgumentException("Credentials need to specify username!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.mongodb.DBCursor;
|
||||
interface CursorPreparer {
|
||||
|
||||
/**
|
||||
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.
|
||||
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
|
||||
*
|
||||
* @param cursor
|
||||
*/
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MongoAction {
|
||||
* @param collectionName the collection name, must not be {@literal null} or empty.
|
||||
* @param entityType the POJO that is being operated against
|
||||
* @param document the converted DBObject from the POJO or Spring Update object
|
||||
* @param query the converted DBOjbect from the Spring Query object
|
||||
* @param query the converted DBObject from the Spring Query object
|
||||
*/
|
||||
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
|
||||
String collectionName, Class<?> entityType, DBObject document, DBObject query) {
|
||||
|
||||
@@ -123,7 +123,7 @@ public abstract class MongoDbUtils {
|
||||
|
||||
DB db = mongo.getDB(databaseName);
|
||||
|
||||
if (requiresAuthDbAuthentication(credentials)) {
|
||||
if (!(mongo instanceof MongoClient) && requiresAuthDbAuthentication(credentials)) {
|
||||
ReflectiveDbInvoker.authenticate(mongo, db, credentials, authenticationDatabaseName);
|
||||
}
|
||||
|
||||
@@ -199,8 +199,8 @@ public abstract class MongoDbUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if credentials present. In case we're using a monog-java-driver version 3 or above we do not have the need
|
||||
* for authentication as the auth data has to be provied within the MongoClient
|
||||
* Check if credentials present. In case we're using a mongo-java-driver version 3 or above we do not have the need
|
||||
* for authentication as the auth data has to be provided within the MongoClient
|
||||
*
|
||||
* @param credentials
|
||||
* @return
|
||||
|
||||
@@ -136,6 +136,7 @@ import com.mongodb.util.JSONParseException;
|
||||
* @author Thomas Darimont
|
||||
* @author Chuong Ngo
|
||||
* @author Christoph Strobl
|
||||
* @author Doménique Tilleuil
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
@@ -335,9 +336,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
DBObject mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), persistentEntity);
|
||||
|
||||
DBCursor cursor = collection.find(mappedQuery, mappedFields);
|
||||
QueryCursorPreparer cursorPreparer = new QueryCursorPreparer(query, entityType);
|
||||
|
||||
ReadDbObjectCallback<T> readCallback = new ReadDbObjectCallback<T>(mongoConverter, entityType);
|
||||
|
||||
return new CloseableIterableCusorAdapter<T>(cursor, exceptionTranslator, readCallback);
|
||||
return new CloseableIterableCusorAdapter<T>(cursorPreparer.prepare(cursor), exceptionTranslator, readCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -382,7 +385,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
CommandResult result = execute(new DbCallback<CommandResult>() {
|
||||
public CommandResult doInDB(DB db) throws MongoException, DataAccessException {
|
||||
return db.command(command, readPreference);
|
||||
return readPreference != null ? db.command(command, readPreference) : db.command(command);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -629,7 +632,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
BasicDBObject command = new BasicDBObject("geoNear", collection);
|
||||
command.putAll(near.toDBObject());
|
||||
|
||||
CommandResult commandResult = executeCommand(command);
|
||||
CommandResult commandResult = executeCommand(command, this.readPreference);
|
||||
List<Object> results = (List<Object>) commandResult.get("results");
|
||||
results = results == null ? Collections.emptyList() : results;
|
||||
|
||||
@@ -841,27 +844,33 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
protected <T> void doInsertAll(Collection<? extends T> listToSave, MongoWriter<T> writer) {
|
||||
Map<String, List<T>> objs = new HashMap<String, List<T>>();
|
||||
|
||||
for (T o : listToSave) {
|
||||
Map<String, List<T>> elementsByCollection = new HashMap<String, List<T>>();
|
||||
|
||||
for (T element : listToSave) {
|
||||
|
||||
if (element == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(element.getClass());
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(o.getClass());
|
||||
if (entity == null) {
|
||||
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
|
||||
+ o.getClass().getName());
|
||||
throw new InvalidDataAccessApiUsageException("No PersistentEntity information found for " + element.getClass());
|
||||
}
|
||||
|
||||
String collection = entity.getCollection();
|
||||
List<T> collectionElements = elementsByCollection.get(collection);
|
||||
|
||||
List<T> objList = objs.get(collection);
|
||||
if (null == objList) {
|
||||
objList = new ArrayList<T>();
|
||||
objs.put(collection, objList);
|
||||
if (null == collectionElements) {
|
||||
collectionElements = new ArrayList<T>();
|
||||
elementsByCollection.put(collection, collectionElements);
|
||||
}
|
||||
objList.add(o);
|
||||
|
||||
collectionElements.add(element);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<T>> entry : objs.entrySet()) {
|
||||
for (Map.Entry<String, List<T>> entry : elementsByCollection.entrySet()) {
|
||||
doInsertBatch(entry.getKey(), entry.getValue(), this.mongoConverter);
|
||||
}
|
||||
}
|
||||
@@ -1494,7 +1503,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
LOGGER.debug("Executing aggregation: {}", serializeToJsonSafely(command));
|
||||
}
|
||||
|
||||
CommandResult commandResult = executeCommand(command);
|
||||
CommandResult commandResult = executeCommand(command, this.readPreference);
|
||||
handleCommandError(commandResult, command);
|
||||
|
||||
return new AggregationResults<O>(returnPotentiallyMappedResults(outputType, commandResult), commandResult);
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.net.UnknownHostException;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
@@ -103,8 +104,8 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException {
|
||||
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())),
|
||||
true, uri.getDatabase());
|
||||
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())), true,
|
||||
uri.getDatabase());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +133,11 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
|
||||
boolean mongoInstanceCreated, String authenticationDatabaseName) {
|
||||
|
||||
if (mongo instanceof MongoClient && (credentials != null && !UserCredentials.NO_CREDENTIALS.equals(credentials))) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"Usage of 'UserCredentials' with 'MongoClient' is no longer supported. Please use 'MongoCredential' for 'MongoClient' or just 'Mongo'.");
|
||||
}
|
||||
|
||||
Assert.notNull(mongo, "Mongo must not be null");
|
||||
Assert.hasText(databaseName, "Database name must not be empty");
|
||||
Assert.isTrue(databaseName.matches("[\\w-]+"),
|
||||
|
||||
@@ -88,7 +88,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFields} instance for the given fields in either sythetic or non-synthetic way.
|
||||
* Creates a new {@link ExposedFields} instance for the given fields in either synthetic or non-synthetic way.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @param synthetic
|
||||
@@ -107,7 +107,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFields} with the given orignals and synthetics.
|
||||
* Creates a new {@link ExposedFields} with the given originals and synthetics.
|
||||
*
|
||||
* @param originals must not be {@literal null}.
|
||||
* @param synthetic must not be {@literal null}.
|
||||
@@ -363,7 +363,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the referenve value for the given field reference. Will return 1 for a synthetic, unaliased field or the
|
||||
* Returns the reference value for the given field reference. Will return 1 for a synthetic, unaliased field or the
|
||||
* raw rendering of the reference otherwise.
|
||||
*
|
||||
* @return
|
||||
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -40,6 +41,7 @@ import com.mongodb.DBObject;
|
||||
* @author Tobias Trelle
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.3
|
||||
*/
|
||||
public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
@@ -763,6 +765,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return field;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#getExposedField()
|
||||
*/
|
||||
@Override
|
||||
public ExposedField getExposedField() {
|
||||
|
||||
if (!getField().isAliased()) {
|
||||
return super.getExposedField();
|
||||
}
|
||||
|
||||
return new ExposedField(new AggregationField(getField().getName()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this {@link OperationProjection} with the given alias.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2015 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,7 +20,7 @@ import java.math.BigInteger;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
|
||||
@@ -46,10 +46,8 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
|
||||
*
|
||||
* @param conversionService
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public AbstractMongoConverter(GenericConversionService conversionService) {
|
||||
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
|
||||
: conversionService;
|
||||
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -106,7 +106,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
*/
|
||||
@Override
|
||||
public DBObject fetch(DBRef dbRef) {
|
||||
return ReflectiveDBRefResolver.fetch(mongoDbFactory.getDb(), dbRef);
|
||||
return ReflectiveDBRefResolver.fetch(mongoDbFactory, dbRef);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,8 +136,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param typeMapper the typeMapper to set
|
||||
*/
|
||||
public void setTypeMapper(MongoTypeMapper typeMapper) {
|
||||
this.typeMapper = typeMapper == null ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY,
|
||||
mappingContext) : typeMapper;
|
||||
this.typeMapper = typeMapper == null
|
||||
? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext) : typeMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -238,7 +238,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
|
||||
entity, provider, path.getCurrentObject());
|
||||
|
||||
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, path);
|
||||
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
|
||||
path);
|
||||
}
|
||||
|
||||
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, final ObjectPath path) {
|
||||
@@ -510,8 +511,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
: new BasicDBObject();
|
||||
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, propDbObj);
|
||||
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext
|
||||
.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
|
||||
? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
|
||||
|
||||
writeInternal(obj, propDbObj, entity);
|
||||
accessor.put(prop, propDbObj);
|
||||
@@ -700,8 +701,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
if (mapKeyDotReplacement == null) {
|
||||
throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make "
|
||||
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source));
|
||||
throw new MappingException(String.format(
|
||||
"Map key %s contains dots but no replacement was configured! Make "
|
||||
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!",
|
||||
source));
|
||||
}
|
||||
|
||||
return source.replaceAll("\\.", mapKeyDotReplacement);
|
||||
@@ -719,8 +722,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return (String) key;
|
||||
}
|
||||
|
||||
return conversions.hasCustomWriteTarget(key.getClass(), String.class) ? (String) getPotentiallyConvertedSimpleWrite(key)
|
||||
: key.toString();
|
||||
return conversions.hasCustomWriteTarget(key.getClass(), String.class)
|
||||
? (String) getPotentiallyConvertedSimpleWrite(key) : key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,16 +892,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
|
||||
|
||||
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
|
||||
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
|
||||
.createCollection(collectionType, rawComponentType, sourceValue.size());
|
||||
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
|
||||
: CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
|
||||
|
||||
for (int i = 0; i < sourceValue.size(); i++) {
|
||||
|
||||
Object dbObjItem = sourceValue.get(i);
|
||||
|
||||
if (dbObjItem instanceof DBRef) {
|
||||
items.add(DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem),
|
||||
path));
|
||||
items.add(
|
||||
DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), path));
|
||||
} else if (dbObjItem instanceof DBObject) {
|
||||
items.add(read(componentType, (DBObject) dbObjItem, path));
|
||||
} else {
|
||||
@@ -1016,10 +1019,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
this.write(obj, newDbo);
|
||||
|
||||
if (typeInformation == null) {
|
||||
return removeTypeInfoRecursively(newDbo);
|
||||
return removeTypeInfo(newDbo, true);
|
||||
}
|
||||
|
||||
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfoRecursively(newDbo);
|
||||
if (typeInformation.getType().equals(NestedDocument.class)) {
|
||||
return removeTypeInfo(newDbo, false);
|
||||
}
|
||||
|
||||
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfo(newDbo, true);
|
||||
}
|
||||
|
||||
public BasicDBList maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
|
||||
@@ -1033,12 +1040,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the type information from the conversion result.
|
||||
* Removes the type information from the entire conversion result.
|
||||
*
|
||||
* @param object
|
||||
* @param recursively whether to apply the removal recursively
|
||||
* @return
|
||||
*/
|
||||
private Object removeTypeInfoRecursively(Object object) {
|
||||
private Object removeTypeInfo(Object object, boolean recursively) {
|
||||
|
||||
if (!(object instanceof DBObject)) {
|
||||
return object;
|
||||
@@ -1046,19 +1054,29 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
DBObject dbObject = (DBObject) object;
|
||||
String keyToRemove = null;
|
||||
|
||||
for (String key : dbObject.keySet()) {
|
||||
|
||||
if (typeMapper.isTypeKey(key)) {
|
||||
keyToRemove = key;
|
||||
if (recursively) {
|
||||
|
||||
Object value = dbObject.get(key);
|
||||
|
||||
if (value instanceof BasicDBList) {
|
||||
for (Object element : (BasicDBList) value) {
|
||||
removeTypeInfo(element, recursively);
|
||||
}
|
||||
} else {
|
||||
removeTypeInfo(value, recursively);
|
||||
}
|
||||
}
|
||||
|
||||
Object value = dbObject.get(key);
|
||||
if (value instanceof BasicDBList) {
|
||||
for (Object element : (BasicDBList) value) {
|
||||
removeTypeInfoRecursively(element);
|
||||
if (typeMapper.isTypeKey(key)) {
|
||||
|
||||
keyToRemove = key;
|
||||
|
||||
if (!recursively) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
removeTypeInfoRecursively(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1122,8 +1140,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private class ConverterAwareSpELExpressionParameterValueProvider extends
|
||||
SpELExpressionParameterValueProvider<MongoPersistentProperty> {
|
||||
private class ConverterAwareSpELExpressionParameterValueProvider
|
||||
extends SpELExpressionParameterValueProvider<MongoPersistentProperty> {
|
||||
|
||||
private final ObjectPath path;
|
||||
|
||||
@@ -1135,7 +1153,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param delegate must not be {@literal null}.
|
||||
*/
|
||||
public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator,
|
||||
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, ObjectPath path) {
|
||||
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate,
|
||||
ObjectPath path) {
|
||||
|
||||
super(evaluator, conversionService, delegate);
|
||||
this.path = path;
|
||||
@@ -1194,4 +1213,15 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
DBObject readRef(DBRef ref) {
|
||||
return dbRefResolver.fetch(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker class used to indicate we have a non root document object here that might be used within an update - so we
|
||||
* need to preserve type hints for potential nested elements but need to remove it on top level.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.8
|
||||
*/
|
||||
static class NestedDocument {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
@@ -58,6 +61,7 @@ public class QueryMapper {
|
||||
|
||||
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
|
||||
private static final DBObject META_TEXT_SCORE = new BasicDBObject("$meta", "textScore");
|
||||
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
|
||||
|
||||
private enum MetaMapping {
|
||||
FORCE, WHEN_PRESENT, IGNORE;
|
||||
@@ -250,8 +254,8 @@ public class QueryMapper {
|
||||
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
|
||||
Object value = keyword.getValue();
|
||||
|
||||
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue(
|
||||
property.with(keyword.getKey()), value);
|
||||
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
|
||||
: getMappedValue(property.with(keyword.getKey()), value);
|
||||
|
||||
return new BasicDBObject(keyword.key, convertedValue);
|
||||
}
|
||||
@@ -473,8 +477,8 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
try {
|
||||
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService
|
||||
.convert(id, ObjectId.class) : delegateConvertToMongoType(id, null);
|
||||
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
|
||||
: delegateConvertToMongoType(id, null);
|
||||
} catch (ConversionException o_O) {
|
||||
return delegateConvertToMongoType(id, null);
|
||||
}
|
||||
@@ -657,6 +661,10 @@ public class QueryMapper {
|
||||
public Association<MongoPersistentProperty> getAssociation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public TypeInformation<?> getTypeHint() {
|
||||
return ClassTypeInformation.OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -816,7 +824,7 @@ public class QueryMapper {
|
||||
|
||||
try {
|
||||
|
||||
PropertyPath path = PropertyPath.from(pathExpression, entity.getTypeInformation());
|
||||
PropertyPath path = PropertyPath.from(pathExpression.replaceAll("\\.\\d", ""), entity.getTypeInformation());
|
||||
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path);
|
||||
|
||||
Iterator<MongoPersistentProperty> iterator = propertyPath.iterator();
|
||||
@@ -862,6 +870,27 @@ public class QueryMapper {
|
||||
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
|
||||
return new AssociationConverter(getAssociation());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation<?> getTypeHint() {
|
||||
|
||||
MongoPersistentProperty property = getProperty();
|
||||
|
||||
if (property == null) {
|
||||
return super.getTypeHint();
|
||||
}
|
||||
|
||||
if (property.getActualType().isInterface()
|
||||
|| java.lang.reflect.Modifier.isAbstract(property.getActualType().getModifiers())) {
|
||||
return ClassTypeInformation.OBJECT;
|
||||
}
|
||||
|
||||
return NESTED_DOCUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,9 +20,9 @@ import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
@@ -51,12 +51,14 @@ class ReflectiveDBRefResolver {
|
||||
* @param ref must not be {@literal null}.
|
||||
* @return the document that this references.
|
||||
*/
|
||||
public static DBObject fetch(DB db, DBRef ref) {
|
||||
public static DBObject fetch(MongoDbFactory factory, DBRef ref) {
|
||||
|
||||
Assert.notNull(ref, "DBRef to fetch must not be null!");
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return db.getCollection(ref.getCollectionName()).findOne(ref.getId());
|
||||
|
||||
Assert.notNull(factory, "DbFactory to fetch DB from must not be null!");
|
||||
return factory.getDb().getCollection(ref.getCollectionName()).findOne(ref.getId());
|
||||
}
|
||||
|
||||
return (DBObject) invokeMethod(FETCH_METHOD, ref);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 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.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifier;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifiers;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -65,8 +66,8 @@ public class UpdateMapper extends QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) {
|
||||
return entity == null ? super.delegateConvertToMongoType(source, null) : converter.convertToMongoType(source,
|
||||
entity.getTypeInformation());
|
||||
return converter.convertToMongoType(source,
|
||||
entity == null ? ClassTypeInformation.OBJECT : getTypeHintForEntity(source, entity));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -89,7 +90,7 @@ public class UpdateMapper extends QueryMapper {
|
||||
return getMappedUpdateModifier(field, rawValue);
|
||||
}
|
||||
|
||||
return super.getMappedObjectForField(field, getMappedValue(field, rawValue));
|
||||
return super.getMappedObjectForField(field, rawValue);
|
||||
}
|
||||
|
||||
private Entry<String, Object> getMappedUpdateModifier(Field field, Object rawValue) {
|
||||
@@ -97,14 +98,14 @@ public class UpdateMapper extends QueryMapper {
|
||||
|
||||
if (rawValue instanceof Modifier) {
|
||||
|
||||
value = getMappedValue((Modifier) rawValue);
|
||||
value = getMappedValue(field, (Modifier) rawValue);
|
||||
|
||||
} else if (rawValue instanceof Modifiers) {
|
||||
|
||||
DBObject modificationOperations = new BasicDBObject();
|
||||
|
||||
for (Modifier modifier : ((Modifiers) rawValue).getModifiers()) {
|
||||
modificationOperations.putAll(getMappedValue(modifier).toMap());
|
||||
modificationOperations.putAll(getMappedValue(field, modifier).toMap());
|
||||
}
|
||||
|
||||
value = modificationOperations;
|
||||
@@ -132,12 +133,30 @@ public class UpdateMapper extends QueryMapper {
|
||||
return value instanceof Query;
|
||||
}
|
||||
|
||||
private DBObject getMappedValue(Modifier modifier) {
|
||||
private DBObject getMappedValue(Field field, Modifier modifier) {
|
||||
|
||||
Object value = converter.convertToMongoType(modifier.getValue(), ClassTypeInformation.OBJECT);
|
||||
TypeInformation<?> typeHint = field == null ? ClassTypeInformation.OBJECT : field.getTypeHint();
|
||||
|
||||
Object value = converter.convertToMongoType(modifier.getValue(), typeHint);
|
||||
return new BasicDBObject(modifier.getKey(), value);
|
||||
}
|
||||
|
||||
private TypeInformation<?> getTypeHintForEntity(Object source, MongoPersistentEntity<?> entity) {
|
||||
|
||||
TypeInformation<?> info = entity.getTypeInformation();
|
||||
Class<?> type = info.getActualType().getType();
|
||||
|
||||
if (source == null || type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) {
|
||||
return info;
|
||||
}
|
||||
|
||||
if (!type.equals(source.getClass())) {
|
||||
return info;
|
||||
}
|
||||
|
||||
return NESTED_DOCUMENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper#createPropertyField(org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.String, org.springframework.data.mapping.context.MappingContext)
|
||||
@@ -146,8 +165,8 @@ public class UpdateMapper extends QueryMapper {
|
||||
protected Field createPropertyField(MongoPersistentEntity<?> entity, String key,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
|
||||
|
||||
return entity == null ? super.createPropertyField(entity, key, mappingContext) : //
|
||||
new MetadataBackedUpdateField(entity, key, mappingContext);
|
||||
return entity == null ? super.createPropertyField(entity, key, mappingContext)
|
||||
: new MetadataBackedUpdateField(entity, key, mappingContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,7 +252,35 @@ public class UpdateMapper extends QueryMapper {
|
||||
protected String mapPropertyName(MongoPersistentProperty property) {
|
||||
|
||||
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
|
||||
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
|
||||
|
||||
boolean inspect = iterator.hasNext();
|
||||
while (inspect) {
|
||||
|
||||
String partial = iterator.next();
|
||||
|
||||
boolean isPositional = isPositionalParameter(partial);
|
||||
if (isPositional) {
|
||||
mappedName += "." + partial;
|
||||
}
|
||||
|
||||
inspect = isPositional && iterator.hasNext();
|
||||
}
|
||||
|
||||
return mappedName;
|
||||
}
|
||||
|
||||
boolean isPositionalParameter(String partial) {
|
||||
|
||||
if (partial.equals("$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Long.valueOf(partial);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,22 +16,24 @@
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* {@link IndexResolver} finds those {@link IndexDefinition}s to be created for a given class.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
* @since 1.5
|
||||
*/
|
||||
interface IndexResolver {
|
||||
|
||||
/**
|
||||
* Find and create {@link IndexDefinition}s for properties of given {@code type}. {@link IndexDefinition}s are created
|
||||
* Find and create {@link IndexDefinition}s for properties of given {@link TypeInformation}. {@link IndexDefinition}s are created
|
||||
* for properties and types with {@link Indexed}, {@link CompoundIndexes} or {@link GeoSpatialIndexed}.
|
||||
*
|
||||
* @param type
|
||||
* @param typeInformation
|
||||
* @return Empty {@link Iterable} in case no {@link IndexDefinition} could be resolved for type.
|
||||
*/
|
||||
Iterable<? extends IndexDefinitionHolder> resolveIndexForClass(Class<?> type);
|
||||
Iterable<? extends IndexDefinitionHolder> resolveIndexFor(TypeInformation<?> typeInformation);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -60,4 +60,10 @@ public class MongoMappingEventPublisher implements ApplicationEventPublisher {
|
||||
indexCreator.onApplicationEvent((MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>) event);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)
|
||||
*/
|
||||
public void publishEvent(Object event) {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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,7 +29,6 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexRes
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -43,8 +42,7 @@ import org.springframework.util.Assert;
|
||||
* @author Laurent Canet
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoPersistentEntityIndexCreator implements
|
||||
ApplicationListener<MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>> {
|
||||
public class MongoPersistentEntityIndexCreator implements ApplicationListener<MappingContextEvent<?, ?>> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class);
|
||||
|
||||
@@ -54,7 +52,7 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
private final IndexResolver indexResolver;
|
||||
|
||||
/**
|
||||
* Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
|
||||
* Creates a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
|
||||
* {@link MongoDbFactory}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
@@ -65,7 +63,7 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
|
||||
* Creates a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and
|
||||
* {@link MongoDbFactory}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
@@ -92,7 +90,7 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
||||
*/
|
||||
public void onApplicationEvent(MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty> event) {
|
||||
public void onApplicationEvent(MappingContextEvent<?, ?> event) {
|
||||
|
||||
if (!event.wasEmittedBy(mappingContext)) {
|
||||
return;
|
||||
@@ -102,7 +100,7 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
|
||||
// Double check type as Spring infrastructure does not consider nested generics
|
||||
if (entity instanceof MongoPersistentEntity) {
|
||||
checkForIndexes(event.getPersistentEntity());
|
||||
checkForIndexes((MongoPersistentEntity<?>) entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,15 +123,15 @@ public class MongoPersistentEntityIndexCreator implements
|
||||
private void checkForAndCreateIndexes(MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (entity.findAnnotation(Document.class) != null) {
|
||||
for (IndexDefinitionHolder indexToCreate : indexResolver.resolveIndexForClass(entity.getType())) {
|
||||
for (IndexDefinitionHolder indexToCreate : indexResolver.resolveIndexFor(entity.getTypeInformation())) {
|
||||
createIndex(indexToCreate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createIndex(IndexDefinitionHolder indexDefinition) {
|
||||
mongoDbFactory.getDb().getCollection(indexDefinition.getCollection())
|
||||
.createIndex(indexDefinition.getIndexKeys(), indexDefinition.getIndexOptions());
|
||||
mongoDbFactory.getDb().getCollection(indexDefinition.getCollection()).createIndex(indexDefinition.getIndexKeys(),
|
||||
indexDefinition.getIndexOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -51,6 +52,7 @@ import com.mongodb.util.JSON;
|
||||
* scanning related annotations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
* @since 1.5
|
||||
*/
|
||||
public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
@@ -70,13 +72,12 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexResolver#resolveIndexForClass(java.lang.Class)
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexResolver#resolveIndexForClass(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
public List<IndexDefinitionHolder> resolveIndexForClass(Class<?> type) {
|
||||
return resolveIndexForEntity(mappingContext.getPersistentEntity(type));
|
||||
public Iterable<? extends IndexDefinitionHolder> resolveIndexFor(TypeInformation<?> typeInformation) {
|
||||
return resolveIndexForEntity(mappingContext.getPersistentEntity(typeInformation));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +260,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
LOGGER.info(e.getMessage(), e);
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
LOGGER.info(
|
||||
String.format("Potentially invald index structure discovered. Breaking operation for %s.",
|
||||
String.format("Potentially invalid index structure discovered. Breaking operation for %s.",
|
||||
entity.getName()), e);
|
||||
}
|
||||
} else if (includeOptions.isForce() || indexed != null) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.core.mapping;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -305,28 +306,44 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
*/
|
||||
private static class PropertyTypeAssertionHandler implements PropertyHandler<MongoPersistentProperty> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PropertyHandler#doWithPersistentProperty(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
potentiallyAssertTextScoreType(persistentProperty);
|
||||
potentiallyAssertLanguageType(persistentProperty);
|
||||
potentiallyAssertDBRefTargetType(persistentProperty);
|
||||
}
|
||||
|
||||
private void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
|
||||
private static void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
if (persistentProperty.isExplicitLanguageProperty()) {
|
||||
assertPropertyType(persistentProperty, String.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void potentiallyAssertTextScoreType(MongoPersistentProperty persistentProperty) {
|
||||
private static void potentiallyAssertTextScoreType(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
if (persistentProperty.isTextScoreProperty()) {
|
||||
assertPropertyType(persistentProperty, Float.class, Double.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPropertyType(MongoPersistentProperty persistentProperty, Class<?>... validMatches) {
|
||||
private static void potentiallyAssertDBRefTargetType(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
if (persistentProperty.isDbReference() && persistentProperty.getDBRef().lazy()) {
|
||||
if (persistentProperty.isArray() || Modifier.isFinal(persistentProperty.getActualType().getModifiers())) {
|
||||
throw new MappingException(String.format(
|
||||
"Invalid lazy DBRef property for %s. Found %s which must not be an array nor a final class.",
|
||||
persistentProperty.getField(), persistentProperty.getActualType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertPropertyType(MongoPersistentProperty persistentProperty, Class<?>... validMatches) {
|
||||
|
||||
for (Class<?> potentialMatch : validMatches) {
|
||||
if (ClassUtils.isAssignable(potentialMatch, persistentProperty.getActualType())) {
|
||||
@@ -334,10 +351,9 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
}
|
||||
}
|
||||
|
||||
throw new MappingException(String.format("Missmatching types for %s. Found %s expected one of %s.",
|
||||
persistentProperty.getField(), persistentProperty.getActualType(),
|
||||
StringUtils.arrayToCommaDelimitedString(validMatches)));
|
||||
throw new MappingException(
|
||||
String.format("Missmatching types for %s. Found %s expected one of %s.", persistentProperty.getField(),
|
||||
persistentProperty.getActualType(), StringUtils.arrayToCommaDelimitedString(validMatches)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
|
||||
int getFieldOrder();
|
||||
|
||||
/**
|
||||
* Returns whether the propert is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
|
||||
* Returns whether the property is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
|
||||
* {@link #getDBRef()} to return an non-{@literal null} value.
|
||||
*
|
||||
* @return
|
||||
|
||||
@@ -176,7 +176,7 @@ public class Query {
|
||||
|
||||
for (Order order : sort) {
|
||||
if (order.isIgnoreCase()) {
|
||||
throw new IllegalArgumentException(String.format("Gven sort contained an Order for %s with ignore case! "
|
||||
throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! "
|
||||
+ "MongoDB does not support sorting ignoreing case currently!", order.getProperty()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TextCriteria implements CriteriaDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* For a full list of supported languages see the mongdodb reference manual for <a
|
||||
* For a full list of supported languages see the mongodb reference manual for <a
|
||||
* href="http://docs.mongodb.org/manual/reference/text-search-languages/">Text Search Languages</a>.
|
||||
*
|
||||
* @param language
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link Update} instance from the given {@link DBObject}. Allows to explicitly exlude fields from making
|
||||
* Creates an {@link Update} instance from the given {@link DBObject}. Allows to explicitly exclude fields from making
|
||||
* it into the created {@link Update} object. Note, that this will set attributes directly and <em>not</em> use
|
||||
* {@literal $set}. This means fields not given in the {@link DBObject} will be nulled when executing the update. To
|
||||
* create an only-updating {@link Update} instance of a {@link DBObject}, call {@link #set(String, Object)} for each
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.springframework.data.annotation.QueryAnnotation;
|
||||
public @interface Query {
|
||||
|
||||
/**
|
||||
* Takes a MongoDB JSON string to define the actual query to be executed. This one will take precendece over the
|
||||
* Takes a MongoDB JSON string to define the actual query to be executed. This one will take precedence over the
|
||||
* method name then.
|
||||
*
|
||||
* @return
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
@@ -44,6 +46,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.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Custom query creator to create Mongo criterias.
|
||||
@@ -55,6 +58,7 @@ import org.springframework.util.Assert;
|
||||
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MongoQueryCreator.class);
|
||||
private static final Pattern PUNCTATION_PATTERN = Pattern.compile("\\p{Punct}");
|
||||
private final MongoParameterAccessor accessor;
|
||||
private final boolean isGeoNearQuery;
|
||||
|
||||
@@ -276,19 +280,23 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
private Criteria createLikeRegexCriteriaOrThrow(Part part, MongoPersistentProperty property, Criteria criteria,
|
||||
PotentiallyConvertingIterator parameters, boolean shouldNegateExpression) {
|
||||
|
||||
PropertyPath path = part.getProperty().getLeafProperty();
|
||||
|
||||
switch (part.shouldIgnoreCase()) {
|
||||
|
||||
case ALWAYS:
|
||||
if (part.getProperty().getType() != String.class) {
|
||||
throw new IllegalArgumentException(String.format("part %s must be of type String but was %s",
|
||||
part.getProperty(), part.getType()));
|
||||
if (path.getType() != String.class) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Part %s must be of type String but was %s", path, path.getType()));
|
||||
}
|
||||
// fall-through
|
||||
|
||||
case WHEN_POSSIBLE:
|
||||
|
||||
if (shouldNegateExpression) {
|
||||
criteria = criteria.not();
|
||||
}
|
||||
|
||||
return addAppropriateLikeRegexTo(criteria, part, parameters.nextConverted(property).toString());
|
||||
|
||||
case NEVER:
|
||||
@@ -365,8 +373,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
return (T) parameter;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type,
|
||||
parameter.getClass()));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Expected parameter type of %s but got %s!", type, parameter.getClass()));
|
||||
}
|
||||
|
||||
private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersistentProperty property) {
|
||||
@@ -384,25 +392,59 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
private String toLikeRegex(String source, Part part) {
|
||||
|
||||
Type type = part.getType();
|
||||
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, part);
|
||||
|
||||
switch (type) {
|
||||
case STARTING_WITH:
|
||||
source = "^" + source;
|
||||
regex = "^" + regex;
|
||||
break;
|
||||
case ENDING_WITH:
|
||||
source = source + "$";
|
||||
regex = regex + "$";
|
||||
break;
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
source = "*" + source + "*";
|
||||
regex = ".*" + regex + ".*";
|
||||
break;
|
||||
case SIMPLE_PROPERTY:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
source = "^" + source + "$";
|
||||
regex = "^" + regex + "$";
|
||||
default:
|
||||
}
|
||||
|
||||
return source.replaceAll("\\*", ".*");
|
||||
return regex;
|
||||
}
|
||||
|
||||
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Part qpart) {
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(Type.LIKE, qpart.getType())) {
|
||||
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source;
|
||||
}
|
||||
|
||||
if (source.equals("*")) {
|
||||
return ".*";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
boolean leadingWildcard = source.startsWith("*");
|
||||
boolean trailingWildcard = source.endsWith("*");
|
||||
|
||||
String valueToUse = source.substring(leadingWildcard ? 1 : 0,
|
||||
trailingWildcard ? source.length() - 1 : source.length());
|
||||
|
||||
if (PUNCTATION_PATTERN.matcher(valueToUse).find()) {
|
||||
valueToUse = Pattern.quote(valueToUse);
|
||||
}
|
||||
|
||||
if (leadingWildcard) {
|
||||
sb.append(".*");
|
||||
}
|
||||
sb.append(valueToUse);
|
||||
if (trailingWildcard) {
|
||||
sb.append(".*");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean isSpherical(MongoPersistentProperty property) {
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.mysema.query.apt.Configuration;
|
||||
import com.mysema.query.apt.DefaultConfiguration;
|
||||
|
||||
/**
|
||||
* Annotation processor to create Querydsl query types for QueryDsl annoated classes.
|
||||
* Annotation processor to create Querydsl query types for QueryDsl annotated classes.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -586,7 +586,7 @@ The comma delimited list of host:port entries to use for replica set/pairs.
|
||||
<xsd:attribute name="credentials" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The comma delimited list of username:password@database entries to use for authentication. Appending ?uri.authMechanism allows to specify the authentication challenge mechanism.
|
||||
The comma delimited list of username:password@database entries to use for authentication. Appending ?uri.authMechanism allows to specify the authentication challenge mechanism. If the credential you're trying to pass contains a comma itself, quote it with single quotes: '…'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
@@ -43,6 +43,9 @@ public class MongoCredentialPropertyEditorUnitTests {
|
||||
static final String USER_2_PWD = "warg";
|
||||
static final String USER_2_DB = "snow";
|
||||
|
||||
static final String USER_3_NAME = "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry";
|
||||
static final String USER_3_DB = "stark";
|
||||
|
||||
static final String USER_1_AUTH_STRING = USER_1_NAME + ":" + USER_1_PWD + "@" + USER_1_DB;
|
||||
static final String USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM = USER_1_AUTH_STRING + "?uri.authMechanism=PLAIN";
|
||||
|
||||
@@ -50,6 +53,9 @@ public class MongoCredentialPropertyEditorUnitTests {
|
||||
static final String USER_2_AUTH_STRING_WITH_MONGODB_CR_AUTH_MECHANISM = USER_2_AUTH_STRING
|
||||
+ "?uri.authMechanism=MONGODB-CR";
|
||||
|
||||
static final String USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM = "'" + USER_3_NAME + "@" + USER_3_DB
|
||||
+ "?uri.authMechanism=MONGODB-X509'";
|
||||
|
||||
static final MongoCredential USER_1_CREDENTIALS = MongoCredential.createCredential(USER_1_NAME, USER_1_DB,
|
||||
USER_1_PWD.toCharArray());
|
||||
static final MongoCredential USER_1_CREDENTIALS_PLAIN_AUTH = MongoCredential.createPlainCredential(USER_1_NAME,
|
||||
@@ -60,6 +66,8 @@ public class MongoCredentialPropertyEditorUnitTests {
|
||||
static final MongoCredential USER_2_CREDENTIALS_CR_AUTH = MongoCredential.createMongoCRCredential(USER_2_NAME,
|
||||
USER_2_DB, USER_2_PWD.toCharArray());
|
||||
|
||||
static final MongoCredential USER_3_CREDENTIALS_X509_AUTH = MongoCredential.createMongoX509Credential(USER_3_NAME);
|
||||
|
||||
MongoCredentialPropertyEditor editor;
|
||||
|
||||
@Before
|
||||
@@ -168,4 +176,75 @@ public class MongoCredentialPropertyEditorUnitTests {
|
||||
|
||||
assertThat((List<MongoCredential>) editor.getValue(), contains(USER_1_CREDENTIALS_PLAIN_AUTH, USER_2_CREDENTIALS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() {
|
||||
|
||||
editor.setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList("'" + USER_1_AUTH_STRING + "'", "'"
|
||||
+ USER_2_AUTH_STRING + "'")));
|
||||
|
||||
assertThat((List<MongoCredential>) editor.getValue(), contains(USER_1_CREDENTIALS, USER_2_CREDENTIALS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() {
|
||||
|
||||
editor.setAsText("'" + USER_1_AUTH_STRING + "'");
|
||||
|
||||
assertThat((List<MongoCredential>) editor.getValue(), contains(USER_1_CREDENTIALS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldReturnX509CredentialsCorrectly() {
|
||||
|
||||
editor.setAsText(USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM);
|
||||
|
||||
assertThat((List<MongoCredential>) editor.getValue(), contains(USER_3_CREDENTIALS_X509_AUTH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldReturnX509CredentialsCorrectlyWhenNoDbSpecified() {
|
||||
|
||||
editor.setAsText("tyrion?uri.authMechanism=MONGODB-X509");
|
||||
|
||||
assertThat((List<MongoCredential>) editor.getValue(), contains(MongoCredential.createMongoX509Credential("tyrion")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenNoDbSpecifiedForMongodbCR() {
|
||||
|
||||
editor.setAsText("tyrion?uri.authMechanism=MONGODB-CR");
|
||||
|
||||
editor.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1257
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenDbIsEmptyForMongodbCR() {
|
||||
|
||||
editor.setAsText("tyrion@?uri.authMechanism=MONGODB-CR");
|
||||
|
||||
editor.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2015 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,6 +17,7 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@@ -27,35 +28,37 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationUtils;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoDbUtils}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Randy Watler
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoDbUtilsUnitTests {
|
||||
|
||||
@Mock Mongo mongo;
|
||||
@Mock MongoClient mongoClientMock;
|
||||
@Mock DB dbMock;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
when(mongo.getDB(anyString())).then(new Answer<DB>() {
|
||||
public DB answer(InvocationOnMock invocation) throws Throwable {
|
||||
return mock(DB.class);
|
||||
}
|
||||
});
|
||||
when(mongo.getDB(anyString())).thenReturn(dbMock).thenReturn(mock(DB.class));
|
||||
when(mongoClientMock.getDB(anyString())).thenReturn(dbMock);
|
||||
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
}
|
||||
@@ -151,6 +154,38 @@ public class MongoDbUtilsUnitTests {
|
||||
assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1218
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void getDBDAuthenticateViaAuthDbWhenCalledWithMongoInstance() {
|
||||
|
||||
assumeThat(MongoClientVersion.isMongo3Driver(), is(false));
|
||||
|
||||
when(dbMock.getName()).thenReturn("db");
|
||||
|
||||
try {
|
||||
MongoDbUtils.getDB(mongo, "db", new UserCredentials("shallan", "davar"), "authdb");
|
||||
} catch (CannotGetMongoDbConnectionException e) {
|
||||
// need to catch that one since we cannot answer the reflective call sufficiently
|
||||
}
|
||||
|
||||
verify(mongo, times(1)).getDB("authdb");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1218
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void getDBDShouldSkipAuthenticationViaAuthDbWhenCalledWithMongoClientInstance() {
|
||||
|
||||
MongoDbUtils.getDB(mongoClientMock, "db", new UserCredentials("dalinar", "kholin"), "authdb");
|
||||
|
||||
verify(mongoClientMock, never()).getDB("authdb");
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate transaction rollback/commit completion protocol on managed transaction synchronizations which will unbind
|
||||
* managed transaction resources. Does not swallow exceptions for testing purposes.
|
||||
|
||||
@@ -73,8 +73,10 @@ 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.util.CloseableIterator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -187,9 +189,14 @@ public class MongoTemplateTests {
|
||||
template.dropCollection(DocumentWithCollection.class);
|
||||
template.dropCollection(DocumentWithCollectionOfSimpleType.class);
|
||||
template.dropCollection(DocumentWithMultipleCollections.class);
|
||||
template.dropCollection(DocumentWithNestedCollection.class);
|
||||
template.dropCollection(DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
template.dropCollection(DocumentWithNestedList.class);
|
||||
template.dropCollection(DocumentWithDBRefCollection.class);
|
||||
template.dropCollection(SomeContent.class);
|
||||
template.dropCollection(SomeTemplate.class);
|
||||
template.dropCollection(Address.class);
|
||||
template.dropCollection(DocumentWithCollectionOfSamples.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2204,6 +2211,243 @@ public class MongoTemplateTests {
|
||||
assertThat(retrieved.model.value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenWholeCollectionIsReplaced() {
|
||||
|
||||
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
|
||||
|
||||
Map<String, Model> entry = new HashMap<String, Model>();
|
||||
entry.put("key1", new ModelA("value1"));
|
||||
doc.models.add(entry);
|
||||
|
||||
template.save(doc);
|
||||
|
||||
entry.put("key2", new ModelA("value2"));
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("models", Collections.singletonList(entry));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithNestedCollection.class);
|
||||
|
||||
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
|
||||
|
||||
assertThat(retrieved, is(notNullValue()));
|
||||
assertThat(retrieved.id, is(doc.id));
|
||||
|
||||
assertThat(retrieved.models.get(0).entrySet(), hasSize(2));
|
||||
|
||||
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
|
||||
|
||||
assertThat(retrieved.models.get(0).get("key2"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get("key2").value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenFirstElementIsReplaced() {
|
||||
|
||||
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
|
||||
|
||||
Map<String, Model> entry = new HashMap<String, Model>();
|
||||
entry.put("key1", new ModelA("value1"));
|
||||
doc.models.add(entry);
|
||||
|
||||
template.save(doc);
|
||||
|
||||
entry.put("key2", new ModelA("value2"));
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("models.0", entry);
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithNestedCollection.class);
|
||||
|
||||
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
|
||||
|
||||
assertThat(retrieved, is(notNullValue()));
|
||||
assertThat(retrieved.id, is(doc.id));
|
||||
|
||||
assertThat(retrieved.models.get(0).entrySet(), hasSize(2));
|
||||
|
||||
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
|
||||
|
||||
assertThat(retrieved.models.get(0).get("key2"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get("key2").value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldAddTypeInformationOnDocumentWithNestedCollectionObjectInsertedAtSecondIndex() {
|
||||
|
||||
DocumentWithNestedCollection doc = new DocumentWithNestedCollection();
|
||||
|
||||
Map<String, Model> entry = new HashMap<String, Model>();
|
||||
entry.put("key1", new ModelA("value1"));
|
||||
doc.models.add(entry);
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("models.1", Collections.singletonMap("key2", new ModelA("value2")));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithNestedCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithNestedCollection.class);
|
||||
|
||||
DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class);
|
||||
|
||||
assertThat(retrieved, is(notNullValue()));
|
||||
assertThat(retrieved.id, is(doc.id));
|
||||
|
||||
assertThat(retrieved.models.get(0).entrySet(), hasSize(1));
|
||||
assertThat(retrieved.models.get(1).entrySet(), hasSize(1));
|
||||
|
||||
assertThat(retrieved.models.get(0).get("key1"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get("key1").value(), equalTo("value1"));
|
||||
|
||||
assertThat(retrieved.models.get(1).get("key2"), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(1).get("key2").value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingPositionedElement()
|
||||
throws Exception {
|
||||
|
||||
List<Model> models = new ArrayList<Model>();
|
||||
models.add(new ModelA("value1"));
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
|
||||
new DocumentWithCollection(models));
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("embeddedDocument.models.0", new ModelA("value2"));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
|
||||
DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
assertThat(retrieved, notNullValue());
|
||||
assertThat(retrieved.embeddedDocument.models, hasSize(1));
|
||||
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingSecondElement()
|
||||
throws Exception {
|
||||
|
||||
List<Model> models = new ArrayList<Model>();
|
||||
models.add(new ModelA("value1"));
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
|
||||
new DocumentWithCollection(models));
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("embeddedDocument.models.1", new ModelA("value2"));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
|
||||
DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
assertThat(retrieved, notNullValue());
|
||||
assertThat(retrieved.embeddedDocument.models, hasSize(2));
|
||||
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value1"));
|
||||
assertThat(retrieved.embeddedDocument.models.get(1).value(), is("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenRewriting()
|
||||
throws Exception {
|
||||
|
||||
List<Model> models = Arrays.<Model> asList(new ModelA("value1"));
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection(
|
||||
new DocumentWithCollection(models));
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
Update update = Update.update("embeddedDocument",
|
||||
new DocumentWithCollection(Arrays.<Model> asList(new ModelA("value2"))));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class), notNullValue());
|
||||
|
||||
template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query,
|
||||
DocumentWithEmbeddedDocumentWithCollection.class);
|
||||
|
||||
assertThat(retrieved, notNullValue());
|
||||
assertThat(retrieved.embeddedDocument.models, hasSize(1));
|
||||
assertThat(retrieved.embeddedDocument.models.get(0).value(), is("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnDocumentWithNestedLists() {
|
||||
|
||||
DocumentWithNestedList doc = new DocumentWithNestedList();
|
||||
|
||||
List<Model> entry = new ArrayList<Model>();
|
||||
entry.add(new ModelA("value1"));
|
||||
doc.models.add(entry);
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithNestedList.class), notNullValue());
|
||||
|
||||
Update update = Update.update("models.0.1", new ModelA("value2"));
|
||||
|
||||
template.findAndModify(query, update, DocumentWithNestedList.class);
|
||||
|
||||
DocumentWithNestedList retrieved = template.findOne(query, DocumentWithNestedList.class);
|
||||
|
||||
assertThat(retrieved, is(notNullValue()));
|
||||
assertThat(retrieved.id, is(doc.id));
|
||||
|
||||
assertThat(retrieved.models.get(0), hasSize(2));
|
||||
|
||||
assertThat(retrieved.models.get(0).get(0), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get(0).value(), equalTo("value1"));
|
||||
|
||||
assertThat(retrieved.models.get(0).get(1), instanceOf(ModelA.class));
|
||||
assertThat(retrieved.models.get(0).get(1).value(), equalTo("value2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-407
|
||||
*/
|
||||
@@ -2609,6 +2853,33 @@ public class MongoTemplateTests {
|
||||
assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values, hasSize(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyAddToSetWithEachShouldNotAddDuplicatesNorTypeHintForSimpleDocuments() {
|
||||
|
||||
DocumentWithCollectionOfSamples doc = new DocumentWithCollectionOfSamples();
|
||||
doc.samples = Arrays.asList(new Sample(null, "sample1"));
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query query = query(where("id").is(doc.id));
|
||||
|
||||
assertThat(template.findOne(query, DocumentWithCollectionOfSamples.class), notNullValue());
|
||||
|
||||
Update update = new Update().addToSet("samples").each(new Sample(null, "sample2"), new Sample(null, "sample1"));
|
||||
|
||||
template.findAndModify(query, update, DocumentWithCollectionOfSamples.class);
|
||||
|
||||
DocumentWithCollectionOfSamples retrieved = template.findOne(query, DocumentWithCollectionOfSamples.class);
|
||||
|
||||
assertThat(retrieved, notNullValue());
|
||||
assertThat(retrieved.samples, hasSize(2));
|
||||
assertThat(retrieved.samples.get(0).field, is("sample1"));
|
||||
assertThat(retrieved.samples.get(1).field, is("sample2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-888
|
||||
*/
|
||||
@@ -2723,6 +2994,61 @@ public class MongoTemplateTests {
|
||||
assertThat(template.findAll(DBObject.class, "collection"), hasSize(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1207
|
||||
*/
|
||||
@Test
|
||||
public void ignoresNullElementsForInsertAll() {
|
||||
|
||||
Address newYork = new Address("NY", "New York");
|
||||
Address washington = new Address("DC", "Washington");
|
||||
|
||||
template.insertAll(Arrays.asList(newYork, null, washington));
|
||||
|
||||
List<Address> result = template.findAll(Address.class);
|
||||
|
||||
assertThat(result, hasSize(2));
|
||||
assertThat(result, hasItems(newYork, washington));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1208
|
||||
*/
|
||||
@Test
|
||||
public void takesSortIntoAccountWhenStreaming() {
|
||||
|
||||
Person youngestPerson = new Person("John", 20);
|
||||
Person oldestPerson = new Person("Jane", 42);
|
||||
|
||||
template.insertAll(Arrays.asList(oldestPerson, youngestPerson));
|
||||
|
||||
Query q = new Query();
|
||||
q.with(new Sort(Direction.ASC, "age"));
|
||||
CloseableIterator<Person> stream = template.stream(q, Person.class);
|
||||
|
||||
assertThat(stream.next().getAge(), is(youngestPerson.getAge()));
|
||||
assertThat(stream.next().getAge(), is(oldestPerson.getAge()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1208
|
||||
*/
|
||||
@Test
|
||||
public void takesLimitIntoAccountWhenStreaming() {
|
||||
|
||||
Person youngestPerson = new Person("John", 20);
|
||||
Person oldestPerson = new Person("Jane", 42);
|
||||
|
||||
template.insertAll(Arrays.asList(oldestPerson, youngestPerson));
|
||||
|
||||
Query q = new Query();
|
||||
q.with(new PageRequest(0, 1, new Sort(Direction.ASC, "age")));
|
||||
CloseableIterator<Person> stream = template.stream(q, Person.class);
|
||||
|
||||
assertThat(stream.next().getAge(), is(youngestPerson.getAge()));
|
||||
assertThat(stream.hasNext(), is(false));
|
||||
}
|
||||
|
||||
static class DoucmentWithNamedIdField {
|
||||
|
||||
@Id String someIdKey;
|
||||
@@ -2798,12 +3124,36 @@ public class MongoTemplateTests {
|
||||
List<String> values;
|
||||
}
|
||||
|
||||
static class DocumentWithCollectionOfSamples {
|
||||
@Id String id;
|
||||
List<Sample> samples;
|
||||
}
|
||||
|
||||
static class DocumentWithMultipleCollections {
|
||||
@Id String id;
|
||||
List<String> string1;
|
||||
List<String> string2;
|
||||
}
|
||||
|
||||
static class DocumentWithNestedCollection {
|
||||
@Id String id;
|
||||
List<Map<String, Model>> models = new ArrayList<Map<String, Model>>();
|
||||
}
|
||||
|
||||
static class DocumentWithNestedList {
|
||||
@Id String id;
|
||||
List<List<Model>> models = new ArrayList<List<Model>>();
|
||||
}
|
||||
|
||||
static class DocumentWithEmbeddedDocumentWithCollection {
|
||||
@Id String id;
|
||||
DocumentWithCollection embeddedDocument;
|
||||
|
||||
DocumentWithEmbeddedDocumentWithCollection(DocumentWithCollection embeddedDocument) {
|
||||
this.embeddedDocument = embeddedDocument;
|
||||
}
|
||||
}
|
||||
|
||||
static interface Model {
|
||||
String value();
|
||||
|
||||
@@ -2909,6 +3259,41 @@ public class MongoTemplateTests {
|
||||
|
||||
String state;
|
||||
String city;
|
||||
|
||||
Address() {}
|
||||
|
||||
Address(String state, String city) {
|
||||
this.state = state;
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Address that = (Address) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.city, that.city) && //
|
||||
ObjectUtils.nullSafeEquals(this.state, that.state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(this.city);
|
||||
result += 31 * ObjectUtils.nullSafeHashCode(this.state);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class VersionedPerson {
|
||||
|
||||
@@ -43,7 +43,9 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
@@ -52,18 +54,21 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCre
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.CommandResult;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.ReadPreference;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoTemplate}.
|
||||
@@ -353,6 +358,70 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
assertThat(captor.getValue(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1166
|
||||
*/
|
||||
@Test
|
||||
public void aggregateShouldHonorReadPreferenceWhenSet() {
|
||||
|
||||
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(
|
||||
mock(CommandResult.class));
|
||||
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
|
||||
template.setReadPreference(ReadPreference.secondary());
|
||||
|
||||
template.aggregate(Aggregation.newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class);
|
||||
|
||||
verify(this.db, times(1)).command(Mockito.any(DBObject.class), eq(ReadPreference.secondary()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1166
|
||||
*/
|
||||
@Test
|
||||
public void aggregateShouldIgnoreReadPreferenceWhenNotSet() {
|
||||
|
||||
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(
|
||||
mock(CommandResult.class));
|
||||
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
|
||||
|
||||
template.aggregate(Aggregation.newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class);
|
||||
|
||||
verify(this.db, times(1)).command(Mockito.any(DBObject.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1166
|
||||
*/
|
||||
@Test
|
||||
public void geoNearShouldHonorReadPreferenceWhenSet() {
|
||||
|
||||
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(
|
||||
mock(CommandResult.class));
|
||||
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
|
||||
template.setReadPreference(ReadPreference.secondary());
|
||||
|
||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
||||
template.geoNear(query, Wrapper.class);
|
||||
|
||||
verify(this.db, times(1)).command(Mockito.any(DBObject.class), eq(ReadPreference.secondary()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1166
|
||||
*/
|
||||
@Test
|
||||
public void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
|
||||
|
||||
when(db.command(Mockito.any(DBObject.class), Mockito.any(ReadPreference.class))).thenReturn(
|
||||
mock(CommandResult.class));
|
||||
when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
|
||||
|
||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
||||
template.geoNear(query, Wrapper.class);
|
||||
|
||||
verify(this.db, times(1)).command(Mockito.any(DBObject.class));
|
||||
}
|
||||
|
||||
class AutogenerateableId {
|
||||
|
||||
@Id BigInteger id;
|
||||
|
||||
@@ -22,10 +22,13 @@ import static org.springframework.test.util.ReflectionTestUtils.*;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
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.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
|
||||
@@ -43,6 +46,7 @@ import com.mongodb.MongoURI;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SimpleMongoDbFactoryUnitTests {
|
||||
|
||||
public @Rule ExpectedException expectedException = ExpectedException.none();
|
||||
@Mock Mongo mongo;
|
||||
|
||||
/**
|
||||
@@ -115,6 +119,46 @@ public class SimpleMongoDbFactoryUnitTests {
|
||||
assertThat(getField(factory, "authenticationDatabaseName").toString(), is("FooBar"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1260
|
||||
*/
|
||||
@Test
|
||||
public void rejectsMongoClientWithUserCredentials() {
|
||||
|
||||
expectedException.expect(InvalidDataAccessApiUsageException.class);
|
||||
expectedException.expectMessage("use 'MongoCredential' for 'MongoClient'");
|
||||
|
||||
new SimpleMongoDbFactory(mock(MongoClient.class), "cairhienin", new UserCredentials("moiraine", "sedai"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1260
|
||||
*/
|
||||
@Test
|
||||
public void rejectsMongoClientWithUserCredentialsAndAuthDb() {
|
||||
|
||||
expectedException.expect(InvalidDataAccessApiUsageException.class);
|
||||
expectedException.expectMessage("use 'MongoCredential' for 'MongoClient'");
|
||||
|
||||
new SimpleMongoDbFactory(mock(MongoClient.class), "malkieri", new UserCredentials("lan", "mandragoran"), "authdb");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1260
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotRejectMongoClientWithNoCredentials() {
|
||||
new SimpleMongoDbFactory(mock(MongoClient.class), "andoran", UserCredentials.NO_CREDENTIALS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1260
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotRejectMongoClientWithEmptyUserCredentials() {
|
||||
new SimpleMongoDbFactory(mock(MongoClient.class), "shangtai", new UserCredentials("", ""));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void rejectsDatabaseName(String databaseName) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 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 org.junit.rules.ExpectedException;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
@@ -37,6 +38,7 @@ import com.mongodb.DBObject;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class AggregationUnitTests {
|
||||
|
||||
@@ -283,6 +285,40 @@ public class AggregationUnitTests {
|
||||
is((DBObject) new BasicDBObject("_id", "$someKey").append("doc", new BasicDBObject("$first", "$$ROOT"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1254
|
||||
*/
|
||||
@Test
|
||||
public void shouldExposeAliasedFieldnameForProjectionsIncludingOperationsDownThePipeline() {
|
||||
|
||||
DBObject agg = Aggregation.newAggregation(//
|
||||
project("date") //
|
||||
.and("tags").minus(10).as("tags_count")//
|
||||
, group("date")//
|
||||
.sum("tags_count").as("count")//
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject group = extractPipelineElement(agg, 1, "$group");
|
||||
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1254
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseAliasedFieldnameForProjectionsIncludingOperationsDownThePipelineWhenUsingSpEL() {
|
||||
|
||||
DBObject agg = Aggregation.newAggregation(//
|
||||
project("date") //
|
||||
.andExpression("tags-10")//
|
||||
, group("date")//
|
||||
.sum("tags_count").as("count")//
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject group = extractPipelineElement(agg, 1, "$group");
|
||||
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
|
||||
}
|
||||
|
||||
private DBObject extractPipelineElement(DBObject agg, int index, String operation) {
|
||||
|
||||
List<DBObject> pipeline = (List<DBObject>) agg.get("pipeline");
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2015 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.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
|
||||
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.mongodb.MongoDbFactory;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReflectiveDBRefResolver}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ReflectiveDBRefResolverUnitTests {
|
||||
|
||||
@Mock MongoDbFactory dbFactoryMock;
|
||||
@Mock DBRef dbRefMock;
|
||||
@Mock DB dbMock;
|
||||
@Mock DBCollection collectionMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
when(dbRefMock.getCollectionName()).thenReturn("collection-1");
|
||||
when(dbRefMock.getId()).thenReturn("id-1");
|
||||
when(dbFactoryMock.getDb()).thenReturn(dbMock);
|
||||
when(dbMock.getCollection(eq("collection-1"))).thenReturn(collectionMock);
|
||||
when(collectionMock.findOne(eq("id-1"))).thenReturn(new BasicDBObject("_id", "id-1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1193
|
||||
*/
|
||||
@Test
|
||||
public void fetchShouldNotLookUpDbWhenUsingDriverVersion2() {
|
||||
|
||||
assumeThat(isMongo3Driver(), is(false));
|
||||
|
||||
ReflectiveDBRefResolver.fetch(dbFactoryMock, dbRefMock);
|
||||
|
||||
verify(dbFactoryMock, never()).getDb();
|
||||
verify(dbFactoryMock, never()).getDb(anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1193
|
||||
*/
|
||||
@Test
|
||||
public void fetchShouldUseDbToResolveDbRefWhenUsingDriverVersion3() {
|
||||
|
||||
assumeThat(isMongo3Driver(), is(true));
|
||||
|
||||
assertThat(ReflectiveDBRefResolver.fetch(dbFactoryMock, dbRefMock), notNullValue());
|
||||
verify(dbFactoryMock, times(1)).getDb();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1193
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void fetchShouldThrowExceptionWhenDbFactoryIsNullUsingDriverVersion3() {
|
||||
|
||||
assumeThat(isMongo3Driver(), is(true));
|
||||
|
||||
ReflectiveDBRefResolver.fetch(null, dbRefMock);
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,13 @@ import static org.hamcrest.collection.IsMapContaining.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.collection.IsIterableContainingInOrder;
|
||||
@@ -39,6 +43,9 @@ import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.Allocation;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.AllocationToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests.ClassWithEnum.StringToAllocationConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
@@ -428,8 +435,8 @@ public class UpdateMapperUnitTests {
|
||||
public void rendersNestedDbRefCorrectly() {
|
||||
|
||||
Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2");
|
||||
DBObject mappedObject = mapper
|
||||
.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Wrapper.class));
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(Wrapper.class));
|
||||
|
||||
DBObject pullClause = getAsDBObject(mappedObject, "$pull");
|
||||
assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true));
|
||||
@@ -524,7 +531,6 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(((DBObject) updateValue).get("_class").toString(),
|
||||
equalTo("org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests$ModelImpl"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,6 +601,274 @@ public class UpdateMapperUnitTests {
|
||||
assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void mappingEachOperatorShouldNotAddTypeInfoForNonInterfaceNonAbstractTypes() {
|
||||
|
||||
Update update = new Update().addToSet("nestedDocs").each(new NestedDocument("nested-1"),
|
||||
new NestedDocument("nested-2"));
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithNestedCollection.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().notContaining("$addToSet.nestedDocs.$each.[0]._class"));
|
||||
assertThat(mappedUpdate, isBsonObject().notContaining("$addToSet.nestedDocs.$each.[1]._class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void mappingEachOperatorShouldAddTypeHintForInterfaceTypes() {
|
||||
|
||||
Update update = new Update().addToSet("models").each(new ModelImpl(1), new ModelImpl(2));
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ListModelWrapper.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$addToSet.models.$each.[0]._class", ModelImpl.class.getName()));
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$addToSet.models.$each.[1]._class", ModelImpl.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void mappingEachOperatorShouldAddTypeHintForAbstractTypes() {
|
||||
|
||||
Update update = new Update().addToSet("list").each(new ConcreteChildClass("foo", "one"),
|
||||
new ConcreteChildClass("bar", "two"));
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ParentClass.class));
|
||||
|
||||
assertThat(mappedUpdate,
|
||||
isBsonObject().containing("$addToSet.aliased.$each.[0]._class", ConcreteChildClass.class.getName()));
|
||||
assertThat(mappedUpdate,
|
||||
isBsonObject().containing("$addToSet.aliased.$each.[1]._class", ConcreteChildClass.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldOnlyRemoveTypeHintFromTopLevelTypeInCaseOfNestedDocument() {
|
||||
|
||||
WrapperAroundInterfaceType wait = new WrapperAroundInterfaceType();
|
||||
wait.interfaceType = new ModelImpl(1);
|
||||
|
||||
Update update = new Update().addToSet("listHoldingConcretyTypeWithInterfaceTypeAttribute").each(wait);
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute.class));
|
||||
|
||||
assertThat(mappedUpdate,
|
||||
isBsonObject().notContaining("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class"));
|
||||
assertThat(mappedUpdate,
|
||||
isBsonObject().containing(
|
||||
"$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class",
|
||||
ModelImpl.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1210
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyParentType() {
|
||||
|
||||
ListModelWrapper lmw = new ListModelWrapper();
|
||||
lmw.models = Collections.<Model> singletonList(new ModelImpl(1));
|
||||
|
||||
Update update = new Update().set("concreteTypeWithListAttributeOfInterfaceType", lmw);
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class"));
|
||||
assertThat(
|
||||
mappedUpdate,
|
||||
isBsonObject().containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class",
|
||||
ModelImpl.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1236
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldRetainTypeInformationForObjectValues() {
|
||||
|
||||
Update update = new Update().set("value", new NestedDocument("kaladin"));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObject.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.value.name", "kaladin"));
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.value._class", NestedDocument.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1236
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldNotRetainTypeInformationForConcreteValues() {
|
||||
|
||||
Update update = new Update().set("concreteValue", new NestedDocument("shallan"));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObject.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteValue.name", "shallan"));
|
||||
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteValue._class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1236
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldRetainTypeInformationForObjectValuesWithAlias() {
|
||||
|
||||
Update update = new Update().set("value", new NestedDocument("adolin"));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithAliasedObject.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value.name", "adolin"));
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value._class", NestedDocument.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1236
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() {
|
||||
|
||||
Map<Object, Object> map = Collections.<Object, Object> singletonMap("szeth", new NestedDocument("son-son-vallano"));
|
||||
|
||||
Update update = new Update().set("map", map);
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObjectMap.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth.name", "son-son-vallano"));
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth._class", NestedDocument.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1236
|
||||
*/
|
||||
@Test
|
||||
public void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() {
|
||||
|
||||
Map<Object, NestedDocument> map = Collections.<Object, NestedDocument> singletonMap("jasnah", new NestedDocument(
|
||||
"kholin"));
|
||||
|
||||
Update update = new Update().set("concreteMap", map);
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObjectMap.class));
|
||||
|
||||
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteMap.jasnah.name", "kholin"));
|
||||
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteMap.jasnah._class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1250
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mapsUpdateWithBothReadingAndWritingConverterRegistered() {
|
||||
|
||||
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("allocation", Allocation.AVAILABLE);
|
||||
DBObject result = mapper.getMappedObject(update.getUpdateObject(),
|
||||
mappingContext.getPersistentEntity(ClassWithEnum.class));
|
||||
|
||||
assertThat(result, isBsonObject().containing("$set.allocation", Allocation.AVAILABLE.code));
|
||||
}
|
||||
|
||||
/**
|
||||
* see DATAMONGO-1251
|
||||
*/
|
||||
@Test
|
||||
public void mapsNullValueCorrectlyForSimpleTypes() {
|
||||
|
||||
Update update = new Update().set("value", null);
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ConcreteChildClass.class));
|
||||
|
||||
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
|
||||
assertThat($set.containsField("value"), is(true));
|
||||
assertThat($set.get("value"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* see DATAMONGO-1251
|
||||
*/
|
||||
@Test
|
||||
public void mapsNullValueCorrectlyForJava8Date() {
|
||||
|
||||
Update update = new Update().set("date", null);
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ClassWithJava8Date.class));
|
||||
|
||||
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
|
||||
assertThat($set.containsField("date"), is(true));
|
||||
assertThat($set.get("value"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* see DATAMONGO-1251
|
||||
*/
|
||||
@Test
|
||||
public void mapsNullValueCorrectlyForCollectionTypes() {
|
||||
|
||||
Update update = new Update().set("values", null);
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ListModel.class));
|
||||
|
||||
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
|
||||
assertThat($set.containsField("values"), is(true));
|
||||
assertThat($set.get("value"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* see DATAMONGO-1251
|
||||
*/
|
||||
@Test
|
||||
public void mapsNullValueCorrectlyForPropertyOfNestedDocument() {
|
||||
|
||||
Update update = new Update().set("concreteValue.name", null);
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(EntityWithObject.class));
|
||||
|
||||
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$set");
|
||||
assertThat($set.containsField("concreteValue.name"), is(true));
|
||||
assertThat($set.get("concreteValue.name"), nullValue());
|
||||
}
|
||||
|
||||
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
|
||||
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
|
||||
}
|
||||
|
||||
static class DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute {
|
||||
List<WrapperAroundInterfaceType> listHoldingConcretyTypeWithInterfaceTypeAttribute;
|
||||
}
|
||||
|
||||
static class WrapperAroundInterfaceType {
|
||||
Model interfaceType;
|
||||
}
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface")
|
||||
static interface DocumentWithReferenceToInterface {
|
||||
|
||||
@@ -631,7 +905,7 @@ public class UpdateMapperUnitTests {
|
||||
|
||||
private @Id String id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef //
|
||||
private InterfaceDocumentDefinitionWithoutId referencedDocument;
|
||||
|
||||
public String getId() {
|
||||
@@ -692,10 +966,10 @@ public class UpdateMapperUnitTests {
|
||||
|
||||
String id;
|
||||
|
||||
@Field("aliased")//
|
||||
@Field("aliased") //
|
||||
List<? extends AbstractChildClass> list;
|
||||
|
||||
@Field//
|
||||
@Field //
|
||||
List<Model> listOfInterface;
|
||||
|
||||
public ParentClass(String id, List<? extends AbstractChildClass> list) {
|
||||
@@ -728,6 +1002,10 @@ public class UpdateMapperUnitTests {
|
||||
|
||||
static class DomainEntity {
|
||||
List<NestedEntity> collectionOfNestedEntities;
|
||||
|
||||
public List<NestedEntity> getCollectionOfNestedEntities() {
|
||||
return collectionOfNestedEntities;
|
||||
}
|
||||
}
|
||||
|
||||
static class NestedEntity {
|
||||
@@ -753,10 +1031,10 @@ public class UpdateMapperUnitTests {
|
||||
|
||||
@Id public String id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef //
|
||||
public List<Entity> dbRefAnnotatedList;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef //
|
||||
public Entity dbRefProperty;
|
||||
}
|
||||
|
||||
@@ -770,4 +1048,87 @@ public class UpdateMapperUnitTests {
|
||||
|
||||
@Field("mapped") DocumentWithDBRefCollection nested;
|
||||
}
|
||||
|
||||
static class DocumentWithNestedCollection {
|
||||
List<NestedDocument> nestedDocs;
|
||||
}
|
||||
|
||||
static class NestedDocument {
|
||||
|
||||
String name;
|
||||
|
||||
public NestedDocument(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
static class EntityWithObject {
|
||||
|
||||
Object value;
|
||||
NestedDocument concreteValue;
|
||||
}
|
||||
|
||||
static class EntityWithAliasedObject {
|
||||
|
||||
@Field("renamed-value") Object value;
|
||||
}
|
||||
|
||||
static class EntityWithObjectMap {
|
||||
|
||||
Map<Object, Object> map;
|
||||
Map<Object, NestedDocument> concreteMap;
|
||||
}
|
||||
|
||||
static class ClassWithEnum {
|
||||
|
||||
Allocation allocation;
|
||||
|
||||
static enum Allocation {
|
||||
|
||||
AVAILABLE("V"), ALLOCATED("A");
|
||||
|
||||
String code;
|
||||
|
||||
private Allocation(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static Allocation of(String code) {
|
||||
|
||||
for (Allocation value : values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
static enum AllocationToStringConverter implements Converter<Allocation, String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Allocation source) {
|
||||
return source.code;
|
||||
}
|
||||
}
|
||||
|
||||
static enum StringToAllocationConverter implements Converter<String, Allocation> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Allocation convert(String source) {
|
||||
return Allocation.of(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassWithJava8Date {
|
||||
|
||||
LocalDate date;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,16 +18,20 @@ package org.springframework.data.mongodb.core.index;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.test.util.CleanMongoDB;
|
||||
import org.springframework.data.mongodb.test.util.MongoVersionRule;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -38,23 +42,22 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MongoPersistentEntityIndexCreatorIntegrationTests {
|
||||
|
||||
public static @ClassRule MongoVersionRule version = MongoVersionRule.atLeast(new Version(2, 6));
|
||||
static final String SAMPLE_TYPE_COLLECTION_NAME = "sampleEntity";
|
||||
static final String RECURSIVE_TYPE_COLLECTION_NAME = "recursiveGenericTypes";
|
||||
|
||||
public static @ClassRule RuleChain rules = RuleChain.outerRule(MongoVersionRule.atLeast(new Version(2, 6))).around(
|
||||
CleanMongoDB.indexes(Arrays.asList(SAMPLE_TYPE_COLLECTION_NAME, RECURSIVE_TYPE_COLLECTION_NAME)));
|
||||
|
||||
@Autowired @Qualifier("mongo1") MongoOperations templateOne;
|
||||
|
||||
@Autowired @Qualifier("mongo2") MongoOperations templateTwo;
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
templateOne.dropCollection(SampleEntity.class);
|
||||
templateTwo.dropCollection(SampleEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsIndexForConfiguredMappingContextOnly() {
|
||||
|
||||
@@ -62,7 +65,42 @@ public class MongoPersistentEntityIndexCreatorIntegrationTests {
|
||||
assertThat(indexInfo, hasSize(greaterThan(0)));
|
||||
assertThat(indexInfo, Matchers.<IndexInfo> hasItem(hasProperty("name", is("prop"))));
|
||||
|
||||
indexInfo = templateTwo.indexOps("sampleEntity").getIndexInfo();
|
||||
indexInfo = templateTwo.indexOps(SAMPLE_TYPE_COLLECTION_NAME).getIndexInfo();
|
||||
assertThat(indexInfo, hasSize(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1202
|
||||
*/
|
||||
@Test
|
||||
public void shouldHonorIndexedPropertiesWithRecursiveMappings() {
|
||||
|
||||
List<IndexInfo> indexInfo = templateOne.indexOps(RecursiveConcreteType.class).getIndexInfo();
|
||||
|
||||
assertThat(indexInfo, hasSize(greaterThan(0)));
|
||||
assertThat(indexInfo, Matchers.<IndexInfo> hasItem(hasProperty("name", is("firstName"))));
|
||||
}
|
||||
|
||||
@Document(collection = RECURSIVE_TYPE_COLLECTION_NAME)
|
||||
static abstract class RecursiveGenericType<RGT extends RecursiveGenericType<RGT>> {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef RGT referrer;
|
||||
|
||||
@Indexed String firstName;
|
||||
|
||||
public RecursiveGenericType(Long id, String firstName, RGT referrer) {
|
||||
this.firstName = firstName;
|
||||
this.id = id;
|
||||
this.referrer = referrer;
|
||||
}
|
||||
}
|
||||
|
||||
static class RecursiveConcreteType extends RecursiveGenericType<RecursiveConcreteType> {
|
||||
|
||||
public RecursiveConcreteType(Long id, String firstName, RecursiveConcreteType referrer) {
|
||||
super(id, firstName, referrer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,89 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
verify(propertyMock, never()).getActualType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1157
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test(expected = MappingException.class)
|
||||
public void verifyShouldThrowErrorForLazyDBRefOnFinalClass() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
|
||||
org.springframework.data.mongodb.core.mapping.DBRef.class);
|
||||
when(propertyMock.isDbReference()).thenReturn(true);
|
||||
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
|
||||
when(dbRefMock.lazy()).thenReturn(true);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) Class.class);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1157
|
||||
*/
|
||||
@Test(expected = MappingException.class)
|
||||
public void verifyShouldThrowErrorForLazyDBRefArray() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
|
||||
org.springframework.data.mongodb.core.mapping.DBRef.class);
|
||||
when(propertyMock.isDbReference()).thenReturn(true);
|
||||
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
|
||||
when(dbRefMock.lazy()).thenReturn(true);
|
||||
when(propertyMock.isArray()).thenReturn(true);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1157
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void verifyShouldPassForLazyDBRefOnNonArrayNonFinalClass() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
|
||||
org.springframework.data.mongodb.core.mapping.DBRef.class);
|
||||
when(propertyMock.isDbReference()).thenReturn(true);
|
||||
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
|
||||
when(dbRefMock.lazy()).thenReturn(true);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) Object.class);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
|
||||
verify(propertyMock, times(1)).isDbReference();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1157
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void verifyShouldPassForNonLazyDBRefOnFinalClass() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock(
|
||||
org.springframework.data.mongodb.core.mapping.DBRef.class);
|
||||
when(propertyMock.isDbReference()).thenReturn(true);
|
||||
when(propertyMock.getDBRef()).thenReturn(dbRefMock);
|
||||
when(dbRefMock.lazy()).thenReturn(false);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) Class.class);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
|
||||
verify(dbRefMock, times(1)).lazy();
|
||||
}
|
||||
|
||||
@Document(collection = "contacts")
|
||||
class Contact {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -16,17 +16,23 @@
|
||||
package org.springframework.data.mongodb.core.mapping.event;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
|
||||
|
||||
public class PersonBeforeSaveListener implements ApplicationListener<BeforeSaveEvent<PersonPojoStringId>> {
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
public final ArrayList<ApplicationEvent> seenEvents = new ArrayList<ApplicationEvent>();
|
||||
public class PersonBeforeSaveListener extends AbstractMongoEventListener<PersonPojoStringId> {
|
||||
|
||||
public void onApplicationEvent(BeforeSaveEvent<PersonPojoStringId> event) {
|
||||
this.seenEvents.add(event);
|
||||
public final List<ApplicationEvent> seenEvents = new ArrayList<ApplicationEvent>();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public void onBeforeSave(PersonPojoStringId source, DBObject dbo) {
|
||||
seenEvents.add(new BeforeSaveEvent<PersonPojoStringId>(source, dbo));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
/**
|
||||
* Sample contactt domain class.
|
||||
* Sample contact domain class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -553,6 +553,114 @@ public class MongoQueryCreatorUnitTests {
|
||||
assertThat(query, is(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1229
|
||||
*/
|
||||
@Test
|
||||
public void appliesIgnoreCaseToLeafProperty() {
|
||||
|
||||
PartTree tree = new PartTree("findByAddressStreetIgnoreCase", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "Street");
|
||||
|
||||
assertThat(new MongoQueryCreator(tree, accessor, context).createQuery(), is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void ignoreCaseShouldEscapeSource() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameIgnoreCase", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "con.flux+");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex("^\\Qcon.flux+\\E$", "i"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void ignoreCaseShouldEscapeSourceWhenUsedForStartingWith() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameStartingWithIgnoreCase", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "dawns.light+");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex("^\\Qdawns.light+\\E", "i"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void ignoreCaseShouldEscapeSourceWhenUsedForEndingWith() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameEndingWithIgnoreCase", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "new.ton+");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex("\\Qnew.ton+\\E$", "i"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void likeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameLike", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex(".*\\Qfire.fight+\\E.*"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void likeShouldEscapeSourceWhenUsedWithLeadingWildcard() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameLike", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex(".*\\Qsteel.heart+\\E"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void likeShouldEscapeSourceWhenUsedWithTrailingWildcard() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameLike", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "cala.mity+*");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
assertThat(query, is(query(where("username").regex("\\Qcala.mity+\\E.*"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1232
|
||||
*/
|
||||
@Test
|
||||
public void likeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameLike", User.class);
|
||||
ConvertingParameterAccessor accessor = getAccessor(converter, "*");
|
||||
|
||||
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
|
||||
assertThat(query, is(query(where("username").regex(".*"))));
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
|
||||
|
||||
@@ -90,6 +90,10 @@ public class IsBsonObject<T extends BSONObject> extends TypeSafeMatcher<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (o != null && expectation.not) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ include::preface.adoc[]
|
||||
|
||||
:leveloffset: +1
|
||||
include::new-features.adoc[]
|
||||
include::{spring-data-commons-docs}/dependencies.adoc[]
|
||||
include::{spring-data-commons-docs}/repositories.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
|
||||
@@ -2013,7 +2013,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`s into `Long`s 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 at the converter implementation.
|
||||
|
||||
[[mongo-template.index-and-collections]]
|
||||
== Index and Collection management
|
||||
|
||||
@@ -1,6 +1,94 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.7.2.RELEASE (2015-07-28)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1261 - Release 1.7.2 (Fowler).
|
||||
* DATAMONGO-1260 - Prevent accidental authentication misconfiguration on SimpleMongoDbFactory.
|
||||
* DATAMONGO-1257 - mongo:mongo-client namespace does not support usernames with a comma.
|
||||
* DATAMONGO-1254 - Group after Project in aggregation uses incorrect field name.
|
||||
* DATAMONGO-1251 - update / findAndModify throws NullPointerException.
|
||||
* DATAMONGO-1250 - Custom converter implementation not used in updates.
|
||||
|
||||
|
||||
Changes in version 1.5.6.RELEASE (2015-07-01)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1246 - Release 1.5.6 (Dijkstra).
|
||||
* DATAMONGO-1234 - Fix typos in JavaDoc.
|
||||
* DATAMONGO-1232 - IgnoreCase should escape queries.
|
||||
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
|
||||
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
|
||||
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
|
||||
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
|
||||
* DATAMONGO-1180 - Incorrect exception message creation in PartTreeMongoQuery.
|
||||
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
|
||||
* DATAMONGO-1155 - Upgrade mongo-next build profiles to Java driver version 2.13.0.
|
||||
|
||||
|
||||
Changes in version 1.6.3.RELEASE (2015-07-01)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1247 - Release 1.6.3 (Evans).
|
||||
* DATAMONGO-1242 - Update mongo-java-driver to 3.0.2 in mongo3 profile.
|
||||
* DATAMONGO-1234 - Fix typos in JavaDoc.
|
||||
* DATAMONGO-1232 - IgnoreCase should escape queries.
|
||||
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
|
||||
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
|
||||
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
|
||||
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
|
||||
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
|
||||
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
|
||||
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
|
||||
* DATAMONGO-1180 - Incorrect exception message creation in PartTreeMongoQuery.
|
||||
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
|
||||
* DATAMONGO-1157 - Throw meaningful exception when @DbRef is used with unsupported types.
|
||||
* DATAMONGO-1155 - Upgrade mongo-next build profiles to Java driver version 2.13.0.
|
||||
* DATAMONGO-1153 - Fix documentation build.
|
||||
* DATAMONGO-1133 - Field aliasing is not honored in Aggregation operations.
|
||||
* DATAMONGO-1124 - Switch log level for cyclic reference index warnings from WARN to INFO.
|
||||
* DATAMONGO-1081 - Improve documentation on field mapping semantics.
|
||||
|
||||
|
||||
Changes in version 1.7.1.RELEASE (2015-06-30)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1248 - Release 1.7.1 (Fowler).
|
||||
* DATAMONGO-1242 - Update mongo-java-driver to 3.0.2 in mongo3 profile.
|
||||
* DATAMONGO-1234 - Fix typos in JavaDoc.
|
||||
* DATAMONGO-1232 - IgnoreCase should escape queries.
|
||||
* DATAMONGO-1229 - MongoQueryCreator incorrectly rejects ignoreCase on nested String path.
|
||||
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
|
||||
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
|
||||
* DATAMONGO-1216 - Authentication mechanism PLAIN changes to SCRAM-SHA-1.
|
||||
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
|
||||
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
|
||||
* DATAMONGO-1208 - MongoTemplate.stream(…) does not consider limit, order, sort etc.
|
||||
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
|
||||
* DATAMONGO-1202 - Indexed annotation problems under generics.
|
||||
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
|
||||
* DATAMONGO-1193 - Prevent unnecessary database lookups when resolving DBRefs on 2.x driver.
|
||||
* DATAMONGO-1166 - ReadPreference not used for Aggregations.
|
||||
* DATAMONGO-1157 - Throw meaningful exception when @DbRef is used with unsupported types.
|
||||
|
||||
|
||||
Changes in version 1.8.0.M1 (2015-06-02)
|
||||
----------------------------------------
|
||||
* DATAMONGO-1228 - Release 1.8 M1 (Gosling).
|
||||
* DATAMONGO-1224 - Assert Spring Framework 4.2 compatibility.
|
||||
* DATAMONGO-1221 - Remove relative reference to parent POM to make sure the right Spring version is picked up.
|
||||
* DATAMONGO-1218 - Deprecate non-MongoClient related configuration options in XML namespace.
|
||||
* DATAMONGO-1216 - Authentication mechanism PLAIN changes to SCRAM-SHA-1.
|
||||
* DATAMONGO-1213 - Include new section on Spring Data and Spring Framework dependencies in reference documentation.
|
||||
* DATAMONGO-1211 - Adapt API changes in Spring Data Commons to simplify custom repository base class registration.
|
||||
* DATAMONGO-1210 - Inconsistent property order of _class type hint breaks document equality.
|
||||
* DATAMONGO-1208 - MongoTemplate.stream(…) does not consider limit, order, sort etc.
|
||||
* DATAMONGO-1207 - MongoTemplate#doInsertAll throws NullPointerException when passed Collection contains a null item.
|
||||
* DATAMONGO-1202 - Indexed annotation problems under generics.
|
||||
* DATAMONGO-1196 - Upgrade build profiles after MongoDB 3.0 Java driver release.
|
||||
* DATAMONGO-1193 - Prevent unnecessary database lookups when resolving DBRefs on 2.x driver.
|
||||
* DATAMONGO-1192 - Switch back to Spring 4.1's CollectionFactory.
|
||||
* DATAMONGO-1134 - Add support for $geoIntersects.
|
||||
* DATAMONGO-990 - Add support for SpEL expressions in @Query.
|
||||
|
||||
|
||||
Changes in version 1.7.0.RELEASE (2015-03-23)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1189 - Release 1.7 GA.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.7 GA
|
||||
Spring Data MongoDB 1.7.2
|
||||
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