Compare commits
23 Commits
2.0.0.RC3
...
2.0.0.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5e8fa45c2 | ||
|
|
f5ad4e42f9 | ||
|
|
e6b7d2ffd0 | ||
|
|
5b24d3fd0b | ||
|
|
10f13c8f37 | ||
|
|
c05f8f056c | ||
|
|
dbd38a8e82 | ||
|
|
77b1f3cb37 | ||
|
|
5444ac39b5 | ||
|
|
cf476b9bc8 | ||
|
|
f28d47b01b | ||
|
|
5bf03cfa70 | ||
|
|
98e893636b | ||
|
|
4b552b051e | ||
|
|
1c295b62c6 | ||
|
|
0a8458a045 | ||
|
|
a3b9fb33ea | ||
|
|
3d651b72ad | ||
|
|
187c25bcc0 | ||
|
|
087482d82e | ||
|
|
e80d1df571 | ||
|
|
a9b1b640c0 | ||
|
|
b888864407 |
10
pom.xml
10
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,7 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -27,7 +27,7 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>2.0.0.RC3</springdata.commons>
|
||||
<springdata.commons>2.0.0.RELEASE</springdata.commons>
|
||||
<mongo>3.5.0</mongo>
|
||||
<mongo.reactivestreams>1.6.0</mongo.reactivestreams>
|
||||
<jmh.version>1.19</jmh.version>
|
||||
@@ -183,8 +183,8 @@
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
<id>spring-libs-release</id>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<properties>
|
||||
<jpa>2.1.1</jpa>
|
||||
<hibernate>5.2.1.Final</hibernate>
|
||||
<java-module-name>spring.data.mongodb.cross.store</java-module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -48,7 +49,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- reactive -->
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC3</version>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<objenesis>1.3</objenesis>
|
||||
<equalsverifier>1.5</equalsverifier>
|
||||
<equalsverifier>1.7.8</equalsverifier>
|
||||
<java-module-name>spring.data.mongodb</java-module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -137,6 +138,21 @@
|
||||
</dependency>
|
||||
|
||||
<!-- CDI -->
|
||||
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jcdi_2.0_spec</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
@@ -146,26 +162,19 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>el-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax-annotation-api}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans.test</groupId>
|
||||
<artifactId>cditest-owb</artifactId>
|
||||
<groupId>org.apache.openwebbeans</groupId>
|
||||
<artifactId>openwebbeans-se</artifactId>
|
||||
<version>${webbeans}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSR 303 Validation -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
* @author Maninder Singh
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface MongoOperations extends FluentMongoOperations {
|
||||
|
||||
@@ -850,7 +851,7 @@ public interface MongoOperations extends FluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
* <p/>
|
||||
* <p/>
|
||||
@@ -907,7 +908,7 @@ public interface MongoOperations extends FluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
@@ -924,7 +925,7 @@ public interface MongoOperations extends FluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">Spring's
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation">Spring's
|
||||
* Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
|
||||
@@ -24,8 +24,19 @@ import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -761,16 +772,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @see org.springframework.data.mongodb.core.MongoOperations#findOne(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> find(final Query query, Class<T> entityClass, String collectionName) {
|
||||
public <T> List<T> find(Query query, Class<T> entityClass, String collectionName) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(collectionName, "CollectionName must not be null!");
|
||||
Assert.notNull(entityClass, "EntityClass must not be null!");
|
||||
|
||||
if (query.getQueryObject().isEmpty() && query.getSortObject().isEmpty()) {
|
||||
return findAll(entityClass, collectionName);
|
||||
}
|
||||
|
||||
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new QueryCursorPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
* <p/>
|
||||
* <p/>
|
||||
@@ -673,7 +673,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
* <p/>
|
||||
* <p/>
|
||||
@@ -721,7 +721,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
@@ -739,7 +739,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">Spring's
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation">Spring's
|
||||
* Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
@@ -758,7 +758,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
@@ -776,7 +776,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">Spring's
|
||||
* http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation">Spring's
|
||||
* Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AggregationExpression} that renders a MongoDB Aggregation Framework expression from the AST of a
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html">SpEL
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions">SpEL
|
||||
* expression</a>. <br />
|
||||
* <br />
|
||||
* <strong>Samples:</strong> <br />
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
|
||||
* </code>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @see SpelExpressionTransformer
|
||||
* @since 1.10
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.Assert;
|
||||
* @author Gustavo de Geus
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Sergey Shcherbakov
|
||||
* @since 1.3
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/aggregation/group/">MongoDB Aggregation Framework: $group</a>
|
||||
*/
|
||||
@@ -155,6 +156,21 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return sum(reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $sum}-expression for the given
|
||||
* {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr must not be {@literal null}.
|
||||
* @return new instance of {@link GroupOperationBuilder}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException when {@code expr} is {@literal null}.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
public GroupOperationBuilder sum(AggregationExpression expr) {
|
||||
|
||||
Assert.notNull(expr, "Expr must not be null!");
|
||||
return newBuilder(GroupOps.SUM, null, expr);
|
||||
}
|
||||
|
||||
private GroupOperationBuilder sum(@Nullable String reference, @Nullable Object value) {
|
||||
return newBuilder(GroupOps.SUM, reference, value);
|
||||
}
|
||||
|
||||
@@ -15,15 +15,20 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -32,7 +37,9 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.CycleGuard.Path;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.TextIndexIncludeOptions.IncludeStrategy;
|
||||
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexDefinitionBuilder;
|
||||
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexedFieldSpec;
|
||||
@@ -98,87 +105,89 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
Document document = root.findAnnotation(Document.class);
|
||||
Assert.notNull(document, "Given entity is not collection root.");
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root));
|
||||
indexInformation.addAll(potentiallyCreateTextIndexDefinition(root));
|
||||
|
||||
final CycleGuard guard = new CycleGuard();
|
||||
|
||||
root.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
try {
|
||||
if (persistentProperty.isEntity()) {
|
||||
indexInformation
|
||||
.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getRequiredActualType(),
|
||||
persistentProperty.getFieldName(), root.getCollection(), guard));
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(
|
||||
persistentProperty.getFieldName(), root.getCollection(), persistentProperty);
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexInformation.add(indexDefinitionHolder);
|
||||
}
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
root.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> this
|
||||
.potentiallyAddIndexForProperty(root, property, indexInformation, new CycleGuard()));
|
||||
|
||||
indexInformation.addAll(resolveIndexesForDbrefs("", root.getCollection(), root));
|
||||
|
||||
return indexInformation;
|
||||
}
|
||||
|
||||
private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, MongoPersistentProperty persistentProperty,
|
||||
List<IndexDefinitionHolder> indexes, CycleGuard guard) {
|
||||
|
||||
try {
|
||||
if (persistentProperty.isEntity()) {
|
||||
indexes.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getActualType(),
|
||||
persistentProperty.getFieldName(), Path.of(persistentProperty), root.getCollection(), guard));
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(
|
||||
persistentProperty.getFieldName(), root.getCollection(), persistentProperty);
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexes.add(indexDefinitionHolder);
|
||||
}
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively resolve and inspect properties of given {@literal type} for indexes to be created.
|
||||
*
|
||||
* @param type
|
||||
* @param path The {@literal "dot} path.
|
||||
* @param dotPath The {@literal "dot} path.
|
||||
* @param path {@link PersistentProperty} path for cycle detection.
|
||||
* @param collection
|
||||
* @param guard
|
||||
* @return List of {@link IndexDefinitionHolder} representing indexes for given type and its referenced property
|
||||
* types. Will never be {@code null}.
|
||||
*/
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass(final TypeInformation<?> type, final String path,
|
||||
final String collection, final CycleGuard guard) {
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass(final TypeInformation<?> type, final String dotPath,
|
||||
final Path path, final String collection, final CycleGuard guard) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, entity));
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(dotPath, collection, entity));
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> this
|
||||
.guradAndPotentiallyAddIndexForProperty(property, dotPath, path, collection, indexInformation, guard));
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(path) ? path + "." : "") + persistentProperty.getFieldName();
|
||||
guard.protect(persistentProperty, path);
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
try {
|
||||
indexInformation.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getActualType(),
|
||||
propertyDotPath, collection, guard));
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(propertyDotPath,
|
||||
collection, persistentProperty);
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexInformation.add(indexDefinitionHolder);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
indexInformation.addAll(resolveIndexesForDbrefs(path, collection, entity));
|
||||
indexInformation.addAll(resolveIndexesForDbrefs(dotPath, collection, entity));
|
||||
|
||||
return indexInformation;
|
||||
}
|
||||
|
||||
private void guradAndPotentiallyAddIndexForProperty(MongoPersistentProperty persistentProperty, String dotPath,
|
||||
Path path, String collection, List<IndexDefinitionHolder> indexes, CycleGuard guard) {
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "") + persistentProperty.getFieldName();
|
||||
|
||||
Path propertyPath = path.append(persistentProperty);
|
||||
guard.protect(persistentProperty, propertyPath);
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
try {
|
||||
indexes.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getActualType(), propertyDotPath,
|
||||
propertyPath, collection, guard));
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
|
||||
persistentProperty);
|
||||
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexes.add(indexDefinitionHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IndexDefinitionHolder createIndexDefinitionHolderForProperty(String dotPath, String collection,
|
||||
MongoPersistentProperty persistentProperty) {
|
||||
@@ -227,8 +236,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
appendTextIndexInformation("", indexDefinitionBuilder, root, new TextIndexIncludeOptions(IncludeStrategy.DEFAULT),
|
||||
new CycleGuard());
|
||||
appendTextIndexInformation("", Path.empty(), indexDefinitionBuilder, root,
|
||||
new TextIndexIncludeOptions(IncludeStrategy.DEFAULT), new CycleGuard());
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
}
|
||||
@@ -244,15 +253,16 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
}
|
||||
|
||||
private void appendTextIndexInformation(final String dotPath, final TextIndexDefinitionBuilder indexDefinitionBuilder,
|
||||
final MongoPersistentEntity<?> entity, final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
|
||||
private void appendTextIndexInformation(final String dotPath, final Path path,
|
||||
final TextIndexDefinitionBuilder indexDefinitionBuilder, final MongoPersistentEntity<?> entity,
|
||||
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
guard.protect(persistentProperty, dotPath);
|
||||
guard.protect(persistentProperty, path);
|
||||
|
||||
if (persistentProperty.isExplicitLanguageProperty() && !StringUtils.hasText(dotPath)) {
|
||||
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
|
||||
@@ -265,6 +275,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "")
|
||||
+ persistentProperty.getFieldName();
|
||||
|
||||
Path propertyPath = path.append(persistentProperty);
|
||||
|
||||
TextIndexedFieldSpec parentFieldSpec = includeOptions.getParentFieldSpec();
|
||||
Float weight = indexed != null ? indexed.weight()
|
||||
: (parentFieldSpec != null ? parentFieldSpec.getWeight() : 1.0F);
|
||||
@@ -278,9 +290,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
appendTextIndexInformation(propertyDotPath, indexDefinitionBuilder,
|
||||
mappingContext.getRequiredPersistentEntity(persistentProperty.getActualType()), optionsForNestedType,
|
||||
guard);
|
||||
appendTextIndexInformation(propertyDotPath, propertyPath, indexDefinitionBuilder,
|
||||
mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.info(e.getMessage());
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
@@ -308,13 +319,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
protected List<IndexDefinitionHolder> createCompoundIndexDefinitions(String dotPath, String fallbackCollection,
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<>();
|
||||
CompoundIndexes indexes = entity.findAnnotation(CompoundIndexes.class);
|
||||
|
||||
if (indexes != null) {
|
||||
for (CompoundIndex index : indexes.value()) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
indexDefinitions = Arrays.stream(indexes.value())
|
||||
.map(index -> createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
CompoundIndex index = entity.findAnnotation(CompoundIndex.class);
|
||||
@@ -384,6 +395,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @param persitentProperty
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
protected IndexDefinitionHolder createIndexDefinition(String dotPath, String collection,
|
||||
MongoPersistentProperty persitentProperty) {
|
||||
|
||||
@@ -428,6 +440,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @param persistentProperty
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
protected IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection,
|
||||
MongoPersistentProperty persistentProperty) {
|
||||
|
||||
@@ -471,86 +484,70 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
private List<IndexDefinitionHolder> resolveIndexesForDbrefs(final String path, final String collection,
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
final List<IndexDefinitionHolder> indexes = new ArrayList<IndexDefinitionHolder>(0);
|
||||
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(path) ? path + "." : "") + property.getFieldName();
|
||||
|
||||
if (property.isAnnotationPresent(GeoSpatialIndexed.class) || property.isAnnotationPresent(TextIndexed.class)) {
|
||||
throw new MappingException(
|
||||
String.format("Cannot create geospatial-/text- index on DBRef in collection '%s' for path '%s'.",
|
||||
collection, propertyDotPath));
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(propertyDotPath,
|
||||
collection, property);
|
||||
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexes.add(indexDefinitionHolder);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final List<IndexDefinitionHolder> indexes = new ArrayList<>(0);
|
||||
entity.doWithAssociations((AssociationHandler<MongoPersistentProperty>) association -> this
|
||||
.resolveAndAddIndexesForAssociation(association, indexes, path, collection));
|
||||
return indexes;
|
||||
}
|
||||
|
||||
private void resolveAndAddIndexesForAssociation(Association<MongoPersistentProperty> association,
|
||||
List<IndexDefinitionHolder> indexes, String path, String collection) {
|
||||
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(path) ? path + "." : "") + property.getFieldName();
|
||||
|
||||
if (property.isAnnotationPresent(GeoSpatialIndexed.class) || property.isAnnotationPresent(TextIndexed.class)) {
|
||||
throw new MappingException(
|
||||
String.format("Cannot create geospatial-/text- index on DBRef in collection '%s' for path '%s'.", collection,
|
||||
propertyDotPath));
|
||||
}
|
||||
|
||||
IndexDefinitionHolder indexDefinitionHolder = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
|
||||
property);
|
||||
|
||||
if (indexDefinitionHolder != null) {
|
||||
indexes.add(indexDefinitionHolder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
|
||||
* to detect potential cycles within the references.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class CycleGuard {
|
||||
|
||||
private final Map<String, List<Path>> propertyTypeMap;
|
||||
|
||||
CycleGuard() {
|
||||
this.propertyTypeMap = new LinkedHashMap<String, List<Path>>();
|
||||
}
|
||||
private final Set<String> seenProperties = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Detect a cycle in a property path if the property was seen at least once.
|
||||
*
|
||||
* @param property The property to inspect
|
||||
* @param path The path under which the property can be reached.
|
||||
* @param path The type path under which the property can be reached.
|
||||
* @throws CyclicPropertyReferenceException in case a potential cycle is detected.
|
||||
* @see Path#cycles(MongoPersistentProperty, String)
|
||||
* @see Path#isCycle()
|
||||
*/
|
||||
void protect(MongoPersistentProperty property, String path) throws CyclicPropertyReferenceException {
|
||||
void protect(MongoPersistentProperty property, Path path) throws CyclicPropertyReferenceException {
|
||||
|
||||
String propertyTypeKey = createMapKey(property);
|
||||
if (propertyTypeMap.containsKey(propertyTypeKey)) {
|
||||
if (!seenProperties.add(propertyTypeKey)) {
|
||||
|
||||
List<Path> paths = propertyTypeMap.get(propertyTypeKey);
|
||||
|
||||
for (Path existingPath : paths) {
|
||||
|
||||
if (existingPath.cycles(property, path) && property.isEntity()) {
|
||||
paths.add(new Path(property, path));
|
||||
|
||||
throw new CyclicPropertyReferenceException(property.getFieldName(), property.getOwner().getType(),
|
||||
existingPath.getPath());
|
||||
}
|
||||
if (path.isCycle()) {
|
||||
throw new CyclicPropertyReferenceException(property.getFieldName(), property.getOwner().getType(),
|
||||
path.toCyclePath());
|
||||
}
|
||||
|
||||
paths.add(new Path(property, path));
|
||||
} else {
|
||||
|
||||
ArrayList<Path> paths = new ArrayList<Path>();
|
||||
paths.add(new Path(property, path));
|
||||
propertyTypeMap.put(propertyTypeKey, paths);
|
||||
}
|
||||
}
|
||||
|
||||
private String createMapKey(MongoPersistentProperty property) {
|
||||
return property.getOwner().getType().getSimpleName() + ":" + property.getFieldName();
|
||||
return ClassUtils.getShortName(property.getOwner().getType()) + ":" + property.getFieldName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Path defines the property and its full path from the document root. <br />
|
||||
* Path defines the full property path from the document root. <br />
|
||||
* A {@link Path} with {@literal spring.data.mongodb} would be created for the property {@code Three.mongodb}.
|
||||
*
|
||||
* <pre>
|
||||
@@ -571,39 +568,117 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
static class Path {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private final String path;
|
||||
private static final Path EMPTY = new Path(Collections.emptyList(), false);
|
||||
|
||||
Path(MongoPersistentProperty property, String path) {
|
||||
private final List<PersistentProperty<?>> elements;
|
||||
private final boolean cycle;
|
||||
|
||||
this.property = property;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
/**
|
||||
* @return an empty {@link Path}.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
static Path empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given property is owned by the same entity and if it has been already visited by a subset of
|
||||
* the current path. Given {@literal foo.bar.bar} cycles if {@literal foo.bar} has already been visited and
|
||||
* {@code class Bar} contains a property of type {@code Bar}. The previously mentioned path would not cycle if
|
||||
* {@code class Bar} contained a property of type {@code SomeEntity} named {@literal bar}.
|
||||
* Creates a new {@link Path} from the initial {@link PersistentProperty}.
|
||||
*
|
||||
* @param property
|
||||
* @param path
|
||||
* @return
|
||||
* @param initial must not be {@literal null}.
|
||||
* @return the new {@link Path}.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
boolean cycles(MongoPersistentProperty property, String path) {
|
||||
static Path of(PersistentProperty<?> initial) {
|
||||
return new Path(Collections.singletonList(initial), false);
|
||||
}
|
||||
|
||||
if (!property.getOwner().equals(this.property.getOwner())) {
|
||||
return false;
|
||||
/**
|
||||
* Creates a new {@link Path} by appending a {@link PersistentProperty breadcrumb} to the path.
|
||||
*
|
||||
* @param breadcrumb must not be {@literal null}.
|
||||
* @return the new {@link Path}.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
Path append(PersistentProperty<?> breadcrumb) {
|
||||
|
||||
List<PersistentProperty<?>> elements = new ArrayList<>(this.elements.size() + 1);
|
||||
elements.addAll(this.elements);
|
||||
elements.add(breadcrumb);
|
||||
|
||||
return new Path(elements, this.elements.contains(breadcrumb));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if a cycle was detected.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
public boolean isCycle() {
|
||||
return cycle;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.elements.isEmpty() ? "(empty)" : toPath(this.elements.iterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cycle path truncated to the first discovered cycle. The result for the path
|
||||
* {@literal foo.bar.baz.bar} is {@literal bar -> baz -> bar}.
|
||||
*
|
||||
* @return the cycle path truncated to the first discovered cycle.
|
||||
* @since 1.10.8
|
||||
*/
|
||||
String toCyclePath() {
|
||||
|
||||
if (!cycle) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return path.equals(this.path) || path.contains(this.path + ".") || path.contains("." + this.path);
|
||||
for (int i = 0; i < this.elements.size(); i++) {
|
||||
|
||||
int index = indexOf(this.elements, this.elements.get(i), i + 1);
|
||||
|
||||
if (index != -1) {
|
||||
return toPath(this.elements.subList(i, index + 1).iterator());
|
||||
}
|
||||
}
|
||||
|
||||
return toString();
|
||||
}
|
||||
|
||||
private static <T> int indexOf(List<T> haystack, T needle, int offset) {
|
||||
|
||||
for (int i = offset; i < haystack.size(); i++) {
|
||||
if (haystack.get(i).equals(needle)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static String toPath(Iterator<PersistentProperty<?>> iterator) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
builder.append(iterator.next().getName());
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(" -> ");
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,9 @@ package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -70,7 +69,7 @@ public abstract class SerializationUtils {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
toFlatMap("", source, result);
|
||||
return result;
|
||||
}
|
||||
@@ -80,12 +79,12 @@ public abstract class SerializationUtils {
|
||||
if (source instanceof Document) {
|
||||
|
||||
Document document = (Document) source;
|
||||
Iterator<Map.Entry<String, Object>> iter = document.entrySet().iterator();
|
||||
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + ".";
|
||||
Iterator<Map.Entry<String, Object>> it = document.entrySet().iterator();
|
||||
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + '.';
|
||||
|
||||
while (iter.hasNext()) {
|
||||
while (it.hasNext()) {
|
||||
|
||||
Map.Entry<String, Object> entry = iter.next();
|
||||
Map.Entry<String, Object> entry = it.next();
|
||||
|
||||
if (entry.getKey().startsWith("$")) {
|
||||
if (map.containsKey(currentPath)) {
|
||||
@@ -109,7 +108,7 @@ public abstract class SerializationUtils {
|
||||
* printing raw {@link Document}s containing complex values before actually converting them into Mongo native types.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
* @return the serialized value or {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
public static String serializeToJsonSafely(@Nullable Object value) {
|
||||
@@ -119,32 +118,26 @@ public abstract class SerializationUtils {
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.serialize(value);
|
||||
return value instanceof Document ? ((Document) value).toJson() : JSON.serialize(value);
|
||||
} catch (Exception e) {
|
||||
|
||||
if (value instanceof Collection) {
|
||||
return toString((Collection<?>) value);
|
||||
} else if (value instanceof Map) {
|
||||
return toString((Map<?, ?>) value);
|
||||
} else {
|
||||
return String.format("{ $java : %s }", value.toString());
|
||||
return String.format("{ \"$java\" : %s }", value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String toString(Map<?, ?> source) {
|
||||
return iterableToDelimitedString(source.entrySet(), "{ ", " }", new Converter<Entry<?, ?>, Object>() {
|
||||
public Object convert(Entry<?, ?> source) {
|
||||
return String.format("\"%s\" : %s", source.getKey(), serializeToJsonSafely(source.getValue()));
|
||||
}
|
||||
});
|
||||
return iterableToDelimitedString(source.entrySet(), "{ ", " }",
|
||||
entry -> String.format("\"%s\" : %s", entry.getKey(), serializeToJsonSafely(entry.getValue())));
|
||||
}
|
||||
|
||||
private static String toString(Collection<?> source) {
|
||||
return iterableToDelimitedString(source, "[ ", " ]", new Converter<Object, Object>() {
|
||||
public Object convert(Object source) {
|
||||
return serializeToJsonSafely(source);
|
||||
}
|
||||
});
|
||||
return iterableToDelimitedString(source, "[ ", " ]", SerializationUtils::serializeToJsonSafely);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,6 +158,7 @@ public abstract class SerializationUtils {
|
||||
Iterator<T> iterator = source.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
builder.append(transformer.convert(iterator.next()));
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(", ");
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -24,6 +22,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -33,6 +32,7 @@ import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -55,9 +55,9 @@ public class Update {
|
||||
}
|
||||
|
||||
private boolean isolated = false;
|
||||
private Set<String> keysToUpdate = new HashSet<String>();
|
||||
private Map<String, Object> modifierOps = new LinkedHashMap<String, Object>();
|
||||
private Map<String, PushOperatorBuilder> pushCommandBuilders = new LinkedHashMap<String, PushOperatorBuilder>(1);
|
||||
private Set<String> keysToUpdate = new HashSet<>();
|
||||
private Map<String, Object> modifierOps = new LinkedHashMap<>();
|
||||
private Map<String, PushOperatorBuilder> pushCommandBuilders = new LinkedHashMap<>(1);
|
||||
|
||||
/**
|
||||
* Static factory method to create an Update using the provided key
|
||||
@@ -122,7 +122,8 @@ public class Update {
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/">MongoDB Update operator: $setOnInsert</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/">MongoDB Update operator:
|
||||
* $setOnInsert</a>
|
||||
*/
|
||||
public Update setOnInsert(String key, Object value) {
|
||||
addMultiFieldOperation("$setOnInsert", key, value);
|
||||
@@ -193,7 +194,8 @@ public class Update {
|
||||
* @param key
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pushAll/">MongoDB Update operator: $pushAll</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pushAll/">MongoDB Update operator:
|
||||
* $pushAll</a>
|
||||
*/
|
||||
public Update pushAll(String key, Object[] values) {
|
||||
addMultiFieldOperation("$pushAll", key, Arrays.asList(values));
|
||||
@@ -218,7 +220,8 @@ public class Update {
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/addToSet/">MongoDB Update operator: $addToSet</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/addToSet/">MongoDB Update operator:
|
||||
* $addToSet</a>
|
||||
*/
|
||||
public Update addToSet(String key, Object value) {
|
||||
addMultiFieldOperation("$addToSet", key, value);
|
||||
@@ -257,7 +260,8 @@ public class Update {
|
||||
* @param key
|
||||
* @param values
|
||||
* @return
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pullAll/">MongoDB Update operator: $pullAll</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pullAll/">MongoDB Update operator:
|
||||
* $pullAll</a>
|
||||
*/
|
||||
public Update pullAll(String key, Object[] values) {
|
||||
addMultiFieldOperation("$pullAll", key, Arrays.asList(values));
|
||||
@@ -270,7 +274,8 @@ public class Update {
|
||||
* @param oldName
|
||||
* @param newName
|
||||
* @return
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/rename/">MongoDB Update operator: $rename</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/rename/">MongoDB Update operator:
|
||||
* $rename</a>
|
||||
*/
|
||||
public Update rename(String oldName, String newName) {
|
||||
addMultiFieldOperation("$rename", oldName, newName);
|
||||
@@ -283,7 +288,8 @@ public class Update {
|
||||
* @param key
|
||||
* @return
|
||||
* @since 1.6
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator: $currentDate</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator:
|
||||
* $currentDate</a>
|
||||
*/
|
||||
public Update currentDate(String key) {
|
||||
|
||||
@@ -297,7 +303,8 @@ public class Update {
|
||||
* @param key
|
||||
* @return
|
||||
* @since 1.6
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator: $currentDate</a>
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator:
|
||||
* $currentDate</a>
|
||||
*/
|
||||
public Update currentTimestamp(String key) {
|
||||
|
||||
@@ -457,7 +464,7 @@ public class Update {
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getUpdateObject().hashCode();
|
||||
return Objects.hash(getUpdateObject(), isolated);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -476,7 +483,11 @@ public class Update {
|
||||
}
|
||||
|
||||
Update that = (Update) obj;
|
||||
return this.getUpdateObject().equals(that.getUpdateObject());
|
||||
if (this.isolated != that.isolated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(this.getUpdateObject(), that.getUpdateObject());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -485,7 +496,14 @@ public class Update {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return SerializationUtils.serializeToJsonSafely(getUpdateObject());
|
||||
|
||||
Document doc = getUpdateObject();
|
||||
|
||||
if (isIsolated()) {
|
||||
doc.append("$isolated", 1);
|
||||
}
|
||||
|
||||
return SerializationUtils.serializeToJsonSafely(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,7 +517,7 @@ public class Update {
|
||||
private Map<String, Modifier> modifiers;
|
||||
|
||||
public Modifiers() {
|
||||
this.modifiers = new LinkedHashMap<String, Modifier>(1);
|
||||
this.modifiers = new LinkedHashMap<>(1);
|
||||
}
|
||||
|
||||
public Collection<Modifier> getModifiers() {
|
||||
@@ -510,15 +528,25 @@ public class Update {
|
||||
this.modifiers.put(modifier.getKey(), modifier);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
/**
|
||||
* @return true if no modifiers present.
|
||||
* @since 2.0
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return modifiers.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nullSafeHashCode(modifiers);
|
||||
return Objects.hashCode(modifiers);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@@ -533,8 +561,12 @@ public class Update {
|
||||
}
|
||||
|
||||
Modifiers that = (Modifiers) obj;
|
||||
return Objects.equals(this.modifiers, that.modifiers);
|
||||
}
|
||||
|
||||
return this.modifiers.equals(that.modifiers);
|
||||
@Override
|
||||
public String toString() {
|
||||
return SerializationUtils.serializeToJsonSafely(this.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +575,7 @@ public class Update {
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static interface Modifier {
|
||||
public interface Modifier {
|
||||
|
||||
/**
|
||||
* @return the command to send eg. {@code $push}
|
||||
@@ -554,6 +586,64 @@ public class Update {
|
||||
* @return value to be sent with command
|
||||
*/
|
||||
Object getValue();
|
||||
|
||||
/**
|
||||
* @return a safely serialized JSON representation.
|
||||
* @since 2.0
|
||||
*/
|
||||
default String toJsonString() {
|
||||
return SerializationUtils.serializeToJsonSafely(Collections.singletonMap(getKey(), getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract {@link Modifier} implementation with defaults for {@link Object#equals(Object)}, {@link Object#hashCode()}
|
||||
* and {@link Object#toString()}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
private static abstract class AbstractModifier implements Modifier {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(getKey()) + ObjectUtils.nullSafeHashCode(getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (that == null || getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(getKey(), ((Modifier) that).getKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.deepEquals(getValue(), ((Modifier) that).getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toJsonString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,11 +652,11 @@ public class Update {
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class Each implements Modifier {
|
||||
private static class Each extends AbstractModifier {
|
||||
|
||||
private Object[] values;
|
||||
|
||||
public Each(Object... values) {
|
||||
Each(Object... values) {
|
||||
this.values = extractValues(values);
|
||||
}
|
||||
|
||||
@@ -600,33 +690,6 @@ public class Update {
|
||||
public Object getValue() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nullSafeHashCode(values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (that == null || getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nullSafeEquals(values, ((Each) that).values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,11 +698,11 @@ public class Update {
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
private static class PositionModifier implements Modifier {
|
||||
private static class PositionModifier extends AbstractModifier {
|
||||
|
||||
private final int position;
|
||||
|
||||
public PositionModifier(int position) {
|
||||
PositionModifier(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@@ -660,11 +723,11 @@ public class Update {
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
private static class Slice implements Modifier {
|
||||
private static class Slice extends AbstractModifier {
|
||||
|
||||
private int count;
|
||||
|
||||
public Slice(int count) {
|
||||
Slice(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
@@ -694,7 +757,7 @@ public class Update {
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
private static class SortModifier implements Modifier {
|
||||
private static class SortModifier extends AbstractModifier {
|
||||
|
||||
private final Object sort;
|
||||
|
||||
@@ -703,7 +766,7 @@ public class Update {
|
||||
*
|
||||
* @param direction must not be {@literal null}.
|
||||
*/
|
||||
public SortModifier(Direction direction) {
|
||||
SortModifier(Direction direction) {
|
||||
|
||||
Assert.notNull(direction, "Direction must not be null!");
|
||||
this.sort = direction.isAscending() ? 1 : -1;
|
||||
@@ -714,7 +777,7 @@ public class Update {
|
||||
*
|
||||
* @param sort must not be {@literal null}.
|
||||
*/
|
||||
public SortModifier(Sort sort) {
|
||||
SortModifier(Sort sort) {
|
||||
|
||||
Assert.notNull(sort, "Sort must not be null!");
|
||||
|
||||
@@ -863,11 +926,17 @@ public class Update {
|
||||
/**
|
||||
* Propagates {@link #value(Object)} to {@code $push}
|
||||
*
|
||||
* @param values
|
||||
* @param value
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public Update value(Object value) {
|
||||
return Update.this.push(key, value);
|
||||
|
||||
if (this.modifiers.isEmpty()) {
|
||||
return Update.this.push(key, value);
|
||||
}
|
||||
|
||||
this.modifiers.addModifier(new Each(Collections.singletonList(value)));
|
||||
return Update.this.push(key, this.modifiers);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -876,14 +945,7 @@ public class Update {
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
|
||||
result += 31 * result + getOuterType().hashCode();
|
||||
result += 31 * result + nullSafeHashCode(key);
|
||||
result += 31 * result + nullSafeHashCode(modifiers);
|
||||
|
||||
return result;
|
||||
return Objects.hash(getOuterType(), key, modifiers);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -903,11 +965,11 @@ public class Update {
|
||||
|
||||
PushOperatorBuilder that = (PushOperatorBuilder) obj;
|
||||
|
||||
if (!getOuterType().equals(that.getOuterType())) {
|
||||
if (!Objects.equals(getOuterType(), that.getOuterType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nullSafeEquals(this.key, that.key) && nullSafeEquals(this.modifiers, that.modifiers);
|
||||
return Objects.equals(this.key, that.key) && Objects.equals(this.modifiers, that.modifiers);
|
||||
}
|
||||
|
||||
private Update getOuterType() {
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
@@ -29,7 +33,18 @@ import lombok.NoArgsConstructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.collection.IsMapContaining;
|
||||
@@ -3256,6 +3271,17 @@ public class MongoTemplateTests {
|
||||
assertThat(template.count(new BasicQuery("{}"), template.determineCollectionName(Sample.class)), is(equalTo(1L)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1779
|
||||
public void appliesQueryLimitToEmptyQuery() {
|
||||
|
||||
Sample first = new Sample("1", "Dave Matthews");
|
||||
Sample second = new Sample("2", "Carter Beauford");
|
||||
|
||||
template.insertAll(Arrays.asList(first, second));
|
||||
|
||||
assertThat(template.find(new Query().limit(1), Sample.class)).hasSize(1);
|
||||
}
|
||||
|
||||
static class TypeWithNumbers {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class SerializationUtilsUnitTests {
|
||||
public void writesSimpleDocument() {
|
||||
|
||||
Document document = new Document("foo", "bar");
|
||||
assertThat(serializeToJsonSafely(document), is("{ \"foo\" : \"bar\"}"));
|
||||
assertThat(serializeToJsonSafely(document), is("{ \"foo\" : \"bar\" }"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,7 +49,7 @@ public class SerializationUtilsUnitTests {
|
||||
|
||||
Document document = new Document("foo", new Complex());
|
||||
assertThat(serializeToJsonSafely(document),
|
||||
startsWith("{ \"foo\" : { $java : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"));
|
||||
startsWith("{ \"foo\" : { \"$java\" : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,7 +58,7 @@ public class SerializationUtilsUnitTests {
|
||||
Document document = new Document("foo", Arrays.asList("bar", new Complex()));
|
||||
Matcher<String> expectedOutput = allOf(
|
||||
startsWith(
|
||||
"{ \"foo\" : [ \"bar\", { $java : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"),
|
||||
"{ \"foo\" : [ \"bar\", { \"$java\" : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"),
|
||||
endsWith(" } ] }"));
|
||||
assertThat(serializeToJsonSafely(document), is(expectedOutput));
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ import com.mongodb.client.MongoCollection;
|
||||
* @author Mark Paluch
|
||||
* @author Nikolay Bogdanov
|
||||
* @author Maninder Singh
|
||||
* @author Sergey Shcherbakov
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -799,6 +800,49 @@ public class AggregationTests {
|
||||
assertThat(((Number) good.get("score")).longValue(), is(equalTo(9000L)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1784
|
||||
public void shouldAllowSumUsingConditionalExpressions() {
|
||||
|
||||
mongoTemplate.dropCollection(CarPerson.class);
|
||||
|
||||
CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000),
|
||||
new CarDescriptor.Entry("MAKE1", "MODEL2", 2001));
|
||||
|
||||
CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014));
|
||||
CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2015));
|
||||
|
||||
mongoTemplate.save(person1);
|
||||
mongoTemplate.save(person2);
|
||||
mongoTemplate.save(person3);
|
||||
|
||||
TypedAggregation<CarPerson> agg = Aggregation.newAggregation(CarPerson.class,
|
||||
unwind("descriptors.carDescriptor.entries"), //
|
||||
project() //
|
||||
.and(ConditionalOperators //
|
||||
.when(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1")).then("good")
|
||||
.otherwise("meh"))
|
||||
.as("make") //
|
||||
.and("descriptors.carDescriptor.entries.model").as("model") //
|
||||
.and("descriptors.carDescriptor.entries.year").as("year"), //
|
||||
group("make").sum(ConditionalOperators //
|
||||
.when(Criteria.where("year").gte(2012)) //
|
||||
.then(1) //
|
||||
.otherwise(9000)).as("score"),
|
||||
sort(ASC, "make"));
|
||||
|
||||
AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
|
||||
|
||||
assertThat(result.getMappedResults(), hasSize(2));
|
||||
|
||||
Document meh = result.getMappedResults().get(0);
|
||||
assertThat(meh.get("_id"), is(equalTo("meh")));
|
||||
assertThat(((Number) meh.get("score")).longValue(), is(equalTo(2L)));
|
||||
|
||||
Document good = result.getMappedResults().get(1);
|
||||
assertThat(good.get("_id"), is(equalTo("good")));
|
||||
assertThat(((Number) good.get("score")).longValue(), is(equalTo(18000L)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/tutorial/aggregation-with-user-preference-data/#return-the-five-most-common-likes">Return
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Arrays;
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GroupOperation}.
|
||||
@@ -216,6 +217,29 @@ public class GroupOperationUnitTests {
|
||||
assertThat(push, is(new Document("$stdDevPop", "$field")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1784
|
||||
public void shouldRenderSumWithExpressionInGroup() {
|
||||
|
||||
GroupOperation groupOperation = Aggregation //
|
||||
.group("username") //
|
||||
.sum(ConditionalOperators //
|
||||
.when(Criteria.where("foo").is("bar")) //
|
||||
.then(1) //
|
||||
.otherwise(-1)) //
|
||||
.as("foobar");
|
||||
|
||||
Document groupClause = extractDocumentFromGroupOperation(groupOperation);
|
||||
Document foobar = DocumentTestUtils.getAsDocument(groupClause, "foobar");
|
||||
|
||||
assertThat(foobar.get("$sum"), is(new Document("$cond",
|
||||
new Document("if", new Document("$eq", Arrays.asList("$foo", "bar"))).append("then", 1).append("else", -1))));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1784
|
||||
public void sumWithNullExpressionShouldThrowException() {
|
||||
Aggregation.group("username").sum((AggregationExpression) null);
|
||||
}
|
||||
|
||||
private Document extractDocumentFromGroupOperation(GroupOperation groupOperation) {
|
||||
Document document = groupOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
Document groupClause = DocumentTestUtils.getAsDocument(document, "$group");
|
||||
|
||||
@@ -62,7 +62,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
/**
|
||||
* Test resolution of {@link Indexed}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class IndexResolutionTests {
|
||||
@@ -297,7 +297,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
/**
|
||||
* Test resolution of {@link GeoSpatialIndexed}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class GeoSpatialIndexResolutionTests {
|
||||
@@ -410,7 +410,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
/**
|
||||
* Test resolution of {@link CompoundIndexes}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class CompoundIndexResolutionTests {
|
||||
@@ -898,6 +898,17 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
.resolveIndexForEntity(selfCyclingEntity);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1782
|
||||
public void shouldAllowMultiplePathsToDeeplyType() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(
|
||||
NoCycleManyPathsToDeepValueObject.class);
|
||||
|
||||
assertIndexPathAndCollection("l3.valueObject.value", "rules", indexDefinitions.get(0));
|
||||
assertIndexPathAndCollection("l2.l3.valueObject.value", "rules", indexDefinitions.get(1));
|
||||
assertThat(indexDefinitions, hasSize(2));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1025
|
||||
public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedCompoundIndexFixedOnCollection() {
|
||||
|
||||
@@ -1055,6 +1066,25 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
@Indexed String foo;
|
||||
}
|
||||
|
||||
@Document(collection = "rules")
|
||||
static class NoCycleManyPathsToDeepValueObject {
|
||||
|
||||
private NoCycleLevel3 l3;
|
||||
private NoCycleLevel2 l2;
|
||||
}
|
||||
|
||||
static class NoCycleLevel2 {
|
||||
private NoCycleLevel3 l3;
|
||||
}
|
||||
|
||||
static class NoCycleLevel3 {
|
||||
private ValueObject valueObject;
|
||||
}
|
||||
|
||||
static class ValueObject {
|
||||
@Indexed private String value;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class SimilarityHolingBean {
|
||||
|
||||
@@ -1171,7 +1201,6 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
static class EntityWithGenericTypeWrapperAsElement {
|
||||
List<GenericEntityWrapper<DocumentWithNamedIndex>> listWithGeneircTypeElement;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static List<IndexDefinitionHolder> prepareMappingContextAndResolveIndexForType(Class<?> type) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@@ -31,45 +31,65 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Path}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class PathUnitTests {
|
||||
|
||||
@Mock MongoPersistentEntity<?> entityMock;
|
||||
|
||||
@Test // DATAMONGO-962
|
||||
public void shouldIdentifyCycleForOwnerOfSameTypeAndMatchingPath() {
|
||||
|
||||
MongoPersistentProperty property = createPersistentPropertyMock(entityMock, "foo");
|
||||
assertThat(new Path(property, "foo.bar").cycles(property, "foo.bar.bar"), is(true));
|
||||
@Before
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setUp() {
|
||||
when(entityMock.getType()).thenReturn((Class) Object.class);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-962
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void shouldAllowMatchingPathForDifferentOwners() {
|
||||
@Test // DATAMONGO-962, DATAMONGO-1782
|
||||
public void shouldIdentifyCycle() {
|
||||
|
||||
MongoPersistentProperty existing = createPersistentPropertyMock(entityMock, "foo");
|
||||
MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo");
|
||||
MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar");
|
||||
|
||||
MongoPersistentEntity entityOfDifferentType = Mockito.mock(MongoPersistentEntity.class);
|
||||
MongoPersistentProperty toBeVerified = createPersistentPropertyMock(entityOfDifferentType, "foo");
|
||||
Path path = Path.of(foo).append(bar).append(bar);
|
||||
|
||||
assertThat(new Path(existing, "foo.bar").cycles(toBeVerified, "foo.bar.bar"), is(false));
|
||||
assertThat(path.isCycle(), is(true));
|
||||
assertThat(path.toCyclePath(), is(equalTo("bar -> bar")));
|
||||
assertThat(path.toString(), is(equalTo("foo -> bar -> bar")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-962
|
||||
public void shouldAllowEqaulPropertiesOnDifferentPaths() {
|
||||
@Test // DATAMONGO-1782
|
||||
public void isCycleShouldReturnFalseWhenNoCyclePresent() {
|
||||
|
||||
MongoPersistentProperty property = createPersistentPropertyMock(entityMock, "foo");
|
||||
assertThat(new Path(property, "foo.bar").cycles(property, "foo2.bar.bar"), is(false));
|
||||
MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo");
|
||||
MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar");
|
||||
|
||||
Path path = Path.of(foo).append(bar);
|
||||
|
||||
assertThat(path.isCycle(), is(false));
|
||||
assertThat(path.toCyclePath(), is(equalTo("")));
|
||||
assertThat(path.toString(), is(equalTo("foo -> bar")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1782
|
||||
public void isCycleShouldReturnFalseCycleForNonEqualProperties() {
|
||||
|
||||
MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo");
|
||||
MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar");
|
||||
MongoPersistentProperty bar2 = createPersistentPropertyMock(mock(MongoPersistentEntity.class), "bar");
|
||||
|
||||
assertThat(Path.of(foo).append(bar).append(bar2).isCycle(), is(false));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private MongoPersistentProperty createPersistentPropertyMock(MongoPersistentEntity owner, String fieldname) {
|
||||
private static MongoPersistentProperty createPersistentPropertyMock(MongoPersistentEntity owner, String fieldname) {
|
||||
|
||||
MongoPersistentProperty property = Mockito.mock(MongoPersistentProperty.class);
|
||||
|
||||
when(property.getOwner()).thenReturn(owner);
|
||||
when(property.getName()).thenReturn(fieldname);
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import nl.jqno.equalsverifier.Warning;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BasicQuery}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author John Willemin
|
||||
|
||||
@@ -210,7 +210,7 @@ public class QueryTests {
|
||||
|
||||
Query query = new Query(where("name").is("foo")).restrict(SpecialDoc.class);
|
||||
assertThat(query.toString(), is(
|
||||
"Query: { \"name\" : \"foo\", \"_$RESTRICTED_TYPES\" : [ { $java : class org.springframework.data.mongodb.core.SpecialDoc } ] }, Fields: { }, Sort: { }"));
|
||||
"Query: { \"name\" : \"foo\", \"_$RESTRICTED_TYPES\" : [ { \"$java\" : class org.springframework.data.mongodb.core.SpecialDoc } ] }, Fields: { }, Sort: { }"));
|
||||
assertThat(query.getRestrictedTypes(), is(notNullValue()));
|
||||
assertThat(query.getRestrictedTypes().size(), is(1));
|
||||
assertThat(query.getRestrictedTypes(), hasItems(Arrays.asList(SpecialDoc.class).toArray(new Class<?>[0])));
|
||||
@@ -221,7 +221,7 @@ public class QueryTests {
|
||||
|
||||
exception.expect(InvalidMongoDbApiUsageException.class);
|
||||
exception.expectMessage("second 'value' criteria");
|
||||
exception.expectMessage("already contains '{ \"value\" : { $java : VAL_1 } }'");
|
||||
exception.expectMessage("already contains '{ \"value\" : { \"$java\" : VAL_1 } }'");
|
||||
|
||||
Query query = new Query();
|
||||
query.addCriteria(where("value").is(EnumType.VAL_1));
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -26,6 +25,7 @@ import org.bson.Document;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
import org.springframework.data.mongodb.core.query.Update.Position;
|
||||
|
||||
/**
|
||||
* Test cases for {@link Update}.
|
||||
@@ -43,51 +43,52 @@ public class UpdateTests {
|
||||
public void testSet() {
|
||||
|
||||
Update u = new Update().set("directory", "/Users/Test/Desktop");
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")));
|
||||
assertThat(u.getUpdateObject())
|
||||
.isEqualTo(Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSet() {
|
||||
|
||||
Update u = new Update().set("directory", "/Users/Test/Desktop").set("size", 0);
|
||||
assertThat(u.getUpdateObject(),
|
||||
is(Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\" , \"size\" : 0}}")));
|
||||
assertThat(u.getUpdateObject())
|
||||
.isEqualTo((Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\" , \"size\" : 0}}")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInc() {
|
||||
|
||||
Update u = new Update().inc("size", 1);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$inc\" : { \"size\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncInc() {
|
||||
|
||||
Update u = new Update().inc("size", 1).inc("count", 1);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$inc\" : { \"size\" : 1 , \"count\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1 , \"count\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncAndSet() {
|
||||
|
||||
Update u = new Update().inc("size", 1).set("directory", "/Users/Test/Desktop");
|
||||
assertThat(u.getUpdateObject(),
|
||||
is(Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(
|
||||
Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnset() {
|
||||
|
||||
Update u = new Update().unset("directory");
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$unset\" : { \"directory\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$unset\" : { \"directory\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPush() {
|
||||
|
||||
Update u = new Update().push("authors", Collections.singletonMap("name", "Sven"));
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$push\" : { \"authors\" : { \"name\" : \"Sven\"}}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$push\" : { \"authors\" : { \"name\" : \"Sven\"}}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,8 +98,8 @@ public class UpdateTests {
|
||||
Map<String, String> m2 = Collections.singletonMap("name", "Maria");
|
||||
|
||||
Update u = new Update().pushAll("authors", new Object[] { m1, m2 });
|
||||
assertThat(u.getUpdateObject(),
|
||||
is(Document.parse("{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(
|
||||
Document.parse("{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-354
|
||||
@@ -110,32 +111,33 @@ public class UpdateTests {
|
||||
Update u = new Update().pushAll("authors", new Object[] { m1, m2 });
|
||||
u.pushAll("books", new Object[] { "Spring in Action" });
|
||||
|
||||
assertThat(u.getUpdateObject(), is(Document.parse(
|
||||
"{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}] , \"books\" : [ \"Spring in Action\"]}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse(
|
||||
"{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}] , \"books\" : [ \"Spring in Action\"]}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddToSet() {
|
||||
|
||||
Update u = new Update().addToSet("authors", Collections.singletonMap("name", "Sven"));
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$addToSet\" : { \"authors\" : { \"name\" : \"Sven\"}}}")));
|
||||
assertThat(u.getUpdateObject())
|
||||
.isEqualTo(Document.parse("{ \"$addToSet\" : { \"authors\" : { \"name\" : \"Sven\"}}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPop() {
|
||||
|
||||
Update u = new Update().pop("authors", Update.Position.FIRST);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$pop\" : { \"authors\" : -1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pop\" : { \"authors\" : -1}}"));
|
||||
|
||||
u = new Update().pop("authors", Update.Position.LAST);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$pop\" : { \"authors\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pop\" : { \"authors\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPull() {
|
||||
|
||||
Update u = new Update().pull("authors", Collections.singletonMap("name", "Sven"));
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$pull\" : { \"authors\" : { \"name\" : \"Sven\"}}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pull\" : { \"authors\" : { \"name\" : \"Sven\"}}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,77 +147,77 @@ public class UpdateTests {
|
||||
Map<String, String> m2 = Collections.singletonMap("name", "Maria");
|
||||
|
||||
Update u = new Update().pullAll("authors", new Object[] { m1, m2 });
|
||||
assertThat(u.getUpdateObject(),
|
||||
is(Document.parse("{ \"$pullAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(
|
||||
Document.parse("{ \"$pullAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRename() {
|
||||
|
||||
Update u = new Update().rename("directory", "folder");
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$rename\" : { \"directory\" : \"folder\"}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$rename\" : { \"directory\" : \"folder\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicUpdateInc() {
|
||||
|
||||
Update u = new Update().inc("size", 1);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$inc\" : { \"size\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicUpdateIncAndSet() {
|
||||
|
||||
Update u = new BasicUpdate("{ \"$inc\" : { \"size\" : 1}}").set("directory", "/Users/Test/Desktop");
|
||||
assertThat(u.getUpdateObject(),
|
||||
is(Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(
|
||||
Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-630
|
||||
public void testSetOnInsert() {
|
||||
|
||||
Update u = new Update().setOnInsert("size", 1);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-630
|
||||
public void testSetOnInsertSetOnInsert() {
|
||||
|
||||
Update u = new Update().setOnInsert("size", 1).setOnInsert("count", 1);
|
||||
assertThat(u.getUpdateObject(), is(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1 , \"count\" : 1}}")));
|
||||
assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1 , \"count\" : 1}}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
public void testUpdateAffectsFieldShouldReturnTrueWhenMultiFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
assertThat(update.modifies("foo"), is(true));
|
||||
assertThat(update.modifies("foo")).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenMultiFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().set("foo", "bar");
|
||||
assertThat(update.modifies("oof"), is(false));
|
||||
assertThat(update.modifies("oof")).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
public void testUpdateAffectsFieldShouldReturnTrueWhenSingleFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().pullAll("foo", new Object[] { "bar" });
|
||||
assertThat(update.modifies("foo"), is(true));
|
||||
assertThat(update.modifies("foo")).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenSingleFieldOperationAddedForField() {
|
||||
|
||||
Update update = new Update().pullAll("foo", new Object[] { "bar" });
|
||||
assertThat(update.modifies("oof"), is(false));
|
||||
assertThat(update.modifies("oof")).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
public void testUpdateAffectsFieldShouldReturnFalseWhenCalledOnEmptyUpdate() {
|
||||
assertThat(new Update().modifies("foo"), is(false));
|
||||
assertThat(new Update().modifies("foo")).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
@@ -224,7 +226,7 @@ public class UpdateTests {
|
||||
Update update = new Update().set("foo", "bar");
|
||||
Update clone = Update.fromDocument(update.getUpdateObject());
|
||||
|
||||
assertThat(clone.modifies("foo"), is(true));
|
||||
assertThat(clone.modifies("foo")).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-852
|
||||
@@ -233,7 +235,7 @@ public class UpdateTests {
|
||||
Update update = new Update().set("foo", "bar");
|
||||
Update clone = Update.fromDocument(update.getUpdateObject());
|
||||
|
||||
assertThat(clone.modifies("oof"), is(false));
|
||||
assertThat(clone.modifies("oof")).isFalse();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-853
|
||||
@@ -270,10 +272,10 @@ public class UpdateTests {
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
assertThat(actualUpdate, is(equalTo(actualUpdate)));
|
||||
assertThat(actualUpdate.hashCode(), is(equalTo(actualUpdate.hashCode())));
|
||||
assertThat(actualUpdate, is(equalTo(expectedUpdate)));
|
||||
assertThat(actualUpdate.hashCode(), is(equalTo(expectedUpdate.hashCode())));
|
||||
assertThat(actualUpdate).isEqualTo(actualUpdate);
|
||||
assertThat(actualUpdate.hashCode()).isEqualTo(actualUpdate.hashCode());
|
||||
assertThat(actualUpdate).isEqualTo(expectedUpdate);
|
||||
assertThat(actualUpdate.hashCode()).isEqualTo(expectedUpdate.hashCode());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-953
|
||||
@@ -295,58 +297,57 @@ public class UpdateTests {
|
||||
.pop("authors", Update.Position.FIRST) //
|
||||
.set("foo", "bar");
|
||||
|
||||
assertThat(actualUpdate.toString(), is(equalTo(expectedUpdate.toString())));
|
||||
assertThat(actualUpdate.getUpdateObject(),
|
||||
is(Document.parse("{ \"$inc\" : { \"size\" : 1} ," //
|
||||
+ " \"$set\" : { \"nl\" : null , \"directory\" : \"/Users/Test/Desktop\" , \"foo\" : \"bar\"} , " //
|
||||
+ "\"$push\" : { \"authors\" : { \"name\" : \"Sven\"}} " //
|
||||
+ ", \"$pop\" : { \"authors\" : -1}}"))); //
|
||||
assertThat(actualUpdate.toString()).isEqualTo(expectedUpdate.toString());
|
||||
assertThat(actualUpdate.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1} ," //
|
||||
+ " \"$set\" : { \"nl\" : null , \"directory\" : \"/Users/Test/Desktop\" , \"foo\" : \"bar\"} , " //
|
||||
+ "\"$push\" : { \"authors\" : { \"name\" : \"Sven\"}} " //
|
||||
+ ", \"$pop\" : { \"authors\" : -1}}")); //
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-944
|
||||
public void getUpdateObjectShouldReturnCurrentDateCorrectlyForSingleFieldWhenUsingDate() {
|
||||
|
||||
Update update = new Update().currentDate("foo");
|
||||
assertThat(update.getUpdateObject(), equalTo(new Document().append("$currentDate", new Document("foo", true))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate", new Document("foo", true)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-944
|
||||
public void getUpdateObjectShouldReturnCurrentDateCorrectlyForMultipleFieldsWhenUsingDate() {
|
||||
|
||||
Update update = new Update().currentDate("foo").currentDate("bar");
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document().append("$currentDate", new Document("foo", true).append("bar", true))));
|
||||
assertThat(update.getUpdateObject())
|
||||
.isEqualTo(new Document().append("$currentDate", new Document("foo", true).append("bar", true)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-944
|
||||
public void getUpdateObjectShouldReturnCurrentDateCorrectlyForSingleFieldWhenUsingTimestamp() {
|
||||
|
||||
Update update = new Update().currentTimestamp("foo");
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document().append("$currentDate", new Document("foo", new Document("$type", "timestamp")))));
|
||||
assertThat(update.getUpdateObject())
|
||||
.isEqualTo(new Document().append("$currentDate", new Document("foo", new Document("$type", "timestamp"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-944
|
||||
public void getUpdateObjectShouldReturnCurrentDateCorrectlyForMultipleFieldsWhenUsingTimestamp() {
|
||||
|
||||
Update update = new Update().currentTimestamp("foo").currentTimestamp("bar");
|
||||
assertThat(update.getUpdateObject(), equalTo(new Document().append("$currentDate",
|
||||
new Document("foo", new Document("$type", "timestamp")).append("bar", new Document("$type", "timestamp")))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate",
|
||||
new Document("foo", new Document("$type", "timestamp")).append("bar", new Document("$type", "timestamp"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-944
|
||||
public void getUpdateObjectShouldReturnCurrentDateCorrectlyWhenUsingMixedDateAndTimestamp() {
|
||||
|
||||
Update update = new Update().currentDate("foo").currentTimestamp("bar");
|
||||
assertThat(update.getUpdateObject(), equalTo(new Document().append("$currentDate",
|
||||
new Document("foo", true).append("bar", new Document("$type", "timestamp")))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate",
|
||||
new Document("foo", true).append("bar", new Document("$type", "timestamp"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1002
|
||||
public void toStringWorksForUpdateWithComplexObject() {
|
||||
|
||||
Update update = new Update().addToSet("key", new DateTime());
|
||||
assertThat(update.toString(), is(notNullValue()));
|
||||
assertThat(update.toString()).isNotNull();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1097
|
||||
@@ -359,7 +360,7 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().multiply("key", 10);
|
||||
|
||||
assertThat(update.getUpdateObject(), equalTo(new Document().append("$mul", new Document("key", 10D))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$mul", new Document("key", 10D)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1101
|
||||
@@ -367,8 +368,8 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().bitwise("key").and(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document().append("$bit", new Document("key", new Document("and", 10L)))));
|
||||
assertThat(update.getUpdateObject())
|
||||
.isEqualTo(new Document().append("$bit", new Document("key", new Document("and", 10L))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1101
|
||||
@@ -376,8 +377,8 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().bitwise("key").or(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document().append("$bit", new Document("key", new Document("or", 10L)))));
|
||||
assertThat(update.getUpdateObject())
|
||||
.isEqualTo(new Document().append("$bit", new Document("key", new Document("or", 10L))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1101
|
||||
@@ -385,8 +386,8 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().bitwise("key").xor(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document().append("$bit", new Document("key", new Document("xor", 10L)))));
|
||||
assertThat(update.getUpdateObject())
|
||||
.isEqualTo(new Document().append("$bit", new Document("key", new Document("xor", 10L))));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-943
|
||||
@@ -405,8 +406,8 @@ public class UpdateTests {
|
||||
|
||||
Document pullAll = DocumentTestUtils.getAsDocument(updateObject, "$pullAll");
|
||||
|
||||
assertThat(pullAll.get("field1"), is(notNullValue()));
|
||||
assertThat(pullAll.get("field2"), is(notNullValue()));
|
||||
assertThat(pullAll.get("field1")).isNotNull();
|
||||
assertThat(pullAll.get("field2")).isNotNull();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-1404
|
||||
@@ -424,8 +425,7 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().max("key", 10);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$max", new Document("key", 10))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", 10)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1404
|
||||
@@ -433,8 +433,7 @@ public class UpdateTests {
|
||||
|
||||
Update update = new Update().min("key", 10);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$min", new Document("key", 10))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", 10)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1404
|
||||
@@ -443,8 +442,7 @@ public class UpdateTests {
|
||||
Update update = new Update().max("key", 10);
|
||||
update.max("key", 99);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$max", new Document("key", 99))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", 99)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1404
|
||||
@@ -453,8 +451,7 @@ public class UpdateTests {
|
||||
Update update = new Update().min("key", 10);
|
||||
update.min("key", 99);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$min", new Document("key", 99))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", 99)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1404
|
||||
@@ -463,8 +460,7 @@ public class UpdateTests {
|
||||
Date date = new Date();
|
||||
Update update = new Update().max("key", date);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$max", new Document("key", date))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", date)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1404
|
||||
@@ -473,7 +469,62 @@ public class UpdateTests {
|
||||
Date date = new Date();
|
||||
Update update = new Update().min("key", date);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new Document("$min", new Document("key", date))));
|
||||
assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", date)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1777
|
||||
public void toStringShouldPrettyPrintModifiers() {
|
||||
|
||||
assertThat(new Update().push("key").atPosition(Position.FIRST).value("Arya").toString()).isEqualTo(
|
||||
"{ \"$push\" : { \"key\" : { \"$java\" : { \"$position\" : { \"$java\" : { \"$position\" : 0} }, \"$each\" : { \"$java\" : { \"$each\" : [ \"Arya\"]} } } } } }");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1777
|
||||
public void toStringConsidersIsolated() {
|
||||
|
||||
assertThat(new Update().set("key", "value").isolated().toString())
|
||||
.isEqualTo("{ \"$set\" : { \"key\" : \"value\" }, \"$isolated\" : 1 }");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1778
|
||||
public void equalsShouldConsiderModifiers() {
|
||||
|
||||
Update update1 = new Update().inc("version", 1).push("someField").slice(-10).each("test");
|
||||
Update update2 = new Update().inc("version", 1).push("someField").slice(-10).each("test");
|
||||
Update update3 = new Update().inc("version", 1).push("someField").slice(10).each("test");
|
||||
|
||||
assertThat(update1).isEqualTo(update2);
|
||||
assertThat(update1).isNotEqualTo(update3);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1778
|
||||
public void equalsShouldConsiderIsolated() {
|
||||
|
||||
Update update1 = new Update().inc("version", 1).isolated();
|
||||
Update update2 = new Update().inc("version", 1).isolated();
|
||||
|
||||
assertThat(update1).isEqualTo(update2);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1778
|
||||
public void hashCodeShouldConsiderModifiers() {
|
||||
|
||||
Update update1 = new Update().inc("version", 1).push("someField").slice(-10).each("test");
|
||||
Update update2 = new Update().inc("version", 1).push("someField").slice(-10).each("test");
|
||||
Update update3 = new Update().inc("version", 1).push("someField").slice(10).each("test");
|
||||
|
||||
assertThat(update1.hashCode()).isEqualTo(update2.hashCode());
|
||||
assertThat(update1.hashCode()).isNotEqualTo(update3.hashCode());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1778
|
||||
public void hashCodeShouldConsiderIsolated() {
|
||||
|
||||
Update update1 = new Update().inc("version", 1).isolated();
|
||||
Update update2 = new Update().inc("version", 1).isolated();
|
||||
Update update3 = new Update().inc("version", 1);
|
||||
|
||||
assertThat(update1.hashCode()).isEqualTo(update2.hashCode());
|
||||
assertThat(update1.hashCode()).isNotEqualTo(update3.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Sample repository managing {@link Person} entities.
|
||||
@@ -87,7 +88,7 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
* @param firstname
|
||||
* @return
|
||||
*/
|
||||
List<Person> findByFirstnameLike(String firstname);
|
||||
List<Person> findByFirstnameLike(@Nullable String firstname);
|
||||
|
||||
List<Person> findByFirstnameNotContains(String firstname);
|
||||
|
||||
@@ -232,7 +233,7 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
boolean someExistQuery(String lastname);
|
||||
|
||||
// DATAMONGO-770
|
||||
List<Person> findByFirstnameIgnoreCase(String firstName);
|
||||
List<Person> findByFirstnameIgnoreCase(@Nullable String firstName);
|
||||
|
||||
// DATAMONGO-770
|
||||
List<Person> findByFirstnameNotIgnoreCase(String firstName);
|
||||
|
||||
@@ -18,8 +18,9 @@ package org.springframework.data.mongodb.repository.cdi;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.apache.webbeans.cditest.CdiTestContainer;
|
||||
import org.apache.webbeans.cditest.CdiTestContainerLoader;
|
||||
import javax.enterprise.inject.se.SeContainer;
|
||||
import javax.enterprise.inject.se.SeContainerInitializer;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@@ -27,29 +28,32 @@ import org.springframework.data.mongodb.repository.Person;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoRepositoryExtension}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class CdiExtensionIntegrationTests {
|
||||
|
||||
static CdiTestContainer container;
|
||||
static SeContainer container;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
container = CdiTestContainerLoader.getCdiContainer();
|
||||
container.bootContainer();
|
||||
public static void setUp() {
|
||||
|
||||
container = SeContainerInitializer.newInstance() //
|
||||
.disableDiscovery() //
|
||||
.addPackages(CdiExtensionIntegrationTests.class) //
|
||||
.initialize();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() throws Exception {
|
||||
container.shutdownContainer();
|
||||
public static void tearDown() {
|
||||
container.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAMONGO-356, DATAMONGO-1785
|
||||
public void bootstrapsRepositoryCorrectly() {
|
||||
|
||||
RepositoryClient client = container.getInstance(RepositoryClient.class);
|
||||
RepositoryClient client = container.select(RepositoryClient.class).get();
|
||||
CdiPersonRepository repository = client.getRepository();
|
||||
|
||||
assertThat(repository, is(notNullValue()));
|
||||
@@ -63,11 +67,10 @@ public class CdiExtensionIntegrationTests {
|
||||
assertThat(repository.findById(person.getId()).get().getId(), is(result.getId()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1017
|
||||
@Test // DATAMONGO-1017, DATAMONGO-1785
|
||||
public void returnOneFromCustomImpl() {
|
||||
|
||||
RepositoryClient repositoryConsumer = container.getInstance(RepositoryClient.class);
|
||||
RepositoryClient repositoryConsumer = container.select(RepositoryClient.class).get();
|
||||
assertThat(repositoryConsumer.getSamplePersonRepository().returnOne(), is(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
|
||||
</beans>
|
||||
|
||||
@@ -6,8 +6,15 @@
|
||||
* Upgrade to Java 8.
|
||||
* Usage of the `Document` API instead of `DBObject`.
|
||||
* <<mongo.reactive>>.
|
||||
* <<mongo.reactive.repositories.infinite-streams,Tailable Cursor>> queries.
|
||||
* Support for aggregation result streaming via Java 8 `Stream`.
|
||||
* <<mongo.query.fluent-template-api,Fluent Collection API>> for CRUD and aggregation operations.
|
||||
* Kotlin extensions for Template and Collection API.
|
||||
* Integration of collations for collection and index creation and query operations.
|
||||
* Query-by-Example support without type matching.
|
||||
* Add support for isolation ``Update``s.
|
||||
* Tooling support for null-safety via Spring's `@NonNullApi` and `@Nullable` annotations.
|
||||
* Deprecated cross-store support and removed Log4j appender.
|
||||
|
||||
[[new-features.1-10-0]]
|
||||
== What's new in Spring Data MongoDB 1.10
|
||||
@@ -35,7 +42,7 @@
|
||||
== What's new in Spring Data MongoDB 1.8
|
||||
|
||||
* `Criteria` offers support for creating `$geoIntersects`.
|
||||
* Support http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[SpEL expressions] in `@Query`.
|
||||
* Support http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#expressions[SpEL expressions] in `@Query`.
|
||||
* `MongoMappingEvents` expose the collection name they are issued for.
|
||||
* Improved support for `<mongo:mongo-client credentials="..." />`.
|
||||
* Improved index creation failure error message.
|
||||
|
||||
@@ -9,7 +9,7 @@ This section provides some basic introduction to Spring and Document databases.
|
||||
|
||||
[[get-started:first-steps:spring]]
|
||||
== Knowing Spring
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#beans[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#validation[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#expressions[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/integration.html#jmx[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
|
||||
The core functionality of the MongoDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data MongoDB, such as the repository support, you will need to configure some parts of the library using Spring.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[[mongo.jmx]]
|
||||
= JMX support
|
||||
|
||||
The JMX support for MongoDB exposes the results of executing the 'serverStatus' command on the admin database for a single MongoDB server instance. It also exposes an administrative MBean, MongoAdmin which will let you perform administrative operations such as drop or create a database. The JMX features build upon the JMX feature set available in the Spring Framework. See http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[here ] for more details.
|
||||
The JMX support for MongoDB exposes the results of executing the 'serverStatus' command on the admin database for a single MongoDB server instance. It also exposes an administrative MBean, MongoAdmin which will let you perform administrative operations such as drop or create a database. The JMX features build upon the JMX feature set available in the Spring Framework. See http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/integration.html#jmx[here ] for more details.
|
||||
|
||||
[[mongodb:jmx-configuration]]
|
||||
== MongoDB JMX Configuration
|
||||
|
||||
@@ -601,7 +601,7 @@ When storing and querying your objects it is convenient to have a `MongoConverte
|
||||
|
||||
To selectively handle the conversion yourself, register one or more one or more `org.springframework.core.convert.converter.Converter` instances with the MongoConverter.
|
||||
|
||||
NOTE: Spring 3.0 introduced a core.convert package that provides a general type conversion system. This is described in detail in the Spring reference documentation section entitled http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[Spring Type Conversion].
|
||||
NOTE: Spring 3.0 introduced a core.convert package that provides a general type conversion system. This is described in detail in the Spring reference documentation section entitled http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#validation[Spring Type Conversion].
|
||||
|
||||
The method `customConversions` in `AbstractMongoConfiguration` can be used to configure Converters. The examples <<mapping-configuration,here>> at the beginning of this chapter show how to perform the configuration using Java and XML.
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ There is an https://github.com/spring-projects/spring-data-examples[github repos
|
||||
|
||||
One of the first tasks when using MongoDB and Spring is to create a `com.mongodb.MongoClient` object using the IoC container. There are two main ways to do this, either using Java based bean metadata or XML based bean metadata. These are discussed in the following sections.
|
||||
|
||||
NOTE: For those not familiar with how to configure the Spring container using Java based bean metadata instead of XML based metadata see the high level introduction in the reference docs http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration[here ] as well as the detailed documentation http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html#beans-java-instantiating-container[ here].
|
||||
NOTE: For those not familiar with how to configure the Spring container using Java based bean metadata instead of XML based metadata see the high level introduction in the reference docs http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration[here ] as well as the detailed documentation http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#beans-java-instantiating-container[ here].
|
||||
|
||||
[[mongo.mongo-java-config]]
|
||||
=== Registering a Mongo instance using Java based metadata
|
||||
@@ -192,7 +192,7 @@ public class AppConfig {
|
||||
----
|
||||
====
|
||||
|
||||
This approach allows you to use the standard `com.mongodb.MongoClient` instance with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.MongoClient` instance directly, the FactoryBean has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features].
|
||||
This approach allows you to use the standard `com.mongodb.MongoClient` instance with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.MongoClient` instance directly, the FactoryBean has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html[Spring's DAO support features].
|
||||
|
||||
An example of a Java based bean metadata that supports exception translation on `@Repository` annotated classes is shown below:
|
||||
|
||||
@@ -1483,7 +1483,7 @@ include::query-by-example.adoc[leveloffset=+1]
|
||||
|
||||
You can query MongoDB using Map-Reduce which is useful for batch processing, data aggregation, and for when the query language doesn't fulfill your needs.
|
||||
|
||||
Spring provides integration with MongoDB's map reduce by providing methods on MongoOperations to simplify the creation and execution of Map-Reduce operations. It can convert the results of a Map-Reduce operation to a POJO also integrates with Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/resources.html[Resource abstraction] abstraction. This will let you place your JavaScript files on the file system, classpath, http server or any other Spring Resource implementation and then reference the JavaScript resources via an easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing JavaScript code in files is often preferable to embedding them as Java strings in your code. Note that you can still pass JavaScript code as Java strings if you prefer.
|
||||
Spring provides integration with MongoDB's map reduce by providing methods on MongoOperations to simplify the creation and execution of Map-Reduce operations. It can convert the results of a Map-Reduce operation to a POJO also integrates with Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#resources[Resource abstraction] abstraction. This will let you place your JavaScript files on the file system, classpath, http server or any other Spring Resource implementation and then reference the JavaScript resources via an easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing JavaScript code in files is often preferable to embedding them as Java strings in your code. Note that you can still pass JavaScript code as Java strings if you prefer.
|
||||
|
||||
[[mongo.mapreduce.example]]
|
||||
=== Example Usage
|
||||
@@ -1633,7 +1633,7 @@ scriptOps.call("echo", "execute script via name"); <3>
|
||||
|
||||
As an alternative to using Map-Reduce to perform data aggregation, you can use the http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group[`group` operation] which feels similar to using SQL's group by query style, so it may feel more approachable vs. using Map-Reduce. Using the group operations does have some limitations, for example it is not supported in a shared environment and it returns the full result set in a single BSON object, so the result should be small, less than 10,000 keys.
|
||||
|
||||
Spring provides integration with MongoDB's group operation by providing methods on MongoOperations to simplify the creation and execution of group operations. It can convert the results of the group operation to a POJO and also integrates with Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/resources.html[Resource abstraction] abstraction. This will let you place your JavaScript files on the file system, classpath, http server or any other Spring Resource implementation and then reference the JavaScript resources via an easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing JavaScript code in files if often preferable to embedding them as Java strings in your code. Note that you can still pass JavaScript code as Java strings if you prefer.
|
||||
Spring provides integration with MongoDB's group operation by providing methods on MongoOperations to simplify the creation and execution of group operations. It can convert the results of the group operation to a POJO and also integrates with Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#resources[Resource abstraction] abstraction. This will let you place your JavaScript files on the file system, classpath, http server or any other Spring Resource implementation and then reference the JavaScript resources via an easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing JavaScript code in files if often preferable to embedding them as Java strings in your code. Note that you can still pass JavaScript code as Java strings if you prefer.
|
||||
|
||||
[[mongo.group.example]]
|
||||
=== Example Usage
|
||||
@@ -1933,7 +1933,7 @@ Have a look at an example in more context in <<mongo.aggregation.examples.exampl
|
||||
| { $not : [$a] }
|
||||
|===
|
||||
|
||||
Next to the transformations shown in <<Supported SpEL transformations>> it is possible to use standard SpEL operations like `new` to eg. create arrays and reference expressions via their name followed by the arguments to use in brackets.
|
||||
Next to the transformations shown in Supported SpEL transformations it is possible to use standard SpEL operations like `new` to eg. create arrays and reference expressions via their name followed by the arguments to use in brackets.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -2235,7 +2235,7 @@ In order to have more fine-grained control over the mapping process you can regi
|
||||
|
||||
The `MappingMongoConverter` checks to see if there are any Spring converters that can handle a specific class before attempting to map the object itself. To 'hijack' the normal mapping strategies of the `MappingMongoConverter`, perhaps for increased performance or other custom mapping needs, you first need to create an implementation of the Spring `Converter` interface and then register it with the MappingConverter.
|
||||
|
||||
NOTE: For more information on the Spring type conversion service see the reference docs http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[here].
|
||||
NOTE: For more information on the Spring type conversion service see the reference docs http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#validation[here].
|
||||
|
||||
[[mongo.custom-converters.writer]]
|
||||
=== Saving using a registered Spring Converter
|
||||
@@ -2477,7 +2477,7 @@ NOTE: Lifecycle events are only emitted for root level types. Complex types used
|
||||
|
||||
The Spring framework provides exception translation for a wide variety of database and mapping technologies. This has traditionally been for JDBC and JPA. The Spring support for MongoDB extends this feature to the MongoDB Database by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface.
|
||||
|
||||
The motivation behind mapping to Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[consistent data access exception hierarchy] is that you are then able to write portable and descriptive exception handling code without resorting to coding against http://www.mongodb.org/about/contributors/error-codes/[MongoDB error codes]. All of Spring's data access exceptions are inherited from the root `DataAccessException` class so you can be sure that you will be able to catch all database related exception within a single try-catch block. Note, that not all exceptions thrown by the MongoDB driver inherit from the MongoException class. The inner exception and message are preserved so no information is lost.
|
||||
The motivation behind mapping to Spring's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html#dao-exceptions[consistent data access exception hierarchy] is that you are then able to write portable and descriptive exception handling code without resorting to coding against MongoDB error codes. All of Spring's data access exceptions are inherited from the root `DataAccessException` class so you can be sure that you will be able to catch all database related exception within a single try-catch block. Note, that not all exceptions thrown by the MongoDB driver inherit from the MongoException class. The inner exception and message are preserved so no information is lost.
|
||||
|
||||
Some of the mappings performed by the `MongoExceptionTranslator` are: com.mongodb.Network to DataAccessResourceFailureException and `MongoException` error codes 1003, 12001, 12010, 12011, 12012 to `InvalidDataAccessApiUsageException`. Look into the implementation for more details on the mapping.
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ Spring Data's Repository abstraction is a dynamic API, mostly defined by you and
|
||||
|
||||
* `ReactiveCrudRepository`
|
||||
* `ReactiveSortingRepository`
|
||||
* `RxJava1CrudRepository`
|
||||
* `RxJava1SortingRepository`
|
||||
* `RxJava2CrudRepository`
|
||||
* `RxJava2SortingRepository`
|
||||
|
||||
Spring Data converts reactive wrapper types behind the scenes so that you can stick to your favorite composition library.
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ public class AppConfig {
|
||||
|
||||
This approach allows you to use the standard `com.mongodb.reactivestreams.client.MongoClient` API that you may already be used to using.
|
||||
|
||||
An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container using Spring's `ReactiveMongoClientFactoryBean`. As compared to instantiating a `com.mongodb.reactivestreams.client.MongoClient` instance directly, the FactoryBean approach has the added advantage of also providing the container with an `ExceptionTranslator` implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features].
|
||||
An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container using Spring's `ReactiveMongoClientFactoryBean`. As compared to instantiating a `com.mongodb.reactivestreams.client.MongoClient` instance directly, the FactoryBean approach has the added advantage of also providing the container with an `ExceptionTranslator` implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html[Spring's DAO support features].
|
||||
|
||||
An example of a Java based bean metadata that supports exception translation on `@Repository` annotated classes is shown below:
|
||||
|
||||
@@ -192,7 +192,7 @@ public class AppConfig {
|
||||
To access the `com.mongodb.reactivestreams.client.MongoClient` object created by the `ReactiveMongoClientFactoryBean` in other `@Configuration` or your own classes, just obtain the `MongoClient` from the context.
|
||||
|
||||
|
||||
[[mongo.mongo-db-factory]]
|
||||
[[mongo.reactive.mongo-db-factory]]
|
||||
=== The ReactiveMongoDatabaseFactory interface
|
||||
|
||||
While `com.mongodb.reactivestreams.client.MongoClient` is the entry point to the reactive MongoDB driver API, connecting to a specific MongoDB database instance requires additional information such as the database name. With that information you can obtain a `com.mongodb.reactivestreams.client.MongoDatabase` object and access all the functionality of a specific MongoDB database instance. Spring provides the `org.springframework.data.mongodb.core.ReactiveMongoDatabaseFactory` interface shown below to bootstrap connectivity to the database.
|
||||
@@ -252,7 +252,7 @@ public class MongoApp {
|
||||
|
||||
The use of `SimpleMongoDbFactory` is the only difference between the listing shown in the <<mongodb-reactive-getting-started,getting started section>>.
|
||||
|
||||
[[mongo.mongo-db-factory-java]]
|
||||
[[mongo.reactive.mongo-db-factory-java]]
|
||||
=== Registering a ReactiveMongoDatabaseFactory instance using Java based metadata
|
||||
|
||||
To register a `ReactiveMongoDatabaseFactory` instance with the container, you write code much like what was highlighted in the previous code listing. A simple example is shown below
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 2.0.0.RELEASE (2017-10-02)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1791 - Adapt to changed Spring Framework 5 documentation structure.
|
||||
* DATAMONGO-1787 - Add explicit automatic module name for Java 9.
|
||||
* DATAMONGO-1786 - Adapt tests to nullability enforcement in repository query methods.
|
||||
* DATAMONGO-1785 - Upgrade to OpenWebBeans 2.0.1.
|
||||
* DATAMONGO-1784 - Add support for AggregationExpression in GroupOperation.sum.
|
||||
* DATAMONGO-1782 - CyclicPropertyReferenceException on index resolution.
|
||||
* DATAMONGO-1781 - Update what's new in reference documentation.
|
||||
* DATAMONGO-1779 - Query.limit(N) on empty query is not applied.
|
||||
* DATAMONGO-1778 - Update.equals(…) fails to recognize an equal Update object.
|
||||
* DATAMONGO-1777 - Update.toString(…) does not pretty-print modifiers.
|
||||
* DATAMONGO-1776 - Release 2.0 GA (Kay).
|
||||
|
||||
|
||||
Changes in version 2.0.0.RC3 (2017-09-11)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-1774 - ReactiveMongoOperations#remove(Mono, java.lang.String) ends up in an infinite loop.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 2.0 RC3
|
||||
Spring Data MongoDB 2.0 GA
|
||||
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