Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da03eb09bf | ||
|
|
69201faf96 | ||
|
|
49081ceced | ||
|
|
f54ca2394f | ||
|
|
2703573d87 | ||
|
|
dee4f3b859 | ||
|
|
34d7a3d654 | ||
|
|
2f3e134680 | ||
|
|
b126165aeb | ||
|
|
4df2c05354 | ||
|
|
c95cb66f99 | ||
|
|
acfc20b73b | ||
|
|
ae21d277c8 | ||
|
|
faa29cb207 | ||
|
|
5a0a45adcf | ||
|
|
1aab32b10d | ||
|
|
a1b0caca42 | ||
|
|
d438ef4b7b | ||
|
|
b160d0b2d7 | ||
|
|
c8b3794108 | ||
|
|
51f06d75a7 | ||
|
|
7c122b7edd | ||
|
|
f9344d7073 | ||
|
|
d26a5ece68 | ||
|
|
89ecf998b6 | ||
|
|
d66e718807 | ||
|
|
c66fb0060d | ||
|
|
3f84177b69 | ||
|
|
f2f9784d6a | ||
|
|
e7dc0ff784 |
2
.mvn/wrapper/maven-wrapper.properties
vendored
2
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1,2 +1,2 @@
|
|||||||
#Mon Jan 30 10:48:12 CET 2023
|
#Mon Jan 30 10:47:19 CET 2023
|
||||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
|
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
|
||||||
|
|||||||
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@@ -9,7 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
triggers {
|
triggers {
|
||||||
pollSCM 'H/10 * * * *'
|
pollSCM 'H/10 * * * *'
|
||||||
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
|
upstream(upstreamProjects: "spring-data-commons/3.0.x", threshold: hudson.model.Result.SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
options {
|
options {
|
||||||
|
|||||||
12
pom.xml
12
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||||
<version>4.1.0-M2</version>
|
<version>4.0.2</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Spring Data MongoDB</name>
|
<name>Spring Data MongoDB</name>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.data.build</groupId>
|
<groupId>org.springframework.data.build</groupId>
|
||||||
<artifactId>spring-data-parent</artifactId>
|
<artifactId>spring-data-parent</artifactId>
|
||||||
<version>3.1.0-M2</version>
|
<version>3.0.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.type>multi</project.type>
|
<project.type>multi</project.type>
|
||||||
<dist.id>spring-data-mongodb</dist.id>
|
<dist.id>spring-data-mongodb</dist.id>
|
||||||
<springdata.commons>3.1.0-M2</springdata.commons>
|
<springdata.commons>3.0.2</springdata.commons>
|
||||||
<mongo>4.9.0</mongo>
|
<mongo>4.8.2</mongo>
|
||||||
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
|
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
|
||||||
<jmh.version>1.19</jmh.version>
|
<jmh.version>1.19</jmh.version>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -145,8 +145,8 @@
|
|||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-libs-milestone</id>
|
<id>spring-libs-release</id>
|
||||||
<url>https://repo.spring.io/libs-milestone</url>
|
<url>https://repo.spring.io/libs-release</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||||
<version>4.1.0-M2</version>
|
<version>4.0.2</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||||
<version>4.1.0-M2</version>
|
<version>4.0.2</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||||
<version>4.1.0-M2</version>
|
<version>4.0.2</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.data.mongodb.core;
|
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import com.mongodb.client.MongoCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for functional preparation of a {@link MongoCollection}.
|
|
||||||
*
|
|
||||||
* @author Mark Paluch
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public interface CollectionPreparer<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a preparer that always returns its input collection.
|
|
||||||
*
|
|
||||||
* @return a preparer that always returns its input collection.
|
|
||||||
*/
|
|
||||||
static <T> CollectionPreparer<T> identity() {
|
|
||||||
return it -> it;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare the {@code collection}.
|
|
||||||
*
|
|
||||||
* @param collection the collection to prepare.
|
|
||||||
* @return the prepared collection.
|
|
||||||
*/
|
|
||||||
T prepare(T collection);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed {@code CollectionPreparer} that first applies this preparer to the collection, and then applies
|
|
||||||
* the {@code after} preparer to the result. If evaluation of either function throws an exception, it is relayed to
|
|
||||||
* the caller of the composed function.
|
|
||||||
*
|
|
||||||
* @param after the collection preparer to apply after this function is applied.
|
|
||||||
* @return a composed {@code CollectionPreparer} that first applies this preparer and then applies the {@code after}
|
|
||||||
* preparer.
|
|
||||||
*/
|
|
||||||
default CollectionPreparer<T> andThen(CollectionPreparer<T> after) {
|
|
||||||
Assert.notNull(after, "After CollectionPreparer must not be null");
|
|
||||||
return c -> after.prepare(prepare(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.data.mongodb.core;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.bson.Document;
|
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
|
||||||
import com.mongodb.client.MongoCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Support class for delegate implementations to apply {@link ReadConcern} and {@link ReadPreference} settings upon
|
|
||||||
* {@link CollectionPreparer preparing a collection}.
|
|
||||||
*
|
|
||||||
* @author Mark Paluch
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware {
|
|
||||||
|
|
||||||
private final List<Object> sources;
|
|
||||||
|
|
||||||
private CollectionPreparerSupport(List<Object> sources) {
|
|
||||||
this.sources = sources;
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> T doPrepare(T collection, Function<T, ReadConcern> concernAccessor, BiFunction<T, ReadConcern, T> concernFunction,
|
|
||||||
Function<T, ReadPreference> preferenceAccessor, BiFunction<T, ReadPreference, T> preferenceFunction) {
|
|
||||||
|
|
||||||
T collectionToUse = collection;
|
|
||||||
|
|
||||||
for (Object source : sources) {
|
|
||||||
if (source instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
|
||||||
|
|
||||||
ReadConcern concern = rca.getReadConcern();
|
|
||||||
if (concernAccessor.apply(collectionToUse) != concern) {
|
|
||||||
collectionToUse = concernFunction.apply(collectionToUse, concern);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object source : sources) {
|
|
||||||
if (source instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
|
||||||
|
|
||||||
ReadPreference preference = rpa.getReadPreference();
|
|
||||||
if (preferenceAccessor.apply(collectionToUse) != preference) {
|
|
||||||
collectionToUse = preferenceFunction.apply(collectionToUse, preference);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return collectionToUse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadConcern() {
|
|
||||||
|
|
||||||
for (Object aware : sources) {
|
|
||||||
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadConcern getReadConcern() {
|
|
||||||
|
|
||||||
for (Object aware : sources) {
|
|
||||||
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
|
||||||
return rca.getReadConcern();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadPreference() {
|
|
||||||
|
|
||||||
for (Object aware : sources) {
|
|
||||||
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadPreference getReadPreference() {
|
|
||||||
|
|
||||||
for (Object aware : sources) {
|
|
||||||
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
|
||||||
return rpa.getReadPreference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class CollectionPreparerDelegate extends CollectionPreparerSupport
|
|
||||||
implements CollectionPreparer<MongoCollection<Document>> {
|
|
||||||
|
|
||||||
private CollectionPreparerDelegate(List<Object> sources) {
|
|
||||||
super(sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CollectionPreparerDelegate of(ReadPreferenceAware... awares) {
|
|
||||||
return of((Object[]) awares);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CollectionPreparerDelegate of(Object... mixedAwares) {
|
|
||||||
|
|
||||||
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
|
|
||||||
return (CollectionPreparerDelegate) mixedAwares[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CollectionPreparerDelegate(Arrays.asList(mixedAwares));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MongoCollection<Document> prepare(MongoCollection<Document> collection) {
|
|
||||||
return doPrepare(collection, MongoCollection::getReadConcern, MongoCollection::withReadConcern,
|
|
||||||
MongoCollection::getReadPreference, MongoCollection::withReadPreference);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ReactiveCollectionPreparerDelegate extends CollectionPreparerSupport
|
|
||||||
implements CollectionPreparer<com.mongodb.reactivestreams.client.MongoCollection<Document>> {
|
|
||||||
|
|
||||||
private ReactiveCollectionPreparerDelegate(List<Object> sources) {
|
|
||||||
super(sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReactiveCollectionPreparerDelegate of(ReadPreferenceAware... awares) {
|
|
||||||
return of((Object[]) awares);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReactiveCollectionPreparerDelegate of(Object... mixedAwares) {
|
|
||||||
|
|
||||||
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
|
|
||||||
return (ReactiveCollectionPreparerDelegate) mixedAwares[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReactiveCollectionPreparerDelegate(Arrays.asList(mixedAwares));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public com.mongodb.reactivestreams.client.MongoCollection<Document> prepare(
|
|
||||||
com.mongodb.reactivestreams.client.MongoCollection<Document> collection) {
|
|
||||||
return doPrepare(collection, //
|
|
||||||
com.mongodb.reactivestreams.client.MongoCollection::getReadConcern,
|
|
||||||
com.mongodb.reactivestreams.client.MongoCollection::withReadConcern,
|
|
||||||
com.mongodb.reactivestreams.client.MongoCollection::getReadPreference,
|
|
||||||
com.mongodb.reactivestreams.client.MongoCollection::withReadPreference);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ import java.util.Optional;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
|
||||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
@@ -167,8 +168,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
|
|||||||
Document queryObject = query.getQueryObject();
|
Document queryObject = query.getQueryObject();
|
||||||
Document fieldsObject = query.getFieldsObject();
|
Document fieldsObject = query.getFieldsObject();
|
||||||
|
|
||||||
return template.doFind(template.createDelegate(query), getCollectionName(), queryObject, fieldsObject, domainType,
|
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||||
returnType,
|
|
||||||
getCursorPreparer(query, preparer));
|
getCursorPreparer(query, preparer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.data.mongodb.core;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.bson.conversions.Bson;
|
|
||||||
import org.springframework.data.mongodb.CodecRegistryProvider;
|
|
||||||
import org.springframework.data.mongodb.util.BsonUtils;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function object to apply a query hint. Can be an index name or a BSON document.
|
|
||||||
*
|
|
||||||
* @author Mark Paluch
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
class HintFunction {
|
|
||||||
|
|
||||||
private static final HintFunction EMPTY = new HintFunction(null);
|
|
||||||
|
|
||||||
private final @Nullable Object hint;
|
|
||||||
|
|
||||||
private HintFunction(@Nullable Object hint) {
|
|
||||||
this.hint = hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an empty hint function.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
static HintFunction empty() {
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link HintFunction} from a {@link Bson document} or {@link String index name}.
|
|
||||||
*
|
|
||||||
* @param hint
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
static HintFunction from(@Nullable Object hint) {
|
|
||||||
return new HintFunction(hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether a hint is present.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean isPresent() {
|
|
||||||
return (hint instanceof String hintString && StringUtils.hasText(hintString)) || hint instanceof Bson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the hint to consumers depending on the hint format.
|
|
||||||
*
|
|
||||||
* @param registryProvider
|
|
||||||
* @param stringConsumer
|
|
||||||
* @param bsonConsumer
|
|
||||||
* @return
|
|
||||||
* @param <R>
|
|
||||||
*/
|
|
||||||
public <R> R apply(@Nullable CodecRegistryProvider registryProvider, Function<String, R> stringConsumer,
|
|
||||||
Function<Bson, R> bsonConsumer) {
|
|
||||||
|
|
||||||
if (!isPresent()) {
|
|
||||||
throw new IllegalStateException("No hint present");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hint instanceof Bson bson) {
|
|
||||||
return bsonConsumer.apply(bson);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hint instanceof String hintString) {
|
|
||||||
|
|
||||||
if (BsonUtils.isJsonDocument(hintString)) {
|
|
||||||
return bsonConsumer.apply(BsonUtils.parse(hintString, registryProvider));
|
|
||||||
}
|
|
||||||
return stringConsumer.apply(hintString);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unable to read hint of type %s".formatted(hint != null ? hint.getClass() : "null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,7 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.bson.conversions.Bson;
|
import org.bson.conversions.Bson;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
@@ -55,7 +56,6 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
|
|||||||
import org.springframework.data.mongodb.MongoDatabaseUtils;
|
import org.springframework.data.mongodb.MongoDatabaseUtils;
|
||||||
import org.springframework.data.mongodb.SessionSynchronization;
|
import org.springframework.data.mongodb.SessionSynchronization;
|
||||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.CollectionPreparerDelegate;
|
|
||||||
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
|
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
|
||||||
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
||||||
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
||||||
@@ -67,11 +67,19 @@ import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
|
|||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.Builder;
|
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||||
import org.springframework.data.mongodb.core.convert.*;
|
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||||
|
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||||
|
import org.springframework.data.mongodb.core.convert.JsonSchemaMapper;
|
||||||
|
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||||
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
|
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||||
|
import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
|
||||||
|
import org.springframework.data.mongodb.core.convert.MongoWriter;
|
||||||
|
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||||
|
import org.springframework.data.mongodb.core.convert.UpdateMapper;
|
||||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||||
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
|
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
|
||||||
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
|
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
|
||||||
@@ -85,12 +93,14 @@ import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
|
|||||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Collation;
|
import org.springframework.data.mongodb.core.query.Collation;
|
||||||
import org.springframework.data.mongodb.core.query.Meta;
|
import org.springframework.data.mongodb.core.query.Meta;
|
||||||
|
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
||||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
||||||
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
|
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
|
||||||
import org.springframework.data.mongodb.core.timeseries.Granularity;
|
import org.springframework.data.mongodb.core.timeseries.Granularity;
|
||||||
import org.springframework.data.mongodb.core.validation.Validator;
|
import org.springframework.data.mongodb.core.validation.Validator;
|
||||||
|
import org.springframework.data.mongodb.util.BsonUtils;
|
||||||
import org.springframework.data.projection.EntityProjection;
|
import org.springframework.data.projection.EntityProjection;
|
||||||
import org.springframework.data.util.CloseableIterator;
|
import org.springframework.data.util.CloseableIterator;
|
||||||
import org.springframework.data.util.Optionals;
|
import org.springframework.data.util.Optionals;
|
||||||
@@ -107,29 +117,22 @@ import com.mongodb.ClientSessionOptions;
|
|||||||
import com.mongodb.MongoException;
|
import com.mongodb.MongoException;
|
||||||
import com.mongodb.ReadPreference;
|
import com.mongodb.ReadPreference;
|
||||||
import com.mongodb.WriteConcern;
|
import com.mongodb.WriteConcern;
|
||||||
import com.mongodb.client.*;
|
import com.mongodb.client.AggregateIterable;
|
||||||
|
import com.mongodb.client.ClientSession;
|
||||||
|
import com.mongodb.client.DistinctIterable;
|
||||||
|
import com.mongodb.client.FindIterable;
|
||||||
|
import com.mongodb.client.MapReduceIterable;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoCursor;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import com.mongodb.client.MongoIterable;
|
||||||
import com.mongodb.client.model.*;
|
import com.mongodb.client.model.*;
|
||||||
import com.mongodb.client.result.DeleteResult;
|
import com.mongodb.client.result.DeleteResult;
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary implementation of {@link MongoOperations}. It simplifies the use of imperative MongoDB usage and helps to
|
* Primary implementation of {@link MongoOperations}.
|
||||||
* avoid common errors. It executes core MongoDB workflow, leaving application code to provide {@link Document} and
|
|
||||||
* extract results. This class executes BSON queries or updates, initiating iteration over {@link FindIterable} and
|
|
||||||
* catching MongoDB exceptions and translating them to the generic, more informative exception hierarchy defined in the
|
|
||||||
* org.springframework.dao package. Can be used within a service implementation via direct instantiation with a
|
|
||||||
* {@link MongoDatabaseFactory} reference, or get prepared in an application context and given to services as bean
|
|
||||||
* reference.
|
|
||||||
* <p>
|
|
||||||
* Note: The {@link MongoDatabaseFactory} should always be configured as a bean in the application context, in the first
|
|
||||||
* case given to the service directly, in the second case to the prepared template.
|
|
||||||
* <h3>{@link ReadPreference} and {@link com.mongodb.ReadConcern}</h3>
|
|
||||||
* <p>
|
|
||||||
* {@code ReadPreference} and {@code ReadConcern} are generally considered from {@link Query} and
|
|
||||||
* {@link AggregationOptions} objects for the action to be executed on a particular {@link MongoCollection}.
|
|
||||||
* <p>
|
|
||||||
* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to
|
|
||||||
* generally apply a {@link ReadPreference}.
|
|
||||||
*
|
*
|
||||||
* @author Thomas Risberg
|
* @author Thomas Risberg
|
||||||
* @author Graeme Rocher
|
* @author Graeme Rocher
|
||||||
@@ -158,8 +161,7 @@ import com.mongodb.client.result.UpdateResult;
|
|||||||
* @author Bartłomiej Mazur
|
* @author Bartłomiej Mazur
|
||||||
* @author Michael Krog
|
* @author Michael Krog
|
||||||
*/
|
*/
|
||||||
public class MongoTemplate
|
public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
|
||||||
implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, ReadPreferenceAware {
|
|
||||||
|
|
||||||
private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
|
private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
|
||||||
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
|
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
|
||||||
@@ -311,16 +313,6 @@ public class MongoTemplate
|
|||||||
this.readPreference = readPreference;
|
this.readPreference = readPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadPreference() {
|
|
||||||
return this.readPreference != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadPreference getReadPreference() {
|
|
||||||
return this.readPreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure whether lifecycle events such as {@link AfterLoadEvent}, {@link BeforeSaveEvent}, etc. should be
|
* Configure whether lifecycle events such as {@link AfterLoadEvent}, {@link BeforeSaveEvent}, etc. should be
|
||||||
* published or whether emission should be suppressed. Enabled by default.
|
* published or whether emission should be suppressed. Enabled by default.
|
||||||
@@ -391,10 +383,10 @@ public class MongoTemplate
|
|||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
|
||||||
this.countExecution = (collectionPreparer, collectionName, filter, options) -> {
|
this.countExecution = (collectionName, filter, options) -> {
|
||||||
|
|
||||||
if (!estimationFilter.test(filter, options)) {
|
if (!estimationFilter.test(filter, options)) {
|
||||||
return doExactCount(collectionPreparer, collectionName, filter, options);
|
return doExactCount(collectionName, filter, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
EstimatedDocumentCountOptions estimatedDocumentCountOptions = new EstimatedDocumentCountOptions();
|
EstimatedDocumentCountOptions estimatedDocumentCountOptions = new EstimatedDocumentCountOptions();
|
||||||
@@ -402,7 +394,7 @@ public class MongoTemplate
|
|||||||
estimatedDocumentCountOptions.maxTime(options.getMaxTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
estimatedDocumentCountOptions.maxTime(options.getMaxTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doEstimatedCount(collectionPreparer, collectionName, estimatedDocumentCountOptions);
|
return doEstimatedCount(collectionName, estimatedDocumentCountOptions);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.countExecution = this::doExactCount;
|
this.countExecution = this::doExactCount;
|
||||||
@@ -471,9 +463,8 @@ public class MongoTemplate
|
|||||||
Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
|
Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
|
||||||
Document mappedFields = queryContext.getMappedFields(persistentEntity, projection);
|
Document mappedFields = queryContext.getMappedFields(persistentEntity, projection);
|
||||||
|
|
||||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
|
||||||
FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
|
FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
|
||||||
col -> readPreference.prepare(col).find(mappedQuery, Document.class).projection(mappedFields));
|
col -> col.find(mappedQuery, Document.class).projection(mappedFields));
|
||||||
|
|
||||||
return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator,
|
return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator,
|
||||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName)).stream();
|
new ProjectingReadCallback<>(mongoConverter, projection, collectionName)).stream();
|
||||||
@@ -546,7 +537,7 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
|
serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executeQueryInternal(new FindCallback(createDelegate(query), queryObject, fieldsObject, null),
|
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject, null),
|
||||||
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, documentCallbackHandler, collectionName);
|
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, documentCallbackHandler, collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,8 +635,7 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MongoCollection<Document> createView(String name, Class<?> source, AggregationPipeline pipeline,
|
public MongoCollection<Document> createView(String name, Class<?> source, AggregationPipeline pipeline, @Nullable ViewOptions options) {
|
||||||
@Nullable ViewOptions options) {
|
|
||||||
|
|
||||||
return createView(name, getCollectionName(source),
|
return createView(name, getCollectionName(source),
|
||||||
queryOperations.createAggregation(Aggregation.newAggregation(source, pipeline.getOperations()), source),
|
queryOperations.createAggregation(Aggregation.newAggregation(source, pipeline.getOperations()), source),
|
||||||
@@ -653,8 +643,7 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MongoCollection<Document> createView(String name, String source, AggregationPipeline pipeline,
|
public MongoCollection<Document> createView(String name, String source, AggregationPipeline pipeline, @Nullable ViewOptions options) {
|
||||||
@Nullable ViewOptions options) {
|
|
||||||
|
|
||||||
return createView(name, source,
|
return createView(name, source,
|
||||||
queryOperations.createAggregation(Aggregation.newAggregation(pipeline.getOperations()), (Class<?>) null),
|
queryOperations.createAggregation(Aggregation.newAggregation(pipeline.getOperations()), (Class<?>) null),
|
||||||
@@ -666,8 +655,7 @@ public class MongoTemplate
|
|||||||
return doCreateView(name, source, aggregation.getAggregationPipeline(), options);
|
return doCreateView(name, source, aggregation.getAggregationPipeline(), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MongoCollection<Document> doCreateView(String name, String source, List<Document> pipeline,
|
protected MongoCollection<Document> doCreateView(String name, String source, List<Document> pipeline, @Nullable ViewOptions options) {
|
||||||
@Nullable ViewOptions options) {
|
|
||||||
|
|
||||||
CreateViewOptions viewOptions = new CreateViewOptions();
|
CreateViewOptions viewOptions = new CreateViewOptions();
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
@@ -794,7 +782,7 @@ public class MongoTemplate
|
|||||||
|
|
||||||
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
||||||
|
|
||||||
return doFindOne(collectionName, createDelegate(query), query.getQueryObject(), query.getFieldsObject(),
|
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||||
new QueryCursorPreparer(query, entityClass), entityClass);
|
new QueryCursorPreparer(query, entityClass), entityClass);
|
||||||
} else {
|
} else {
|
||||||
query.limit(1);
|
query.limit(1);
|
||||||
@@ -826,7 +814,7 @@ public class MongoTemplate
|
|||||||
Document mappedQuery = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
Document mappedQuery = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
||||||
|
|
||||||
return execute(collectionName,
|
return execute(collectionName,
|
||||||
new ExistsCallback(createDelegate(query), mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
|
new ExistsCallback(mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find methods that take a Query to express the query and that return a List of objects.
|
// Find methods that take a Query to express the query and that return a List of objects.
|
||||||
@@ -843,7 +831,7 @@ public class MongoTemplate
|
|||||||
Assert.notNull(collectionName, "CollectionName must not be null");
|
Assert.notNull(collectionName, "CollectionName must not be null");
|
||||||
Assert.notNull(entityClass, "EntityClass must not be null");
|
Assert.notNull(entityClass, "EntityClass must not be null");
|
||||||
|
|
||||||
return doFind(collectionName, createDelegate(query), query.getQueryObject(), query.getFieldsObject(), entityClass,
|
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||||
new QueryCursorPreparer(query, entityClass));
|
new QueryCursorPreparer(query, entityClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,8 +851,7 @@ public class MongoTemplate
|
|||||||
|
|
||||||
String idKey = operations.getIdPropertyName(entityClass);
|
String idKey = operations.getIdPropertyName(entityClass);
|
||||||
|
|
||||||
return doFindOne(collectionName, CollectionPreparer.identity(), new Document(idKey, id), new Document(),
|
return doFindOne(collectionName, new Document(idKey, id), new Document(), entityClass);
|
||||||
entityClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -897,7 +884,10 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(mappedQuery), field, collectionName));
|
serializeToJsonSafely(mappedQuery), field, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
collection = createDelegate(query).prepare(collection);
|
QueryCursorPreparer preparer = new QueryCursorPreparer(query, entityClass);
|
||||||
|
if (preparer.hasReadPreference()) {
|
||||||
|
collection = collection.withReadPreference(preparer.getReadPreference());
|
||||||
|
}
|
||||||
|
|
||||||
DistinctIterable<T> iterable = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
|
DistinctIterable<T> iterable = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
|
||||||
distinctQueryContext.applyCollation(entityClass, iterable::collation);
|
distinctQueryContext.applyCollation(entityClass, iterable::collation);
|
||||||
@@ -947,18 +937,8 @@ public class MongoTemplate
|
|||||||
String collection = StringUtils.hasText(collectionName) ? collectionName : getCollectionName(domainType);
|
String collection = StringUtils.hasText(collectionName) ? collectionName : getCollectionName(domainType);
|
||||||
String distanceField = operations.nearQueryDistanceFieldName(domainType);
|
String distanceField = operations.nearQueryDistanceFieldName(domainType);
|
||||||
|
|
||||||
Builder optionsBuilder = AggregationOptions.builder().collation(near.getCollation());
|
|
||||||
|
|
||||||
if (near.hasReadPreference()) {
|
|
||||||
optionsBuilder.readPreference(near.getReadPreference());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(near.hasReadConcern()) {
|
|
||||||
optionsBuilder.readConcern(near.getReadConcern());
|
|
||||||
}
|
|
||||||
|
|
||||||
Aggregation $geoNear = TypedAggregation.newAggregation(domainType, Aggregation.geoNear(near, distanceField))
|
Aggregation $geoNear = TypedAggregation.newAggregation(domainType, Aggregation.geoNear(near, distanceField))
|
||||||
.withOptions(optionsBuilder.build());
|
.withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
|
||||||
|
|
||||||
AggregationResults<Document> results = aggregate($geoNear, collection, Document.class);
|
AggregationResults<Document> results = aggregate($geoNear, collection, Document.class);
|
||||||
EntityProjection<T, ?> projection = operations.introspectProjection(returnType, domainType);
|
EntityProjection<T, ?> projection = operations.introspectProjection(returnType, domainType);
|
||||||
@@ -1023,7 +1003,7 @@ public class MongoTemplate
|
|||||||
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doFindAndModify(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(),
|
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||||
getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,7 +1025,6 @@ public class MongoTemplate
|
|||||||
QueryContext queryContext = queryOperations.createQueryContext(query);
|
QueryContext queryContext = queryOperations.createQueryContext(query);
|
||||||
|
|
||||||
EntityProjection<T, S> projection = operations.introspectProjection(resultType, entityType);
|
EntityProjection<T, S> projection = operations.introspectProjection(resultType, entityType);
|
||||||
CollectionPreparerDelegate collectionPreparer = createDelegate(query);
|
|
||||||
Document mappedQuery = queryContext.getMappedQuery(entity);
|
Document mappedQuery = queryContext.getMappedQuery(entity);
|
||||||
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
||||||
Document mappedSort = queryContext.getMappedSort(entity);
|
Document mappedSort = queryContext.getMappedSort(entity);
|
||||||
@@ -1056,7 +1035,7 @@ public class MongoTemplate
|
|||||||
maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
|
maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
|
||||||
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
|
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
|
||||||
|
|
||||||
T saved = doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort,
|
T saved = doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
|
||||||
queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, projection);
|
queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, projection);
|
||||||
|
|
||||||
if (saved != null) {
|
if (saved != null) {
|
||||||
@@ -1084,7 +1063,7 @@ public class MongoTemplate
|
|||||||
Assert.notNull(entityClass, "EntityClass must not be null");
|
Assert.notNull(entityClass, "EntityClass must not be null");
|
||||||
Assert.notNull(collectionName, "CollectionName must not be null");
|
Assert.notNull(collectionName, "CollectionName must not be null");
|
||||||
|
|
||||||
return doFindAndRemove(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(),
|
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||||
getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
|
getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
|
||||||
entityClass);
|
entityClass);
|
||||||
}
|
}
|
||||||
@@ -1116,19 +1095,17 @@ public class MongoTemplate
|
|||||||
CountOptions options = countContext.getCountOptions(entityClass);
|
CountOptions options = countContext.getCountOptions(entityClass);
|
||||||
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
||||||
|
|
||||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
return doCount(collectionName, mappedQuery, options);
|
||||||
return doCount(readPreference, collectionName, mappedQuery, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long doCount(CollectionPreparer collectionPreparer, String collectionName, Document filter,
|
protected long doCount(String collectionName, Document filter, CountOptions options) {
|
||||||
CountOptions options) {
|
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER
|
LOGGER
|
||||||
.debug(String.format("Executing count: %s in collection: %s", serializeToJsonSafely(filter), collectionName));
|
.debug(String.format("Executing count: %s in collection: %s", serializeToJsonSafely(filter), collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return countExecution.countDocuments(collectionPreparer, collectionName, filter, options);
|
return countExecution.countDocuments(collectionName, filter, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1137,13 +1114,11 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public long estimatedCount(String collectionName) {
|
public long estimatedCount(String collectionName) {
|
||||||
return doEstimatedCount(CollectionPreparerDelegate.of(this), collectionName, new EstimatedDocumentCountOptions());
|
return doEstimatedCount(collectionName, new EstimatedDocumentCountOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long doEstimatedCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected long doEstimatedCount(String collectionName, EstimatedDocumentCountOptions options) {
|
||||||
String collectionName, EstimatedDocumentCountOptions options) {
|
return execute(collectionName, collection -> collection.estimatedDocumentCount(options));
|
||||||
return execute(collectionName,
|
|
||||||
collection -> collectionPreparer.prepare(collection).estimatedDocumentCount(options));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1154,13 +1129,12 @@ public class MongoTemplate
|
|||||||
CountOptions options = countContext.getCountOptions(entityClass);
|
CountOptions options = countContext.getCountOptions(entityClass);
|
||||||
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
||||||
|
|
||||||
return doExactCount(createDelegate(query), collectionName, mappedQuery, options);
|
return doExactCount(collectionName, mappedQuery, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long doExactCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
protected long doExactCount(String collectionName, Document filter, CountOptions options) {
|
||||||
Document filter, CountOptions options) {
|
return execute(collectionName,
|
||||||
return execute(collectionName, collection -> collectionPreparer.prepare(collection)
|
collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
|
||||||
.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean countCanBeEstimated(Document filter, CountOptions options) {
|
protected boolean countCanBeEstimated(Document filter, CountOptions options) {
|
||||||
@@ -1220,8 +1194,8 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
||||||
|
|
||||||
if (this.readPreference != null && this.readPreference != collection.getReadPreference()) {
|
if (this.readPreference != null) {
|
||||||
return collection.withReadPreference(readPreference);
|
collection = collection.withReadPreference(readPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
@@ -1797,7 +1771,7 @@ public class MongoTemplate
|
|||||||
@Override
|
@Override
|
||||||
public <T> List<T> findAll(Class<T> entityClass, String collectionName) {
|
public <T> List<T> findAll(Class<T> entityClass, String collectionName) {
|
||||||
return executeFindMultiInternal(
|
return executeFindMultiInternal(
|
||||||
new FindCallback(CollectionPreparer.identity(), new Document(), new Document(),
|
new FindCallback(new Document(), new Document(),
|
||||||
operations.forType(entityClass).getCollation().map(Collation::toMongoCollation).orElse(null)),
|
operations.forType(entityClass).getCollation().map(Collation::toMongoCollation).orElse(null)),
|
||||||
CursorPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
CursorPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
||||||
collectionName);
|
collectionName);
|
||||||
@@ -1855,9 +1829,7 @@ public class MongoTemplate
|
|||||||
|
|
||||||
String mapFunc = replaceWithResourceIfNecessary(mapFunction);
|
String mapFunc = replaceWithResourceIfNecessary(mapFunction);
|
||||||
String reduceFunc = replaceWithResourceIfNecessary(reduceFunction);
|
String reduceFunc = replaceWithResourceIfNecessary(reduceFunction);
|
||||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
MongoCollection<Document> inputCollection = getAndPrepareCollection(doGetDatabase(), inputCollectionName);
|
||||||
MongoCollection<Document> inputCollection = readPreference
|
|
||||||
.prepare(getAndPrepareCollection(doGetDatabase(), inputCollectionName));
|
|
||||||
|
|
||||||
// MapReduceOp
|
// MapReduceOp
|
||||||
MapReduceIterable<Document> mapReduce = inputCollection.mapReduce(mapFunc, reduceFunc, Document.class);
|
MapReduceIterable<Document> mapReduce = inputCollection.mapReduce(mapFunc, reduceFunc, Document.class);
|
||||||
@@ -2022,9 +1994,6 @@ public class MongoTemplate
|
|||||||
if (!CollectionUtils.isEmpty(result)) {
|
if (!CollectionUtils.isEmpty(result)) {
|
||||||
|
|
||||||
Query byIdInQuery = operations.getByIdInQuery(result);
|
Query byIdInQuery = operations.getByIdInQuery(result);
|
||||||
if (query.hasReadPreference()) {
|
|
||||||
byIdInQuery.withReadPreference(query.getReadPreference());
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(byIdInQuery, entityClass, collectionName);
|
remove(byIdInQuery, entityClass, collectionName);
|
||||||
}
|
}
|
||||||
@@ -2080,7 +2049,7 @@ public class MongoTemplate
|
|||||||
return execute(collectionName, collection -> {
|
return execute(collectionName, collection -> {
|
||||||
|
|
||||||
List<Document> rawResult = new ArrayList<>();
|
List<Document> rawResult = new ArrayList<>();
|
||||||
CollectionPreparerDelegate delegate = CollectionPreparerDelegate.of(options);
|
|
||||||
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation<?>) aggregation).getInputType()
|
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation<?>) aggregation).getInputType()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -2088,7 +2057,7 @@ public class MongoTemplate
|
|||||||
() -> operations.forType(domainType) //
|
() -> operations.forType(domainType) //
|
||||||
.getCollation());
|
.getCollation());
|
||||||
|
|
||||||
AggregateIterable<Document> aggregateIterable = delegate.prepare(collection).aggregate(pipeline, Document.class) //
|
AggregateIterable<Document> aggregateIterable = collection.aggregate(pipeline, Document.class) //
|
||||||
.collation(collation.map(Collation::toMongoCollation).orElse(null)) //
|
.collation(collation.map(Collation::toMongoCollation).orElse(null)) //
|
||||||
.allowDiskUse(options.isAllowDiskUse());
|
.allowDiskUse(options.isAllowDiskUse());
|
||||||
|
|
||||||
@@ -2097,10 +2066,7 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
options.getComment().ifPresent(aggregateIterable::comment);
|
options.getComment().ifPresent(aggregateIterable::comment);
|
||||||
HintFunction hintFunction = options.getHintObject().map(HintFunction::from).orElseGet(HintFunction::empty);
|
options.getHint().ifPresent(aggregateIterable::hint);
|
||||||
if (hintFunction.isPresent()) {
|
|
||||||
aggregateIterable = hintFunction.apply(mongoDbFactory, aggregateIterable::hintString, aggregateIterable::hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.hasExecutionTimeLimit()) {
|
if (options.hasExecutionTimeLimit()) {
|
||||||
aggregateIterable = aggregateIterable.maxTime(options.getMaxTime().toMillis(), TimeUnit.MILLISECONDS);
|
aggregateIterable = aggregateIterable.maxTime(options.getMaxTime().toMillis(), TimeUnit.MILLISECONDS);
|
||||||
@@ -2151,9 +2117,7 @@ public class MongoTemplate
|
|||||||
|
|
||||||
return execute(collectionName, (CollectionCallback<Stream<O>>) collection -> {
|
return execute(collectionName, (CollectionCallback<Stream<O>>) collection -> {
|
||||||
|
|
||||||
CollectionPreparerDelegate delegate = CollectionPreparerDelegate.of(options);
|
AggregateIterable<Document> cursor = collection.aggregate(pipeline, Document.class) //
|
||||||
|
|
||||||
AggregateIterable<Document> cursor = delegate.prepare(collection).aggregate(pipeline, Document.class) //
|
|
||||||
.allowDiskUse(options.isAllowDiskUse());
|
.allowDiskUse(options.isAllowDiskUse());
|
||||||
|
|
||||||
if (options.getCursorBatchSize() != null) {
|
if (options.getCursorBatchSize() != null) {
|
||||||
@@ -2161,10 +2125,7 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
options.getComment().ifPresent(cursor::comment);
|
options.getComment().ifPresent(cursor::comment);
|
||||||
HintFunction hintFunction = options.getHintObject().map(HintFunction::from).orElseGet(HintFunction::empty);
|
options.getHint().ifPresent(cursor::hint);
|
||||||
if (options.getHintObject().isPresent()) {
|
|
||||||
cursor = hintFunction.apply(mongoDbFactory, cursor::hintString, cursor::hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation) aggregation).getInputType()
|
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation) aggregation).getInputType()
|
||||||
: null;
|
: null;
|
||||||
@@ -2394,16 +2355,14 @@ public class MongoTemplate
|
|||||||
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
* @return the converted object or {@literal null} if none exists.
|
* @return the converted object or {@literal null} if none exists.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> T doFindOne(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||||
Document query, Document fields, Class<T> entityClass) {
|
return doFindOne(collectionName, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
|
||||||
return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2411,18 +2370,17 @@ public class MongoTemplate
|
|||||||
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param preparer the preparer used to modify the cursor on execution.
|
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
|
* @param preparer the preparer used to modify the cursor on execution.
|
||||||
* @return the converted object or {@literal null} if none exists.
|
* @return the converted object or {@literal null} if none exists.
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> T doFindOne(String collectionName, Document query, Document fields, CursorPreparer preparer,
|
||||||
Document query, Document fields, CursorPreparer preparer, Class<T> entityClass) {
|
Class<T> entityClass) {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
|
|
||||||
@@ -2435,7 +2393,7 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(new FindOneCallback(collectionPreparer, mappedQuery, mappedFields, preparer),
|
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields, preparer),
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2444,15 +2402,13 @@ public class MongoTemplate
|
|||||||
* query document is specified as a standard Document and so is the fields specification.
|
* query document is specified as a standard Document and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from
|
* @param collectionName name of the collection to retrieve the objects from
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record
|
* @param query the query document that specifies the criteria used to find a record
|
||||||
* @param fields the document that specifies the fields to be returned
|
* @param fields the document that specifies the fields to be returned
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
* @return the List of converted objects.
|
* @return the List of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> List<T> doFind(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> List<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||||
Document query, Document fields, Class<T> entityClass) {
|
return doFind(collectionName, query, fields, entityClass, null,
|
||||||
return doFind(collectionName, collectionPreparer, query, fields, entityClass, null,
|
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2462,7 +2418,6 @@ public class MongoTemplate
|
|||||||
* specified as a standard Document and so is the fields specification.
|
* specified as a standard Document and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
@@ -2470,15 +2425,14 @@ public class MongoTemplate
|
|||||||
* (apply limits, skips and so on).
|
* (apply limits, skips and so on).
|
||||||
* @return the {@link List} of converted objects.
|
* @return the {@link List} of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> List<T> doFind(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> List<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
|
||||||
Document query, Document fields, Class<T> entityClass, CursorPreparer preparer) {
|
CursorPreparer preparer) {
|
||||||
return doFind(collectionName, collectionPreparer, query, fields, entityClass, preparer,
|
return doFind(collectionName, query, fields, entityClass, preparer,
|
||||||
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <S, T> List<T> doFind(String collectionName,
|
protected <S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
|
@Nullable CursorPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||||
Class<S> entityClass, @Nullable CursorPreparer preparer, DocumentCallback<T> objectCallback) {
|
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
|
|
||||||
@@ -2491,7 +2445,7 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields, null),
|
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields, null),
|
||||||
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, objectCallback, collectionName);
|
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, objectCallback, collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2501,8 +2455,8 @@ public class MongoTemplate
|
|||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
<S, T> List<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
<S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> sourceClass,
|
||||||
Document query, Document fields, Class<S> sourceClass, Class<T> targetClass, CursorPreparer preparer) {
|
Class<T> targetClass, CursorPreparer preparer) {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
||||||
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
||||||
@@ -2516,7 +2470,7 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields, null), preparer,
|
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields, null), preparer,
|
||||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2590,8 +2544,8 @@ public class MongoTemplate
|
|||||||
* @return the List of converted objects.
|
* @return the List of converted objects.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
protected <T> T doFindAndRemove(String collectionName, Document query, Document fields, Document sort,
|
||||||
Document fields, Document sort, @Nullable Collation collation, Class<T> entityClass) {
|
@Nullable Collation collation, Class<T> entityClass) {
|
||||||
|
|
||||||
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
||||||
|
|
||||||
@@ -2602,15 +2556,14 @@ public class MongoTemplate
|
|||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
|
|
||||||
return executeFindOneInternal(new FindAndRemoveCallback(collectionPreparer,
|
return executeFindOneInternal(
|
||||||
queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||||
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
protected <T> T doFindAndModify(String collectionName, Document query, Document fields, Document sort,
|
||||||
Document fields, Document sort, Class<T> entityClass, UpdateDefinition update,
|
Class<T> entityClass, UpdateDefinition update, @Nullable FindAndModifyOptions options) {
|
||||||
@Nullable FindAndModifyOptions options) {
|
|
||||||
|
|
||||||
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
||||||
|
|
||||||
@@ -2635,7 +2588,7 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(
|
return executeFindOneInternal(
|
||||||
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
|
new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate,
|
||||||
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
||||||
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
@@ -2656,18 +2609,14 @@ public class MongoTemplate
|
|||||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
|
protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||||
Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
|
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
|
||||||
Class<?> entityType, Document replacement, FindAndReplaceOptions options, Class<T> resultType) {
|
Document replacement, FindAndReplaceOptions options, Class<T> resultType) {
|
||||||
|
|
||||||
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
||||||
|
|
||||||
return doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort, collation,
|
return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort, collation, entityType, replacement,
|
||||||
entityType, replacement, options, projection);
|
options, projection);
|
||||||
}
|
|
||||||
|
|
||||||
CollectionPreparerDelegate createDelegate(Query query) {
|
|
||||||
return CollectionPreparerDelegate.of(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2687,9 +2636,9 @@ public class MongoTemplate
|
|||||||
* @since 3.4
|
* @since 3.4
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
|
private <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||||
Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
|
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
|
||||||
Class<?> entityType, Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER
|
LOGGER
|
||||||
@@ -2700,9 +2649,9 @@ public class MongoTemplate
|
|||||||
serializeToJsonSafely(mappedSort), entityType, serializeToJsonSafely(replacement), collectionName));
|
serializeToJsonSafely(mappedSort), entityType, serializeToJsonSafely(replacement), collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields, mappedSort,
|
return executeFindOneInternal(
|
||||||
replacement, collation, options), new ProjectingReadCallback<>(mongoConverter, projection, collectionName),
|
new FindAndReplaceCallback(mappedQuery, mappedFields, mappedSort, replacement, collation, options),
|
||||||
collectionName);
|
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2872,15 +2821,12 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
private static class FindOneCallback implements CollectionCallback<Document> {
|
private static class FindOneCallback implements CollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Optional<Document> fields;
|
private final Optional<Document> fields;
|
||||||
private final CursorPreparer cursorPreparer;
|
private final CursorPreparer cursorPreparer;
|
||||||
|
|
||||||
FindOneCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
|
FindOneCallback(Document query, Document fields, CursorPreparer preparer) {
|
||||||
CursorPreparer preparer) {
|
|
||||||
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = Optional.of(fields).filter(it -> !ObjectUtils.isEmpty(fields));
|
this.fields = Optional.of(fields).filter(it -> !ObjectUtils.isEmpty(fields));
|
||||||
this.cursorPreparer = preparer;
|
this.cursorPreparer = preparer;
|
||||||
@@ -2889,8 +2835,7 @@ public class MongoTemplate
|
|||||||
@Override
|
@Override
|
||||||
public Document doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
public Document doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||||
|
|
||||||
FindIterable<Document> iterable = cursorPreparer.initiateFind(collection,
|
FindIterable<Document> iterable = cursorPreparer.initiateFind(collection, col -> col.find(query, Document.class));
|
||||||
col -> collectionPreparer.prepare(col).find(query, Document.class));
|
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
|
||||||
@@ -2917,18 +2862,15 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
private static class FindCallback implements CollectionCallback<FindIterable<Document>> {
|
private static class FindCallback implements CollectionCallback<FindIterable<Document>> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||||
|
|
||||||
public FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
public FindCallback(Document query, Document fields, @Nullable com.mongodb.client.model.Collation collation) {
|
||||||
Document fields, @Nullable com.mongodb.client.model.Collation collation) {
|
|
||||||
|
|
||||||
Assert.notNull(query, "Query must not be null");
|
Assert.notNull(query, "Query must not be null");
|
||||||
Assert.notNull(fields, "Fields must not be null");
|
Assert.notNull(fields, "Fields must not be null");
|
||||||
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.collation = collation;
|
this.collation = collation;
|
||||||
@@ -2938,8 +2880,7 @@ public class MongoTemplate
|
|||||||
public FindIterable<Document> doInCollection(MongoCollection<Document> collection)
|
public FindIterable<Document> doInCollection(MongoCollection<Document> collection)
|
||||||
throws MongoException, DataAccessException {
|
throws MongoException, DataAccessException {
|
||||||
|
|
||||||
FindIterable<Document> findIterable = collectionPreparer.prepare(collection).find(query, Document.class)
|
FindIterable<Document> findIterable = collection.find(query, Document.class).projection(fields);
|
||||||
.projection(fields);
|
|
||||||
|
|
||||||
if (collation != null) {
|
if (collation != null) {
|
||||||
findIterable = findIterable.collation(collation);
|
findIterable = findIterable.collation(collation);
|
||||||
@@ -2957,14 +2898,11 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
private class ExistsCallback implements CollectionCallback<Boolean> {
|
private class ExistsCallback implements CollectionCallback<Boolean> {
|
||||||
|
|
||||||
private final CollectionPreparer collectionPreparer;
|
|
||||||
private final Document mappedQuery;
|
private final Document mappedQuery;
|
||||||
private final com.mongodb.client.model.Collation collation;
|
private final com.mongodb.client.model.Collation collation;
|
||||||
|
|
||||||
ExistsCallback(CollectionPreparer collectionPreparer, Document mappedQuery,
|
ExistsCallback(Document mappedQuery, com.mongodb.client.model.Collation collation) {
|
||||||
com.mongodb.client.model.Collation collation) {
|
|
||||||
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.mappedQuery = mappedQuery;
|
this.mappedQuery = mappedQuery;
|
||||||
this.collation = collation;
|
this.collation = collation;
|
||||||
}
|
}
|
||||||
@@ -2972,7 +2910,7 @@ public class MongoTemplate
|
|||||||
@Override
|
@Override
|
||||||
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||||
|
|
||||||
return doCount(collectionPreparer, collection.getNamespace().getCollectionName(), mappedQuery,
|
return doCount(collection.getNamespace().getCollectionName(), mappedQuery,
|
||||||
new CountOptions().limit(1).collation(collation)) > 0;
|
new CountOptions().limit(1).collation(collation)) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2985,15 +2923,12 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
private static class FindAndRemoveCallback implements CollectionCallback<Document> {
|
private static class FindAndRemoveCallback implements CollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
private final Optional<Collation> collation;
|
private final Optional<Collation> collation;
|
||||||
|
|
||||||
FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
|
||||||
Document fields, Document sort, @Nullable Collation collation) {
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
@@ -3007,13 +2942,12 @@ public class MongoTemplate
|
|||||||
FindOneAndDeleteOptions opts = new FindOneAndDeleteOptions().sort(sort).projection(fields);
|
FindOneAndDeleteOptions opts = new FindOneAndDeleteOptions().sort(sort).projection(fields);
|
||||||
collation.map(Collation::toMongoCollation).ifPresent(opts::collation);
|
collation.map(Collation::toMongoCollation).ifPresent(opts::collation);
|
||||||
|
|
||||||
return collectionPreparer.prepare(collection).findOneAndDelete(query, opts);
|
return collection.findOneAndDelete(query, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FindAndModifyCallback implements CollectionCallback<Document> {
|
private static class FindAndModifyCallback implements CollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
@@ -3021,10 +2955,9 @@ public class MongoTemplate
|
|||||||
private final List<Document> arrayFilters;
|
private final List<Document> arrayFilters;
|
||||||
private final FindAndModifyOptions options;
|
private final FindAndModifyOptions options;
|
||||||
|
|
||||||
FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
|
||||||
Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
|
FindAndModifyOptions options) {
|
||||||
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
@@ -3053,9 +2986,9 @@ public class MongoTemplate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (update instanceof Document) {
|
if (update instanceof Document) {
|
||||||
return collectionPreparer.prepare(collection).findOneAndUpdate(query, (Document) update, opts);
|
return collection.findOneAndUpdate(query, (Document) update, opts);
|
||||||
} else if (update instanceof List) {
|
} else if (update instanceof List) {
|
||||||
return collectionPreparer.prepare(collection).findOneAndUpdate(query, (List<Document>) update, opts);
|
return collection.findOneAndUpdate(query, (List<Document>) update, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException(String.format("Using %s is not supported in findOneAndUpdate", update));
|
throw new IllegalArgumentException(String.format("Using %s is not supported in findOneAndUpdate", update));
|
||||||
@@ -3071,7 +3004,6 @@ public class MongoTemplate
|
|||||||
*/
|
*/
|
||||||
private static class FindAndReplaceCallback implements CollectionCallback<Document> {
|
private static class FindAndReplaceCallback implements CollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
@@ -3079,10 +3011,9 @@ public class MongoTemplate
|
|||||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||||
private final FindAndReplaceOptions options;
|
private final FindAndReplaceOptions options;
|
||||||
|
|
||||||
FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndReplaceCallback(Document query, Document fields, Document sort, Document update,
|
||||||
Document fields, Document sort, Document update, @Nullable com.mongodb.client.model.Collation collation,
|
@Nullable com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
|
||||||
FindAndReplaceOptions options) {
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
@@ -3107,7 +3038,7 @@ public class MongoTemplate
|
|||||||
opts.returnDocument(ReturnDocument.AFTER);
|
opts.returnDocument(ReturnDocument.AFTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectionPreparer.prepare(collection).findOneAndReplace(query, update, opts);
|
return collection.findOneAndReplace(query, update, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3224,9 +3155,8 @@ public class MongoTemplate
|
|||||||
.ifPresent(cursorToUse::collation);
|
.ifPresent(cursorToUse::collation);
|
||||||
|
|
||||||
Meta meta = query.getMeta();
|
Meta meta = query.getMeta();
|
||||||
HintFunction hintFunction = HintFunction.from(query.getHint());
|
|
||||||
if (query.getSkip() <= 0 && query.getLimit() <= 0 && ObjectUtils.isEmpty(query.getSortObject())
|
if (query.getSkip() <= 0 && query.getLimit() <= 0 && ObjectUtils.isEmpty(query.getSortObject())
|
||||||
&& !hintFunction.isPresent() && !meta.hasValues() && !query.getCollation().isPresent()) {
|
&& !StringUtils.hasText(query.getHint()) && !meta.hasValues() && !query.getCollation().isPresent()) {
|
||||||
return cursorToUse;
|
return cursorToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3242,8 +3172,15 @@ public class MongoTemplate
|
|||||||
cursorToUse = cursorToUse.sort(sort);
|
cursorToUse = cursorToUse.sort(sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hintFunction.isPresent()) {
|
if (StringUtils.hasText(query.getHint())) {
|
||||||
cursorToUse = hintFunction.apply(mongoDbFactory, cursorToUse::hintString, cursorToUse::hint);
|
|
||||||
|
String hint = query.getHint();
|
||||||
|
|
||||||
|
if (BsonUtils.isJsonDocument(hint)) {
|
||||||
|
cursorToUse = cursorToUse.hint(BsonUtils.parse(hint, mongoDbFactory));
|
||||||
|
} else {
|
||||||
|
cursorToUse = cursorToUse.hintString(hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.hasValues()) {
|
if (meta.hasValues()) {
|
||||||
@@ -3289,6 +3226,11 @@ public class MongoTemplate
|
|||||||
return cursorToUse;
|
return cursorToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadPreference getReadPreference() {
|
||||||
|
return query.getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3474,7 +3416,6 @@ public class MongoTemplate
|
|||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface CountExecution {
|
interface CountExecution {
|
||||||
long countDocuments(CollectionPreparer collectionPreparer, String collection, Document filter,
|
long countDocuments(String collection, Document filter, CountOptions options);
|
||||||
CountOptions options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import org.springframework.data.projection.EntityProjection;
|
|||||||
import org.springframework.data.util.Lazy;
|
import org.springframework.data.util.Lazy;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.mongodb.client.model.CountOptions;
|
import com.mongodb.client.model.CountOptions;
|
||||||
import com.mongodb.client.model.DeleteOptions;
|
import com.mongodb.client.model.DeleteOptions;
|
||||||
@@ -566,11 +567,14 @@ class QueryOperations {
|
|||||||
if (query.getSkip() > 0) {
|
if (query.getSkip() > 0) {
|
||||||
options.skip((int) query.getSkip());
|
options.skip((int) query.getSkip());
|
||||||
}
|
}
|
||||||
|
if (StringUtils.hasText(query.getHint())) {
|
||||||
|
|
||||||
HintFunction hintFunction = HintFunction.from(query.getHint());
|
String hint = query.getHint();
|
||||||
|
if (BsonUtils.isJsonDocument(hint)) {
|
||||||
if (hintFunction.isPresent()) {
|
options.hint(BsonUtils.parse(hint, codecRegistryProvider));
|
||||||
options = hintFunction.apply(codecRegistryProvider, options::hintString, options::hint);
|
} else {
|
||||||
|
options.hintString(hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import reactor.core.publisher.Mono;
|
|||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate;
|
|
||||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||||
@@ -68,8 +67,8 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
|
|||||||
private final String collection;
|
private final String collection;
|
||||||
private final Query query;
|
private final Query query;
|
||||||
|
|
||||||
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType, String collection,
|
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType,
|
||||||
Query query) {
|
String collection, Query query) {
|
||||||
|
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.domainType = domainType;
|
this.domainType = domainType;
|
||||||
@@ -170,8 +169,8 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
|
|||||||
Document queryObject = query.getQueryObject();
|
Document queryObject = query.getQueryObject();
|
||||||
Document fieldsObject = query.getFieldsObject();
|
Document fieldsObject = query.getFieldsObject();
|
||||||
|
|
||||||
return template.doFind(getCollectionName(), ReactiveCollectionPreparerDelegate.of(query), queryObject,
|
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||||
fieldsObject, domainType, returnType, preparer != null ? preparer : getCursorPreparer(query));
|
preparer != null ? preparer : getCursorPreparer(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
|
|||||||
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
|
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
|
||||||
import org.springframework.data.mongodb.ReactiveMongoDatabaseUtils;
|
import org.springframework.data.mongodb.ReactiveMongoDatabaseUtils;
|
||||||
import org.springframework.data.mongodb.SessionSynchronization;
|
import org.springframework.data.mongodb.SessionSynchronization;
|
||||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate;
|
|
||||||
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
||||||
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
||||||
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
|
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
|
||||||
@@ -81,7 +80,6 @@ import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
|
|||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.Builder;
|
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
||||||
import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
|
import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
|
||||||
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
|
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
|
||||||
@@ -107,10 +105,12 @@ import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
|||||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Collation;
|
import org.springframework.data.mongodb.core.query.Collation;
|
||||||
import org.springframework.data.mongodb.core.query.Meta;
|
import org.springframework.data.mongodb.core.query.Meta;
|
||||||
|
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
||||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
||||||
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
|
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
|
||||||
|
import org.springframework.data.mongodb.util.BsonUtils;
|
||||||
import org.springframework.data.projection.EntityProjection;
|
import org.springframework.data.projection.EntityProjection;
|
||||||
import org.springframework.data.util.Optionals;
|
import org.springframework.data.util.Optionals;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
@@ -148,18 +148,9 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
|
|||||||
* extract results. This class executes BSON queries or updates, initiating iteration over {@link FindPublisher} and
|
* extract results. This class executes BSON queries or updates, initiating iteration over {@link FindPublisher} and
|
||||||
* catching MongoDB exceptions and translating them to the generic, more informative exception hierarchy defined in the
|
* catching MongoDB exceptions and translating them to the generic, more informative exception hierarchy defined in the
|
||||||
* org.springframework.dao package. Can be used within a service implementation via direct instantiation with a
|
* org.springframework.dao package. Can be used within a service implementation via direct instantiation with a
|
||||||
* {@link ReactiveMongoDatabaseFactory} reference, or get prepared in an application context and given to services as
|
* {@link SimpleReactiveMongoDatabaseFactory} reference, or get prepared in an application context and given to services
|
||||||
* bean reference.
|
* as bean reference. Note: The {@link SimpleReactiveMongoDatabaseFactory} should always be configured as a bean in the
|
||||||
* <p>
|
* application context, in the first case given to the service directly, in the second case to the prepared template.
|
||||||
* Note: The {@link ReactiveMongoDatabaseFactory} should always be configured as a bean in the application context, in
|
|
||||||
* the first case given to the service directly, in the second case to the prepared template.
|
|
||||||
* <h3>{@link ReadPreference} and {@link com.mongodb.ReadConcern}</h3>
|
|
||||||
* <p>
|
|
||||||
* {@code ReadPreference} and {@code ReadConcern} are generally considered from {@link Query} and
|
|
||||||
* {@link AggregationOptions} objects for the action to be executed on a particular {@link MongoCollection}.
|
|
||||||
* <p>
|
|
||||||
* You can also set the default {@link #setReadPreference(ReadPreference) ReadPreference} on the template level to
|
|
||||||
* generally apply a {@link ReadPreference}.
|
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
@@ -766,8 +757,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
public <T> Mono<T> findOne(Query query, Class<T> entityClass, String collectionName) {
|
public <T> Mono<T> findOne(Query query, Class<T> entityClass, String collectionName) {
|
||||||
|
|
||||||
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
||||||
return doFindOne(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||||
query.getFieldsObject(), entityClass, new QueryFindPublisherPreparer(query, entityClass));
|
new QueryFindPublisherPreparer(query, entityClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
query.limit(1);
|
query.limit(1);
|
||||||
@@ -793,11 +784,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
return createFlux(collectionName, collection -> {
|
return createFlux(collectionName, collection -> {
|
||||||
|
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
|
||||||
QueryContext queryContext = queryOperations.createQueryContext(query);
|
QueryContext queryContext = queryOperations.createQueryContext(query);
|
||||||
Document filter = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
Document filter = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
||||||
|
|
||||||
FindPublisher<Document> findPublisher = collectionPreparer.prepare(collection).find(filter, Document.class)
|
FindPublisher<Document> findPublisher = collection.find(filter, Document.class)
|
||||||
.projection(new Document("_id", 1));
|
.projection(new Document("_id", 1));
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
@@ -822,8 +812,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
return findAll(entityClass, collectionName);
|
return findAll(entityClass, collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||||
query.getFieldsObject(), entityClass, new QueryFindPublisherPreparer(query, entityClass));
|
new QueryFindPublisherPreparer(query, entityClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -836,8 +826,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
String idKey = operations.getIdPropertyName(entityClass);
|
String idKey = operations.getIdPropertyName(entityClass);
|
||||||
|
|
||||||
return doFindOne(collectionName, CollectionPreparer.identity(), new Document(idKey, id), null, entityClass,
|
return doFindOne(collectionName, new Document(idKey, id), null, entityClass, (Collation) null);
|
||||||
(Collation) null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -862,7 +851,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
Document mappedQuery = distinctQueryContext.getMappedQuery(entity);
|
Document mappedQuery = distinctQueryContext.getMappedQuery(entity);
|
||||||
String mappedFieldName = distinctQueryContext.getMappedFieldName(entity);
|
String mappedFieldName = distinctQueryContext.getMappedFieldName(entity);
|
||||||
Class<T> mongoDriverCompatibleType = distinctQueryContext.getDriverCompatibleClass(resultClass);
|
Class<T> mongoDriverCompatibleType = distinctQueryContext.getDriverCompatibleClass(resultClass);
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
|
||||||
|
|
||||||
Flux<?> result = execute(collectionName, collection -> {
|
Flux<?> result = execute(collectionName, collection -> {
|
||||||
|
|
||||||
@@ -872,9 +860,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
FindPublisherPreparer preparer = new QueryFindPublisherPreparer(query, entityClass);
|
FindPublisherPreparer preparer = new QueryFindPublisherPreparer(query, entityClass);
|
||||||
|
if (preparer.hasReadPreference()) {
|
||||||
|
collection = collection.withReadPreference(preparer.getReadPreference());
|
||||||
|
}
|
||||||
|
|
||||||
DistinctPublisher<T> publisher = collectionPreparer.prepare(collection).distinct(mappedFieldName, mappedQuery,
|
DistinctPublisher<T> publisher = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
|
||||||
mongoDriverCompatibleType);
|
|
||||||
distinctQueryContext.applyCollation(entityClass, publisher::collation);
|
distinctQueryContext.applyCollation(entityClass, publisher::collation);
|
||||||
return publisher;
|
return publisher;
|
||||||
});
|
});
|
||||||
@@ -940,8 +930,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
boolean isOutOrMerge, AggregationOptions options, ReadDocumentCallback<O> readCallback,
|
boolean isOutOrMerge, AggregationOptions options, ReadDocumentCallback<O> readCallback,
|
||||||
@Nullable Class<?> inputType) {
|
@Nullable Class<?> inputType) {
|
||||||
|
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(options);
|
AggregatePublisher<Document> cursor = collection.aggregate(pipeline, Document.class)
|
||||||
AggregatePublisher<Document> cursor = collectionPreparer.prepare(collection).aggregate(pipeline, Document.class)
|
|
||||||
.allowDiskUse(options.isAllowDiskUse());
|
.allowDiskUse(options.isAllowDiskUse());
|
||||||
|
|
||||||
if (options.getCursorBatchSize() != null) {
|
if (options.getCursorBatchSize() != null) {
|
||||||
@@ -949,11 +938,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
options.getComment().ifPresent(cursor::comment);
|
options.getComment().ifPresent(cursor::comment);
|
||||||
|
options.getHint().ifPresent(cursor::hint);
|
||||||
HintFunction hintFunction = options.getHintObject().map(HintFunction::from).orElseGet(HintFunction::empty);
|
|
||||||
if (hintFunction.isPresent()) {
|
|
||||||
cursor = hintFunction.apply(mongoDatabaseFactory, cursor::hintString, cursor::hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optionals.firstNonEmpty(options::getCollation, () -> operations.forType(inputType).getCollation()) //
|
Optionals.firstNonEmpty(options::getCollation, () -> operations.forType(inputType).getCollation()) //
|
||||||
.map(Collation::toMongoCollation) //
|
.map(Collation::toMongoCollation) //
|
||||||
@@ -999,19 +984,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
GeoNearResultDocumentCallback<T> callback = new GeoNearResultDocumentCallback<>(distanceField,
|
GeoNearResultDocumentCallback<T> callback = new GeoNearResultDocumentCallback<>(distanceField,
|
||||||
new ProjectingReadCallback<>(mongoConverter, projection, collection), near.getMetric());
|
new ProjectingReadCallback<>(mongoConverter, projection, collection), near.getMetric());
|
||||||
|
|
||||||
Builder optionsBuilder = AggregationOptions.builder();
|
|
||||||
if (near.hasReadPreference()) {
|
|
||||||
optionsBuilder.readPreference(near.getReadPreference());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(near.hasReadConcern()) {
|
|
||||||
optionsBuilder.readConcern(near.getReadConcern());
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsBuilder.collation(near.getCollation());
|
|
||||||
|
|
||||||
Aggregation $geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, distanceField))
|
Aggregation $geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, distanceField))
|
||||||
.withOptions(optionsBuilder.build());
|
.withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
|
||||||
|
|
||||||
return aggregate($geoNear, collection, Document.class) //
|
return aggregate($geoNear, collection, Document.class) //
|
||||||
.concatMap(callback::doWith);
|
.concatMap(callback::doWith);
|
||||||
@@ -1051,8 +1025,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doFindAndModify(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||||
query.getFieldsObject(), getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1076,7 +1050,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
Document mappedQuery = queryContext.getMappedQuery(entity);
|
Document mappedQuery = queryContext.getMappedQuery(entity);
|
||||||
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
||||||
Document mappedSort = queryContext.getMappedSort(entity);
|
Document mappedSort = queryContext.getMappedSort(entity);
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
|
||||||
|
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
|
|
||||||
@@ -1094,9 +1067,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
mapped.getCollection()));
|
mapped.getCollection()));
|
||||||
}).flatMap(it -> {
|
}).flatMap(it -> {
|
||||||
|
|
||||||
Mono<T> afterFindAndReplace = doFindAndReplace(it.getCollection(), collectionPreparer, mappedQuery,
|
Mono<T> afterFindAndReplace = doFindAndReplace(it.getCollection(), mappedQuery, mappedFields, mappedSort,
|
||||||
mappedFields, mappedSort, queryContext.getCollation(entityType).orElse(null), entityType, it.getTarget(),
|
queryContext.getCollation(entityType).orElse(null), entityType, it.getTarget(), options, projection);
|
||||||
options, projection);
|
|
||||||
return afterFindAndReplace.flatMap(saved -> {
|
return afterFindAndReplace.flatMap(saved -> {
|
||||||
maybeEmitEvent(new AfterSaveEvent<>(saved, it.getTarget(), it.getCollection()));
|
maybeEmitEvent(new AfterSaveEvent<>(saved, it.getTarget(), it.getCollection()));
|
||||||
return maybeCallAfterSave(saved, it.getTarget(), it.getCollection());
|
return maybeCallAfterSave(saved, it.getTarget(), it.getCollection());
|
||||||
@@ -1114,9 +1086,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
public <T> Mono<T> findAndRemove(Query query, Class<T> entityClass, String collectionName) {
|
public <T> Mono<T> findAndRemove(Query query, Class<T> entityClass, String collectionName) {
|
||||||
|
|
||||||
operations.forType(entityClass).getCollation(query);
|
operations.forType(entityClass).getCollation(query);
|
||||||
return doFindAndRemove(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||||
query.getFieldsObject(), getMappedSortObject(query, entityClass),
|
getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
|
||||||
operations.forType(entityClass).getCollation(query).orElse(null), entityClass);
|
entityClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1554,8 +1526,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
Publisher<?> publisher;
|
Publisher<?> publisher;
|
||||||
if (!mapped.hasId()) {
|
if (!mapped.hasId()) {
|
||||||
publisher = collectionToUse
|
publisher = collectionToUse.insertOne(queryOperations.createInsertContext(mapped).prepareId(entityClass).getDocument());
|
||||||
.insertOne(queryOperations.createInsertContext(mapped).prepareId(entityClass).getDocument());
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
@@ -1824,14 +1795,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass,
|
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass,
|
||||||
null, removeQuery);
|
null, removeQuery);
|
||||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
|
||||||
|
|
||||||
return execute(collectionName, collection -> {
|
return execute(collectionName, collection -> {
|
||||||
|
|
||||||
maybeEmitEvent(new BeforeDeleteEvent<>(removeQuery, entityClass, collectionName));
|
maybeEmitEvent(new BeforeDeleteEvent<>(removeQuery, entityClass, collectionName));
|
||||||
|
|
||||||
MongoCollection<Document> collectionToUse = collectionPreparer
|
MongoCollection<Document> collectionToUse = prepareCollection(collection, writeConcernToUse);
|
||||||
.prepare(prepareCollection(collection, writeConcernToUse));
|
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug(String.format("Remove using query: %s in collection: %s.", serializeToJsonSafely(removeQuery),
|
LOGGER.debug(String.format("Remove using query: %s in collection: %s.", serializeToJsonSafely(removeQuery),
|
||||||
@@ -1866,9 +1835,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Flux<T> findAll(Class<T> entityClass, String collectionName) {
|
public <T> Flux<T> findAll(Class<T> entityClass, String collectionName) {
|
||||||
return executeFindMultiInternal(new FindCallback(CollectionPreparer.identity(), null),
|
return executeFindMultiInternal(new FindCallback(null), FindPublisherPreparer.NO_OP_PREPARER,
|
||||||
FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName), collectionName);
|
||||||
collectionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1895,19 +1863,17 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
@Override
|
@Override
|
||||||
public <T> Flux<T> tail(@Nullable Query query, Class<T> entityClass, String collectionName) {
|
public <T> Flux<T> tail(@Nullable Query query, Class<T> entityClass, String collectionName) {
|
||||||
|
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
|
|
||||||
LOGGER.debug(String.format("Tail for class: %s in collection: %s", entityClass, collectionName));
|
LOGGER.debug(String.format("Tail for class: %s in collection: %s", entityClass, collectionName));
|
||||||
|
|
||||||
return executeFindMultiInternal(
|
return executeFindMultiInternal(
|
||||||
collection -> new FindCallback(collectionPreparer, null).doInCollection(collection)
|
collection -> new FindCallback(null).doInCollection(collection).cursorType(CursorType.TailableAwait),
|
||||||
.cursorType(CursorType.TailableAwait),
|
|
||||||
FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
||||||
collectionName);
|
collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doFind(collectionName, collectionPreparer, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||||
new TailingQueryFindPublisherPreparer(query, entityClass));
|
new TailingQueryFindPublisherPreparer(query, entityClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1991,14 +1957,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
assertLocalFunctionNames(mapFunction, reduceFunction);
|
assertLocalFunctionNames(mapFunction, reduceFunction);
|
||||||
|
|
||||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(filterQuery);
|
|
||||||
return createFlux(inputCollectionName, collection -> {
|
return createFlux(inputCollectionName, collection -> {
|
||||||
|
|
||||||
Document mappedQuery = queryMapper.getMappedObject(filterQuery.getQueryObject(),
|
Document mappedQuery = queryMapper.getMappedObject(filterQuery.getQueryObject(),
|
||||||
mappingContext.getPersistentEntity(domainType));
|
mappingContext.getPersistentEntity(domainType));
|
||||||
|
|
||||||
MapReducePublisher<Document> publisher = collectionPreparer.prepare(collection).mapReduce(mapFunction,
|
MapReducePublisher<Document> publisher = collection.mapReduce(mapFunction, reduceFunction, Document.class);
|
||||||
reduceFunction, Document.class);
|
|
||||||
|
|
||||||
publisher.filter(mappedQuery);
|
publisher.filter(mappedQuery);
|
||||||
|
|
||||||
@@ -2165,18 +2129,16 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
* @param collation can be {@literal null}.
|
* @param collation can be {@literal null}.
|
||||||
* @return the {@link List} of converted objects.
|
* @return the {@link List} of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> Mono<T> doFindOne(String collectionName,
|
protected <T> Mono<T> doFindOne(String collectionName, Document query, @Nullable Document fields,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, @Nullable Document fields,
|
|
||||||
Class<T> entityClass, @Nullable Collation collation) {
|
Class<T> entityClass, @Nullable Collation collation) {
|
||||||
|
|
||||||
return doFindOne(collectionName, collectionPreparer, query, fields, entityClass,
|
return doFindOne(collectionName, query, fields, entityClass,
|
||||||
findPublisher -> collation != null ? findPublisher.collation(collation.toMongoCollation()) : findPublisher);
|
findPublisher -> collation != null ? findPublisher.collation(collation.toMongoCollation()) : findPublisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2185,7 +2147,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
* The query document is specified as a standard {@link Document} and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
@@ -2193,8 +2154,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* @return the {@link List} of converted objects.
|
* @return the {@link List} of converted objects.
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
*/
|
*/
|
||||||
protected <T> Mono<T> doFindOne(String collectionName,
|
protected <T> Mono<T> doFindOne(String collectionName, Document query, @Nullable Document fields,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, @Nullable Document fields,
|
|
||||||
Class<T> entityClass, FindPublisherPreparer preparer) {
|
Class<T> entityClass, FindPublisherPreparer preparer) {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
@@ -2209,7 +2169,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(new FindOneCallback(collectionPreparer, mappedQuery, mappedFields, preparer),
|
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields, preparer),
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2218,15 +2178,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* query document is specified as a standard Document and so is the fields specification.
|
* query document is specified as a standard Document and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from
|
* @param collectionName name of the collection to retrieve the objects from
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record
|
* @param query the query document that specifies the criteria used to find a record
|
||||||
* @param fields the document that specifies the fields to be returned
|
* @param fields the document that specifies the fields to be returned
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
* @return the List of converted objects.
|
* @return the List of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> Flux<T> doFind(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||||
Document query, Document fields, Class<T> entityClass) {
|
return doFind(collectionName, query, fields, entityClass, null,
|
||||||
return doFind(collectionName, collectionPreparer, query, fields, entityClass, null,
|
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2236,7 +2194,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* specified as a standard Document and so is the fields specification.
|
* specified as a standard Document and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param query the query document that specifies the criteria used to find a record.
|
||||||
* @param fields the document that specifies the fields to be returned.
|
* @param fields the document that specifies the fields to be returned.
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
@@ -2244,15 +2201,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* the result set, (apply limits, skips and so on).
|
* the result set, (apply limits, skips and so on).
|
||||||
* @return the {@link List} of converted objects.
|
* @return the {@link List} of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> Flux<T> doFind(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
protected <T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
|
||||||
Document query, Document fields, Class<T> entityClass, FindPublisherPreparer preparer) {
|
FindPublisherPreparer preparer) {
|
||||||
return doFind(collectionName, collectionPreparer, query, fields, entityClass, preparer,
|
return doFind(collectionName, query, fields, entityClass, preparer,
|
||||||
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <S, T> Flux<T> doFind(String collectionName,
|
protected <S, T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
|
@Nullable FindPublisherPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||||
Class<S> entityClass, @Nullable FindPublisherPreparer preparer, DocumentCallback<T> objectCallback) {
|
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
|
|
||||||
@@ -2265,8 +2221,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer,
|
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer, objectCallback,
|
||||||
objectCallback, collectionName);
|
collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2275,8 +2231,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
<S, T> Flux<T> doFind(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
<S, T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<S> sourceClass,
|
||||||
Document query, Document fields, Class<S> sourceClass, Class<T> targetClass, FindPublisherPreparer preparer) {
|
Class<T> targetClass, FindPublisherPreparer preparer) {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
||||||
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
||||||
@@ -2290,7 +2246,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer,
|
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer,
|
||||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2308,15 +2264,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* The first document that matches the query is returned and also removed from the collection in the database. <br />
|
* The first document that matches the query is returned and also removed from the collection in the database. <br />
|
||||||
* The query document is specified as a standard Document and so is the fields specification.
|
* The query document is specified as a standard Document and so is the fields specification.
|
||||||
*
|
*
|
||||||
* @param collectionName name of the collection to retrieve the objects from.
|
* @param collectionName name of the collection to retrieve the objects from
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
* @param query the query document that specifies the criteria used to find a record
|
||||||
* @param query the query document that specifies the criteria used to find a record.
|
* @param collation collation
|
||||||
* @param collation collation.
|
|
||||||
* @param entityClass the parameterized type of the returned list.
|
* @param entityClass the parameterized type of the returned list.
|
||||||
* @return the List of converted objects.
|
* @return the List of converted objects.
|
||||||
*/
|
*/
|
||||||
protected <T> Mono<T> doFindAndRemove(String collectionName,
|
protected <T> Mono<T> doFindAndRemove(String collectionName, Document query, Document fields, Document sort,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort,
|
|
||||||
@Nullable Collation collation, Class<T> entityClass) {
|
@Nullable Collation collation, Class<T> entityClass) {
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
@@ -2326,13 +2280,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
|
|
||||||
return executeFindOneInternal(new FindAndRemoveCallback(collectionPreparer,
|
return executeFindOneInternal(
|
||||||
queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Mono<T> doFindAndModify(String collectionName,
|
protected <T> Mono<T> doFindAndModify(String collectionName, Document query, Document fields, Document sort,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort,
|
|
||||||
Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
|
Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
|
||||||
|
|
||||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||||
@@ -2353,7 +2306,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(
|
return executeFindOneInternal(
|
||||||
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
|
new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate,
|
||||||
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
||||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||||
});
|
});
|
||||||
@@ -2363,7 +2316,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* Customize this part for findAndReplace.
|
* Customize this part for findAndReplace.
|
||||||
*
|
*
|
||||||
* @param collectionName The name of the collection to perform the operation in.
|
* @param collectionName The name of the collection to perform the operation in.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param mappedQuery the query to look up documents.
|
* @param mappedQuery the query to look up documents.
|
||||||
* @param mappedFields the fields to project the result to.
|
* @param mappedFields the fields to project the result to.
|
||||||
* @param mappedSort the sort to be applied when executing the query.
|
* @param mappedSort the sort to be applied when executing the query.
|
||||||
@@ -2376,22 +2328,20 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
protected <T> Mono<T> doFindAndReplace(String collectionName,
|
protected <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document mappedQuery, Document mappedFields,
|
|
||||||
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||||
FindAndReplaceOptions options, Class<T> resultType) {
|
FindAndReplaceOptions options, Class<T> resultType) {
|
||||||
|
|
||||||
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
||||||
|
|
||||||
return doFindAndReplace(collectionName, collectionPreparer, mappedQuery, mappedFields, mappedSort, collation,
|
return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort, collation, entityType, replacement,
|
||||||
entityType, replacement, options, projection);
|
options, projection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customize this part for findAndReplace.
|
* Customize this part for findAndReplace.
|
||||||
*
|
*
|
||||||
* @param collectionName The name of the collection to perform the operation in.
|
* @param collectionName The name of the collection to perform the operation in.
|
||||||
* @param collectionPreparer the preparer to prepare the collection for the actual use.
|
|
||||||
* @param mappedQuery the query to look up documents.
|
* @param mappedQuery the query to look up documents.
|
||||||
* @param mappedFields the fields to project the result to.
|
* @param mappedFields the fields to project the result to.
|
||||||
* @param mappedSort the sort to be applied when executing the query.
|
* @param mappedSort the sort to be applied when executing the query.
|
||||||
@@ -2404,8 +2354,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||||
* @since 3.4
|
* @since 3.4
|
||||||
*/
|
*/
|
||||||
private <T> Mono<T> doFindAndReplace(String collectionName,
|
private <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document mappedQuery, Document mappedFields,
|
|
||||||
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||||
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||||
|
|
||||||
@@ -2419,8 +2368,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
serializeToJsonSafely(replacement), collectionName));
|
serializeToJsonSafely(replacement), collectionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields,
|
return executeFindOneInternal(
|
||||||
mappedSort, replacement, collation, options),
|
new FindAndReplaceCallback(mappedQuery, mappedFields, mappedSort, replacement, collation, options),
|
||||||
new ProjectingReadCallback<>(this.mongoConverter, projection, collectionName), collectionName);
|
new ProjectingReadCallback<>(this.mongoConverter, projection, collectionName), collectionName);
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -2498,12 +2447,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
* @param collection
|
* @param collection
|
||||||
*/
|
*/
|
||||||
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
||||||
|
return this.readPreference != null ? collection.withReadPreference(readPreference) : collection;
|
||||||
if (this.readPreference != null && this.readPreference != collection.getReadPreference()) {
|
|
||||||
return collection.withReadPreference(readPreference);
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2673,14 +2617,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*/
|
*/
|
||||||
private static class FindOneCallback implements ReactiveCollectionCallback<Document> {
|
private static class FindOneCallback implements ReactiveCollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Optional<Document> fields;
|
private final Optional<Document> fields;
|
||||||
private final FindPublisherPreparer preparer;
|
private final FindPublisherPreparer preparer;
|
||||||
|
|
||||||
FindOneCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindOneCallback(Document query, @Nullable Document fields, FindPublisherPreparer preparer) {
|
||||||
@Nullable Document fields, FindPublisherPreparer preparer) {
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = Optional.ofNullable(fields);
|
this.fields = Optional.ofNullable(fields);
|
||||||
this.preparer = preparer;
|
this.preparer = preparer;
|
||||||
@@ -2697,8 +2638,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
serializeToJsonSafely(fields.orElseGet(Document::new)), collection.getNamespace().getFullName()));
|
serializeToJsonSafely(fields.orElseGet(Document::new)), collection.getNamespace().getFullName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FindPublisher<Document> publisher = preparer.initiateFind(collectionPreparer.prepare(collection),
|
FindPublisher<Document> publisher = preparer.initiateFind(collection, col -> col.find(query, Document.class));
|
||||||
col -> col.find(query, Document.class));
|
|
||||||
|
|
||||||
if (fields.isPresent()) {
|
if (fields.isPresent()) {
|
||||||
publisher = publisher.projection(fields.get());
|
publisher = publisher.projection(fields.get());
|
||||||
@@ -2716,17 +2656,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*/
|
*/
|
||||||
private static class FindCallback implements ReactiveCollectionQueryCallback<Document> {
|
private static class FindCallback implements ReactiveCollectionQueryCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
|
|
||||||
private final @Nullable Document query;
|
private final @Nullable Document query;
|
||||||
private final @Nullable Document fields;
|
private final @Nullable Document fields;
|
||||||
|
|
||||||
FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, @Nullable Document query) {
|
FindCallback(@Nullable Document query) {
|
||||||
this(collectionPreparer, query, null);
|
this(query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields) {
|
FindCallback(Document query, Document fields) {
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
}
|
}
|
||||||
@@ -2734,12 +2672,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
@Override
|
@Override
|
||||||
public FindPublisher<Document> doInCollection(MongoCollection<Document> collection) {
|
public FindPublisher<Document> doInCollection(MongoCollection<Document> collection) {
|
||||||
|
|
||||||
MongoCollection<Document> collectionToUse = collectionPreparer.prepare(collection);
|
|
||||||
FindPublisher<Document> findPublisher;
|
FindPublisher<Document> findPublisher;
|
||||||
if (ObjectUtils.isEmpty(query)) {
|
if (ObjectUtils.isEmpty(query)) {
|
||||||
findPublisher = collectionToUse.find(Document.class);
|
findPublisher = collection.find(Document.class);
|
||||||
} else {
|
} else {
|
||||||
findPublisher = collectionToUse.find(query, Document.class);
|
findPublisher = collection.find(query, Document.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectUtils.isEmpty(fields)) {
|
if (ObjectUtils.isEmpty(fields)) {
|
||||||
@@ -2758,15 +2695,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*/
|
*/
|
||||||
private static class FindAndRemoveCallback implements ReactiveCollectionCallback<Document> {
|
private static class FindAndRemoveCallback implements ReactiveCollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
private final Optional<Collation> collation;
|
private final Optional<Collation> collation;
|
||||||
|
|
||||||
FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
|
||||||
Document fields, Document sort, @Nullable Collation collation) {
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
@@ -2780,7 +2715,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
||||||
collation.map(Collation::toMongoCollation).ifPresent(findOneAndDeleteOptions::collation);
|
collation.map(Collation::toMongoCollation).ifPresent(findOneAndDeleteOptions::collation);
|
||||||
|
|
||||||
return collectionPreparer.prepare(collection).findOneAndDelete(query, findOneAndDeleteOptions);
|
return collection.findOneAndDelete(query, findOneAndDeleteOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2789,7 +2724,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*/
|
*/
|
||||||
private static class FindAndModifyCallback implements ReactiveCollectionCallback<Document> {
|
private static class FindAndModifyCallback implements ReactiveCollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
@@ -2797,10 +2731,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
private final List<Document> arrayFilters;
|
private final List<Document> arrayFilters;
|
||||||
private final FindAndModifyOptions options;
|
private final FindAndModifyOptions options;
|
||||||
|
|
||||||
FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
|
||||||
Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
|
FindAndModifyOptions options) {
|
||||||
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
@@ -2813,22 +2746,21 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
public Publisher<Document> doInCollection(MongoCollection<Document> collection)
|
public Publisher<Document> doInCollection(MongoCollection<Document> collection)
|
||||||
throws MongoException, DataAccessException {
|
throws MongoException, DataAccessException {
|
||||||
|
|
||||||
MongoCollection<Document> collectionToUse = collectionPreparer.prepare(collection);
|
|
||||||
if (options.isRemove()) {
|
if (options.isRemove()) {
|
||||||
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
||||||
|
|
||||||
findOneAndDeleteOptions = options.getCollation().map(Collation::toMongoCollation)
|
findOneAndDeleteOptions = options.getCollation().map(Collation::toMongoCollation)
|
||||||
.map(findOneAndDeleteOptions::collation).orElse(findOneAndDeleteOptions);
|
.map(findOneAndDeleteOptions::collation).orElse(findOneAndDeleteOptions);
|
||||||
|
|
||||||
return collectionToUse.findOneAndDelete(query, findOneAndDeleteOptions);
|
return collection.findOneAndDelete(query, findOneAndDeleteOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
FindOneAndUpdateOptions findOneAndUpdateOptions = convertToFindOneAndUpdateOptions(options, fields, sort,
|
FindOneAndUpdateOptions findOneAndUpdateOptions = convertToFindOneAndUpdateOptions(options, fields, sort,
|
||||||
arrayFilters);
|
arrayFilters);
|
||||||
if (update instanceof Document) {
|
if (update instanceof Document) {
|
||||||
return collectionToUse.findOneAndUpdate(query, (Document) update, findOneAndUpdateOptions);
|
return collection.findOneAndUpdate(query, (Document) update, findOneAndUpdateOptions);
|
||||||
} else if (update instanceof List) {
|
} else if (update instanceof List) {
|
||||||
return collectionToUse.findOneAndUpdate(query, (List<Document>) update, findOneAndUpdateOptions);
|
return collection.findOneAndUpdate(query, (List<Document>) update, findOneAndUpdateOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Flux
|
return Flux
|
||||||
@@ -2867,7 +2799,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
*/
|
*/
|
||||||
private static class FindAndReplaceCallback implements ReactiveCollectionCallback<Document> {
|
private static class FindAndReplaceCallback implements ReactiveCollectionCallback<Document> {
|
||||||
|
|
||||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
|
||||||
private final Document query;
|
private final Document query;
|
||||||
private final Document fields;
|
private final Document fields;
|
||||||
private final Document sort;
|
private final Document sort;
|
||||||
@@ -2875,10 +2806,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||||
private final FindAndReplaceOptions options;
|
private final FindAndReplaceOptions options;
|
||||||
|
|
||||||
FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
FindAndReplaceCallback(Document query, Document fields, Document sort, Document update,
|
||||||
Document fields, Document sort, Document update, com.mongodb.client.model.Collation collation,
|
com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
|
||||||
FindAndReplaceOptions options) {
|
|
||||||
this.collectionPreparer = collectionPreparer;
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
@@ -2892,7 +2822,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
throws MongoException, DataAccessException {
|
throws MongoException, DataAccessException {
|
||||||
|
|
||||||
FindOneAndReplaceOptions findOneAndReplaceOptions = convertToFindOneAndReplaceOptions(options, fields, sort);
|
FindOneAndReplaceOptions findOneAndReplaceOptions = convertToFindOneAndReplaceOptions(options, fields, sort);
|
||||||
return collectionPreparer.prepare(collection).findOneAndReplace(query, update, findOneAndReplaceOptions);
|
return collection.findOneAndReplace(query, update, findOneAndReplaceOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FindOneAndReplaceOptions convertToFindOneAndReplaceOptions(FindAndReplaceOptions options, Document fields,
|
private FindOneAndReplaceOptions convertToFindOneAndReplaceOptions(FindAndReplaceOptions options, Document fields,
|
||||||
@@ -3105,10 +3035,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
.map(findPublisher::collation) //
|
.map(findPublisher::collation) //
|
||||||
.orElse(findPublisher);
|
.orElse(findPublisher);
|
||||||
|
|
||||||
HintFunction hintFunction = HintFunction.from(query.getHint());
|
|
||||||
Meta meta = query.getMeta();
|
Meta meta = query.getMeta();
|
||||||
if (query.getSkip() <= 0 && query.getLimit() <= 0 && ObjectUtils.isEmpty(query.getSortObject())
|
if (query.getSkip() <= 0 && query.getLimit() <= 0 && ObjectUtils.isEmpty(query.getSortObject())
|
||||||
&& !hintFunction.isPresent() && !meta.hasValues()) {
|
&& !StringUtils.hasText(query.getHint()) && !meta.hasValues()) {
|
||||||
return findPublisherToUse;
|
return findPublisherToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3127,9 +3056,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
findPublisherToUse = findPublisherToUse.sort(sort);
|
findPublisherToUse = findPublisherToUse.sort(sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hintFunction.isPresent()) {
|
if (StringUtils.hasText(query.getHint())) {
|
||||||
findPublisherToUse = hintFunction.apply(mongoDatabaseFactory, findPublisherToUse::hintString,
|
|
||||||
findPublisherToUse::hint);
|
String hint = query.getHint();
|
||||||
|
|
||||||
|
if (BsonUtils.isJsonDocument(hint)) {
|
||||||
|
findPublisherToUse = findPublisherToUse.hint(BsonUtils.parse(hint, mongoDatabaseFactory));
|
||||||
|
} else {
|
||||||
|
findPublisherToUse = findPublisherToUse.hintString(hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.hasValues()) {
|
if (meta.hasValues()) {
|
||||||
@@ -3158,6 +3093,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
|||||||
return findPublisherToUse;
|
return findPublisherToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadPreference getReadPreference() {
|
||||||
|
return query.getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TailingQueryFindPublisherPreparer extends QueryFindPublisherPreparer {
|
class TailingQueryFindPublisherPreparer extends QueryFindPublisherPreparer {
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.data.mongodb.core;
|
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface to be implemented by any object that wishes to expose the {@link ReadConcern}.
|
|
||||||
* <p>
|
|
||||||
* Typically implemented by cursor or query preparer objects.
|
|
||||||
*
|
|
||||||
* @author Mark Paluch
|
|
||||||
* @since 4.1
|
|
||||||
* @see org.springframework.data.mongodb.core.query.Query
|
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOptions
|
|
||||||
*/
|
|
||||||
public interface ReadConcernAware {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@literal true} if a {@link ReadConcern} is set.
|
|
||||||
*/
|
|
||||||
default boolean hasReadConcern() {
|
|
||||||
return getReadConcern() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the {@link ReadConcern} to apply or {@literal null} if none set.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
ReadConcern getReadConcern();
|
|
||||||
}
|
|
||||||
@@ -27,8 +27,6 @@ import com.mongodb.ReadPreference;
|
|||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
* @see org.springframework.data.mongodb.core.query.Query
|
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOptions
|
|
||||||
*/
|
*/
|
||||||
public interface ReadPreferenceAware {
|
public interface ReadPreferenceAware {
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddF
|
|||||||
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
|
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
|
||||||
import org.springframework.data.mongodb.core.aggregation.FacetOperation.FacetOperationBuilder;
|
import org.springframework.data.mongodb.core.aggregation.FacetOperation.FacetOperationBuilder;
|
||||||
import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder;
|
import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder;
|
||||||
import org.springframework.data.mongodb.core.aggregation.LookupOperation.LookupOperationBuilder;
|
|
||||||
import org.springframework.data.mongodb.core.aggregation.MergeOperation.MergeOperationBuilder;
|
import org.springframework.data.mongodb.core.aggregation.MergeOperation.MergeOperationBuilder;
|
||||||
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperationBuilder;
|
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperationBuilder;
|
||||||
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootOperationBuilder;
|
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootOperationBuilder;
|
||||||
@@ -51,7 +50,6 @@ import org.springframework.util.Assert;
|
|||||||
* @author Nikolay Bogdanov
|
* @author Nikolay Bogdanov
|
||||||
* @author Gustavo de Geus
|
* @author Gustavo de Geus
|
||||||
* @author Jérôme Guyon
|
* @author Jérôme Guyon
|
||||||
* @author Sangyong Choi
|
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
public class Aggregation {
|
public class Aggregation {
|
||||||
@@ -666,23 +664,6 @@ public class Aggregation {
|
|||||||
return new LookupOperation(from, localField, foreignField, as);
|
return new LookupOperation(from, localField, foreignField, as);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Entrypoint for creating {@link LookupOperation $lookup} using a fluent builder API.
|
|
||||||
* <pre class="code">
|
|
||||||
* Aggregation.lookup().from("restaurants")
|
|
||||||
* .localField("restaurant_name")
|
|
||||||
* .foreignField("name")
|
|
||||||
* .let(newVariable("orders_drink").forField("drink"))
|
|
||||||
* .pipeline(match(ctx -> new Document("$expr", new Document("$in", List.of("$$orders_drink", "$beverages")))))
|
|
||||||
* .as("matches")
|
|
||||||
* </pre>
|
|
||||||
* @return new instance of {@link LookupOperationBuilder}.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static LookupOperationBuilder lookup() {
|
|
||||||
return new LookupOperationBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link CountOperationBuilder}.
|
* Creates a new {@link CountOperationBuilder}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -19,16 +19,10 @@ import java.time.Duration;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.springframework.data.mongodb.core.ReadConcernAware;
|
|
||||||
import org.springframework.data.mongodb.core.ReadPreferenceAware;
|
|
||||||
import org.springframework.data.mongodb.core.query.Collation;
|
import org.springframework.data.mongodb.core.query.Collation;
|
||||||
import org.springframework.data.mongodb.util.BsonUtils;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
|
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
|
||||||
* aggregation options can be found in the MongoDB reference documentation
|
* aggregation options can be found in the MongoDB reference documentation
|
||||||
@@ -44,7 +38,7 @@ import com.mongodb.ReadPreference;
|
|||||||
* @see TypedAggregation#withOptions(AggregationOptions)
|
* @see TypedAggregation#withOptions(AggregationOptions)
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware {
|
public class AggregationOptions {
|
||||||
|
|
||||||
private static final String BATCH_SIZE = "batchSize";
|
private static final String BATCH_SIZE = "batchSize";
|
||||||
private static final String CURSOR = "cursor";
|
private static final String CURSOR = "cursor";
|
||||||
@@ -60,11 +54,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
private final Optional<Document> cursor;
|
private final Optional<Document> cursor;
|
||||||
private final Optional<Collation> collation;
|
private final Optional<Collation> collation;
|
||||||
private final Optional<String> comment;
|
private final Optional<String> comment;
|
||||||
private final Optional<Object> hint;
|
private final Optional<Document> hint;
|
||||||
|
|
||||||
private Optional<ReadConcern> readConcern;
|
|
||||||
|
|
||||||
private Optional<ReadPreference> readPreference;
|
|
||||||
private Duration maxTime = Duration.ZERO;
|
private Duration maxTime = Duration.ZERO;
|
||||||
private ResultOptions resultOptions = ResultOptions.READ;
|
private ResultOptions resultOptions = ResultOptions.READ;
|
||||||
private DomainTypeMapping domainTypeMapping = DomainTypeMapping.RELAXED;
|
private DomainTypeMapping domainTypeMapping = DomainTypeMapping.RELAXED;
|
||||||
@@ -124,7 +114,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
private AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
|
private AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
|
||||||
@Nullable Collation collation, @Nullable String comment, @Nullable Object hint) {
|
@Nullable Collation collation, @Nullable String comment, @Nullable Document hint) {
|
||||||
|
|
||||||
this.allowDiskUse = allowDiskUse;
|
this.allowDiskUse = allowDiskUse;
|
||||||
this.explain = explain;
|
this.explain = explain;
|
||||||
@@ -132,8 +122,6 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
this.collation = Optional.ofNullable(collation);
|
this.collation = Optional.ofNullable(collation);
|
||||||
this.comment = Optional.ofNullable(comment);
|
this.comment = Optional.ofNullable(comment);
|
||||||
this.hint = Optional.ofNullable(hint);
|
this.hint = Optional.ofNullable(hint);
|
||||||
this.readConcern = Optional.empty();
|
|
||||||
this.readPreference = Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,56 +237,15 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the hint used to fulfill the aggregation.
|
* Get the hint used to to fulfill the aggregation.
|
||||||
*
|
*
|
||||||
* @return never {@literal null}.
|
* @return never {@literal null}.
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @deprecated since 4.1, use {@link #getHintObject()} instead.
|
|
||||||
*/
|
*/
|
||||||
public Optional<Document> getHint() {
|
public Optional<Document> getHint() {
|
||||||
return hint.map(it -> {
|
|
||||||
if (it instanceof Document doc) {
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
if (it instanceof String hintString) {
|
|
||||||
if (BsonUtils.isJsonDocument(hintString)) {
|
|
||||||
return BsonUtils.parse(hintString, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("Unable to read hint of type %s".formatted(it.getClass()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the hint used to fulfill the aggregation.
|
|
||||||
*
|
|
||||||
* @return never {@literal null}.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public Optional<Object> getHintObject() {
|
|
||||||
return hint;
|
return hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadConcern() {
|
|
||||||
return readConcern.isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadConcern getReadConcern() {
|
|
||||||
return readConcern.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadPreference() {
|
|
||||||
return readPreference.isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadPreference getReadPreference() {
|
|
||||||
return readPreference.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
|
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
@@ -415,9 +362,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
private @Nullable Document cursor;
|
private @Nullable Document cursor;
|
||||||
private @Nullable Collation collation;
|
private @Nullable Collation collation;
|
||||||
private @Nullable String comment;
|
private @Nullable String comment;
|
||||||
private @Nullable Object hint;
|
private @Nullable Document hint;
|
||||||
private @Nullable ReadConcern readConcern;
|
|
||||||
private @Nullable ReadPreference readPreference;
|
|
||||||
private @Nullable Duration maxTime;
|
private @Nullable Duration maxTime;
|
||||||
private @Nullable ResultOptions resultOptions;
|
private @Nullable ResultOptions resultOptions;
|
||||||
private @Nullable DomainTypeMapping domainTypeMapping;
|
private @Nullable DomainTypeMapping domainTypeMapping;
|
||||||
@@ -510,45 +455,6 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a hint that is used by query optimizer to to fulfill the aggregation.
|
|
||||||
*
|
|
||||||
* @param indexName can be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public Builder hint(@Nullable String indexName) {
|
|
||||||
|
|
||||||
this.hint = indexName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a {@link ReadConcern} to apply to the aggregation.
|
|
||||||
*
|
|
||||||
* @param readConcern can be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public Builder readConcern(@Nullable ReadConcern readConcern) {
|
|
||||||
|
|
||||||
this.readConcern = readConcern;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a {@link ReadPreference} to apply to the aggregation.
|
|
||||||
*
|
|
||||||
* @param readPreference can be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public Builder readPreference(@Nullable ReadPreference readPreference) {
|
|
||||||
|
|
||||||
this.readPreference = readPreference;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the time limit for processing.
|
* Set the time limit for processing.
|
||||||
*
|
*
|
||||||
@@ -632,12 +538,6 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
|
|||||||
if (domainTypeMapping != null) {
|
if (domainTypeMapping != null) {
|
||||||
options.domainTypeMapping = domainTypeMapping;
|
options.domainTypeMapping = domainTypeMapping;
|
||||||
}
|
}
|
||||||
if (readConcern != null) {
|
|
||||||
options.readConcern = Optional.of(readConcern);
|
|
||||||
}
|
|
||||||
if (readPreference != null) {
|
|
||||||
options.readPreference = Optional.of(readPreference);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,44 +15,28 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.mongodb.core.aggregation;
|
package org.springframework.data.mongodb.core.aggregation;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let;
|
|
||||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the aggregation framework {@code $lookup}-operation. We recommend to use the builder provided via
|
* Encapsulates the aggregation framework {@code $lookup}-operation. We recommend to use the static factory method
|
||||||
* {@link #newLookup()} instead of creating instances of this class directly.
|
* {@link Aggregation#lookup(String, String, String, String)} instead of creating instances of this class directly.
|
||||||
*
|
*
|
||||||
* @author Alessio Fachechi
|
* @author Alessio Fachechi
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Sangyong Choi
|
|
||||||
* @since 1.9
|
* @since 1.9
|
||||||
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/">MongoDB Aggregation Framework:
|
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/">MongoDB Aggregation Framework:
|
||||||
* $lookup</a>
|
* $lookup</a>
|
||||||
*/
|
*/
|
||||||
public class LookupOperation implements FieldsExposingAggregationOperation, InheritsFieldsAggregationOperation {
|
public class LookupOperation implements FieldsExposingAggregationOperation, InheritsFieldsAggregationOperation {
|
||||||
|
|
||||||
private final String from;
|
private final Field from;
|
||||||
|
|
||||||
@Nullable //
|
|
||||||
private final Field localField;
|
private final Field localField;
|
||||||
|
|
||||||
@Nullable //
|
|
||||||
private final Field foreignField;
|
private final Field foreignField;
|
||||||
|
|
||||||
@Nullable //
|
|
||||||
private final Let let;
|
|
||||||
|
|
||||||
@Nullable //
|
|
||||||
private final AggregationPipeline pipeline;
|
|
||||||
|
|
||||||
private final ExposedField as;
|
private final ExposedField as;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,55 +48,16 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
* @param as must not be {@literal null}.
|
* @param as must not be {@literal null}.
|
||||||
*/
|
*/
|
||||||
public LookupOperation(Field from, Field localField, Field foreignField, Field as) {
|
public LookupOperation(Field from, Field localField, Field foreignField, Field as) {
|
||||||
this(((Supplier<String>) () -> {
|
|
||||||
|
|
||||||
Assert.notNull(from, "From must not be null");
|
|
||||||
return from.getTarget();
|
|
||||||
}).get(), localField, foreignField, null, null, as);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link LookupOperation} for the given combination of {@link Field}s and {@link AggregationPipeline
|
|
||||||
* pipeline}.
|
|
||||||
*
|
|
||||||
* @param from must not be {@literal null}.
|
|
||||||
* @param let must not be {@literal null}.
|
|
||||||
* @param as must not be {@literal null}.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public LookupOperation(String from, @Nullable Let let, AggregationPipeline pipeline, Field as) {
|
|
||||||
this(from, null, null, let, pipeline, as);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link LookupOperation} for the given combination of {@link Field}s and {@link AggregationPipeline
|
|
||||||
* pipeline}.
|
|
||||||
*
|
|
||||||
* @param from must not be {@literal null}.
|
|
||||||
* @param localField can be {@literal null} if {@literal pipeline} is present.
|
|
||||||
* @param foreignField can be {@literal null} if {@literal pipeline} is present.
|
|
||||||
* @param let can be {@literal null} if {@literal localField} and {@literal foreignField} are present.
|
|
||||||
* @param as must not be {@literal null}.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public LookupOperation(String from, @Nullable Field localField, @Nullable Field foreignField, @Nullable Let let,
|
|
||||||
@Nullable AggregationPipeline pipeline, Field as) {
|
|
||||||
|
|
||||||
Assert.notNull(from, "From must not be null");
|
Assert.notNull(from, "From must not be null");
|
||||||
if (pipeline == null) {
|
Assert.notNull(localField, "LocalField must not be null");
|
||||||
Assert.notNull(localField, "LocalField must not be null");
|
Assert.notNull(foreignField, "ForeignField must not be null");
|
||||||
Assert.notNull(foreignField, "ForeignField must not be null");
|
|
||||||
} else if (localField == null && foreignField == null) {
|
|
||||||
Assert.notNull(pipeline, "Pipeline must not be null");
|
|
||||||
}
|
|
||||||
Assert.notNull(as, "As must not be null");
|
Assert.notNull(as, "As must not be null");
|
||||||
|
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.localField = localField;
|
this.localField = localField;
|
||||||
this.foreignField = foreignField;
|
this.foreignField = foreignField;
|
||||||
this.as = new ExposedField(as, true);
|
this.as = new ExposedField(as, true);
|
||||||
this.let = let;
|
|
||||||
this.pipeline = pipeline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,20 +70,9 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
|
|
||||||
Document lookupObject = new Document();
|
Document lookupObject = new Document();
|
||||||
|
|
||||||
lookupObject.append("from", from);
|
lookupObject.append("from", from.getTarget());
|
||||||
if (localField != null) {
|
lookupObject.append("localField", localField.getTarget());
|
||||||
lookupObject.append("localField", localField.getTarget());
|
lookupObject.append("foreignField", foreignField.getTarget());
|
||||||
}
|
|
||||||
if (foreignField != null) {
|
|
||||||
lookupObject.append("foreignField", foreignField.getTarget());
|
|
||||||
}
|
|
||||||
if (let != null) {
|
|
||||||
lookupObject.append("let", let.toDocument(context).get("$let", Document.class).get("vars"));
|
|
||||||
}
|
|
||||||
if (pipeline != null) {
|
|
||||||
lookupObject.append("pipeline", pipeline.toDocuments(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
lookupObject.append("as", as.getTarget());
|
lookupObject.append("as", as.getTarget());
|
||||||
|
|
||||||
return new Document(getOperator(), lookupObject);
|
return new Document(getOperator(), lookupObject);
|
||||||
@@ -167,7 +101,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
LocalFieldBuilder from(String name);
|
LocalFieldBuilder from(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface LocalFieldBuilder extends PipelineBuilder {
|
public static interface LocalFieldBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name the field from the documents input to the {@code $lookup} stage, must not be {@literal null} or
|
* @param name the field from the documents input to the {@code $lookup} stage, must not be {@literal null} or
|
||||||
@@ -186,67 +120,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
AsBuilder foreignField(String name);
|
AsBuilder foreignField(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static interface AsBuilder {
|
||||||
* @since 4.1
|
|
||||||
* @author Christoph Strobl
|
|
||||||
*/
|
|
||||||
public interface LetBuilder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies {@link Let#getVariableNames() variables) that can be used in the
|
|
||||||
* {@link PipelineBuilder#pipeline(AggregationOperation...) pipeline stages}.
|
|
||||||
*
|
|
||||||
* @param let must not be {@literal null}.
|
|
||||||
* @return never {@literal null}.
|
|
||||||
* @see PipelineBuilder
|
|
||||||
*/
|
|
||||||
PipelineBuilder let(Let let);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies {@link Let#getVariableNames() variables) that can be used in the
|
|
||||||
* {@link PipelineBuilder#pipeline(AggregationOperation...) pipeline stages}.
|
|
||||||
*
|
|
||||||
* @param variables must not be {@literal null}.
|
|
||||||
* @return never {@literal null}.
|
|
||||||
* @see PipelineBuilder
|
|
||||||
*/
|
|
||||||
default PipelineBuilder let(ExpressionVariable... variables) {
|
|
||||||
return let(Let.just(variables));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 4.1
|
|
||||||
* @author Christoph Strobl
|
|
||||||
*/
|
|
||||||
public interface PipelineBuilder extends LetBuilder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the {@link AggregationPipeline pipeline} that determines the resulting documents.
|
|
||||||
*
|
|
||||||
* @param pipeline must not be {@literal null}.
|
|
||||||
* @return never {@literal null}.
|
|
||||||
*/
|
|
||||||
AsBuilder pipeline(AggregationPipeline pipeline);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the {@link AggregationPipeline#getOperations() stages} that determine the resulting documents.
|
|
||||||
*
|
|
||||||
* @param stages must not be {@literal null} can be empty.
|
|
||||||
* @return never {@literal null}.
|
|
||||||
*/
|
|
||||||
default AsBuilder pipeline(AggregationOperation... stages) {
|
|
||||||
return pipeline(AggregationPipeline.of(stages));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param name the name of the new array field to add to the input documents, must not be {@literal null} or empty.
|
|
||||||
* @return new instance of {@link LookupOperation}.
|
|
||||||
*/
|
|
||||||
LookupOperation as(String name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface AsBuilder extends PipelineBuilder {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name the name of the new array field to add to the input documents, must not be {@literal null} or empty.
|
* @param name the name of the new array field to add to the input documents, must not be {@literal null} or empty.
|
||||||
@@ -264,12 +138,10 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
public static final class LookupOperationBuilder
|
public static final class LookupOperationBuilder
|
||||||
implements FromBuilder, LocalFieldBuilder, ForeignFieldBuilder, AsBuilder {
|
implements FromBuilder, LocalFieldBuilder, ForeignFieldBuilder, AsBuilder {
|
||||||
|
|
||||||
private @Nullable String from;
|
private @Nullable Field from;
|
||||||
private @Nullable Field localField;
|
private @Nullable Field localField;
|
||||||
private @Nullable Field foreignField;
|
private @Nullable Field foreignField;
|
||||||
private @Nullable ExposedField as;
|
private @Nullable ExposedField as;
|
||||||
private @Nullable Let let;
|
|
||||||
private @Nullable AggregationPipeline pipeline;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new builder for {@link LookupOperation}.
|
* Creates new builder for {@link LookupOperation}.
|
||||||
@@ -284,10 +156,18 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
public LocalFieldBuilder from(String name) {
|
public LocalFieldBuilder from(String name) {
|
||||||
|
|
||||||
Assert.hasText(name, "'From' must not be null or empty");
|
Assert.hasText(name, "'From' must not be null or empty");
|
||||||
from = name;
|
from = Fields.field(name);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LookupOperation as(String name) {
|
||||||
|
|
||||||
|
Assert.hasText(name, "'As' must not be null or empty");
|
||||||
|
as = new ExposedField(Fields.field(name), true);
|
||||||
|
return new LookupOperation(from, localField, foreignField, as);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsBuilder foreignField(String name) {
|
public AsBuilder foreignField(String name) {
|
||||||
|
|
||||||
@@ -303,29 +183,5 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe
|
|||||||
localField = Fields.field(name);
|
localField = Fields.field(name);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PipelineBuilder let(Let let) {
|
|
||||||
|
|
||||||
Assert.notNull(let, "Let must not be null");
|
|
||||||
this.let = let;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsBuilder pipeline(AggregationPipeline pipeline) {
|
|
||||||
|
|
||||||
Assert.notNull(pipeline, "Pipeline must not be null");
|
|
||||||
this.pipeline = pipeline;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LookupOperation as(String name) {
|
|
||||||
|
|
||||||
Assert.hasText(name, "'As' must not be null or empty");
|
|
||||||
as = new ExposedField(Fields.field(name), true);
|
|
||||||
return new LookupOperation(from, localField, foreignField, let, pipeline, as);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package org.springframework.data.mongodb.core.aggregation;
|
package org.springframework.data.mongodb.core.aggregation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -223,41 +224,28 @@ public class VariableOperators {
|
|||||||
public static class Let implements AggregationExpression {
|
public static class Let implements AggregationExpression {
|
||||||
|
|
||||||
private final List<ExpressionVariable> vars;
|
private final List<ExpressionVariable> vars;
|
||||||
|
|
||||||
@Nullable //
|
|
||||||
private final AggregationExpression expression;
|
private final AggregationExpression expression;
|
||||||
|
|
||||||
private Let(List<ExpressionVariable> vars, @Nullable AggregationExpression expression) {
|
private Let(List<ExpressionVariable> vars, AggregationExpression expression) {
|
||||||
|
|
||||||
this.vars = vars;
|
this.vars = vars;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link Let} holding just the given {@literal variables}.
|
|
||||||
*
|
|
||||||
* @param variables must not be {@literal null}.
|
|
||||||
* @return new instance of {@link Let}.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static Let just(ExpressionVariable... variables) {
|
|
||||||
return new Let(List.of(variables), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start creating new {@link Let} by defining the variables for {@code $vars}.
|
* Start creating new {@link Let} by defining the variables for {@code $vars}.
|
||||||
*
|
*
|
||||||
* @param variables must not be {@literal null}.
|
* @param variables must not be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static LetBuilder define(Collection<ExpressionVariable> variables) {
|
public static LetBuilder define(final Collection<ExpressionVariable> variables) {
|
||||||
|
|
||||||
Assert.notNull(variables, "Variables must not be null");
|
Assert.notNull(variables, "Variables must not be null");
|
||||||
|
|
||||||
return new LetBuilder() {
|
return new LetBuilder() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Let andApply(AggregationExpression expression) {
|
public Let andApply(final AggregationExpression expression) {
|
||||||
|
|
||||||
Assert.notNull(expression, "Expression must not be null");
|
Assert.notNull(expression, "Expression must not be null");
|
||||||
return new Let(new ArrayList<ExpressionVariable>(variables), expression);
|
return new Let(new ArrayList<ExpressionVariable>(variables), expression);
|
||||||
@@ -271,10 +259,19 @@ public class VariableOperators {
|
|||||||
* @param variables must not be {@literal null}.
|
* @param variables must not be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static LetBuilder define(ExpressionVariable... variables) {
|
public static LetBuilder define(final ExpressionVariable... variables) {
|
||||||
|
|
||||||
Assert.notNull(variables, "Variables must not be null");
|
Assert.notNull(variables, "Variables must not be null");
|
||||||
return define(List.of(variables));
|
|
||||||
|
return new LetBuilder() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Let andApply(final AggregationExpression expression) {
|
||||||
|
|
||||||
|
Assert.notNull(expression, "Expression must not be null");
|
||||||
|
return new Let(Arrays.asList(variables), expression);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface LetBuilder {
|
public interface LetBuilder {
|
||||||
@@ -286,11 +283,10 @@ public class VariableOperators {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Let andApply(AggregationExpression expression);
|
Let andApply(AggregationExpression expression);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Document toDocument(AggregationOperationContext context) {
|
public Document toDocument(final AggregationOperationContext context) {
|
||||||
return toLet(ExposedFields.synthetic(Fields.fields(getVariableNames())), context);
|
return toLet(ExposedFields.synthetic(Fields.fields(getVariableNames())), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,22 +312,16 @@ public class VariableOperators {
|
|||||||
}
|
}
|
||||||
|
|
||||||
letExpression.put("vars", mappedVars);
|
letExpression.put("vars", mappedVars);
|
||||||
if (expression != null) {
|
letExpression.put("in", getMappedIn(operationContext));
|
||||||
letExpression.put("in", getMappedIn(operationContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Document("$let", letExpression);
|
return new Document("$let", letExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Document getMappedVariable(ExpressionVariable var, AggregationOperationContext context) {
|
private Document getMappedVariable(ExpressionVariable var, AggregationOperationContext context) {
|
||||||
|
|
||||||
if (var.expression instanceof AggregationExpression expression) {
|
return new Document(var.variableName,
|
||||||
return new Document(var.variableName, expression.toDocument(context));
|
var.expression instanceof AggregationExpression ? ((AggregationExpression) var.expression).toDocument(context)
|
||||||
}
|
: var.expression);
|
||||||
if (var.expression instanceof Field field) {
|
|
||||||
return new Document(var.variableName, context.getReference(field).toString());
|
|
||||||
}
|
|
||||||
return new Document(var.variableName, var.expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getMappedIn(AggregationOperationContext context) {
|
private Object getMappedIn(AggregationOperationContext context) {
|
||||||
@@ -383,10 +373,6 @@ public class VariableOperators {
|
|||||||
return new ExpressionVariable(variableName, expression);
|
return new ExpressionVariable(variableName, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpressionVariable forField(String fieldRef) {
|
|
||||||
return new ExpressionVariable(variableName, Fields.field(fieldRef));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link ExpressionVariable} with current name and given {@literal expressionObject}.
|
* Create a new {@link ExpressionVariable} with current name and given {@literal expressionObject}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -24,16 +24,11 @@ import org.springframework.data.geo.Distance;
|
|||||||
import org.springframework.data.geo.Metric;
|
import org.springframework.data.geo.Metric;
|
||||||
import org.springframework.data.geo.Metrics;
|
import org.springframework.data.geo.Metrics;
|
||||||
import org.springframework.data.geo.Point;
|
import org.springframework.data.geo.Point;
|
||||||
import org.springframework.data.mongodb.core.ReadConcernAware;
|
|
||||||
import org.springframework.data.mongodb.core.ReadPreferenceAware;
|
|
||||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder class to build near-queries. <br />
|
* Builder class to build near-queries. <br />
|
||||||
* MongoDB {@code $geoNear} operator allows usage of a {@literal GeoJSON Point} or legacy coordinate pair. Though
|
* MongoDB {@code $geoNear} operator allows usage of a {@literal GeoJSON Point} or legacy coordinate pair. Though
|
||||||
@@ -176,7 +171,7 @@ import com.mongodb.ReadPreference;
|
|||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
*/
|
*/
|
||||||
public final class NearQuery implements ReadConcernAware, ReadPreferenceAware {
|
public final class NearQuery {
|
||||||
|
|
||||||
private final Point point;
|
private final Point point;
|
||||||
private @Nullable Query query;
|
private @Nullable Query query;
|
||||||
@@ -186,8 +181,6 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware {
|
|||||||
private boolean spherical;
|
private boolean spherical;
|
||||||
private @Nullable Long limit;
|
private @Nullable Long limit;
|
||||||
private @Nullable Long skip;
|
private @Nullable Long skip;
|
||||||
private @Nullable ReadConcern readConcern;
|
|
||||||
private @Nullable ReadPreference readPreference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link NearQuery}.
|
* Creates a new {@link NearQuery}.
|
||||||
@@ -562,74 +555,6 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware {
|
|||||||
return query != null ? query.getCollation().orElse(null) : null;
|
return query != null ? query.getCollation().orElse(null) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the query to use the given {@link ReadConcern} unless the underlying {@link #query(Query)}
|
|
||||||
* {@link Query#hasReadConcern() specifies} another one.
|
|
||||||
*
|
|
||||||
* @param readConcern must not be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public NearQuery withReadConcern(ReadConcern readConcern) {
|
|
||||||
|
|
||||||
Assert.notNull(readConcern, "ReadConcern must not be null");
|
|
||||||
this.readConcern = readConcern;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the query to use the given {@link ReadPreference} unless the underlying {@link #query(Query)}
|
|
||||||
* {@link Query#hasReadPreference() specifies} another one.
|
|
||||||
*
|
|
||||||
* @param readPreference must not be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public NearQuery withReadPreference(ReadPreference readPreference) {
|
|
||||||
|
|
||||||
Assert.notNull(readPreference, "ReadPreference must not be null");
|
|
||||||
this.readPreference = readPreference;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the {@link ReadConcern} to use. Will return the underlying {@link #query(Query) queries}
|
|
||||||
* {@link Query#getReadConcern() ReadConcern} if present or the one defined on the {@link NearQuery#readConcern}
|
|
||||||
* itself.
|
|
||||||
*
|
|
||||||
* @return can be {@literal null} if none set.
|
|
||||||
* @since 4.1
|
|
||||||
* @see ReadConcernAware
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ReadConcern getReadConcern() {
|
|
||||||
|
|
||||||
if (query != null && query.hasReadConcern()) {
|
|
||||||
return query.getReadConcern();
|
|
||||||
}
|
|
||||||
return readConcern;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the {@link ReadPreference} to use. Will return the underlying {@link #query(Query) queries}
|
|
||||||
* {@link Query#getReadPreference() ReadPreference} if present or the one defined on the
|
|
||||||
* {@link NearQuery#readPreference} itself.
|
|
||||||
*
|
|
||||||
* @return can be {@literal null} if none set.
|
|
||||||
* @since 4.1
|
|
||||||
* @see ReadPreferenceAware
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ReadPreference getReadPreference() {
|
|
||||||
|
|
||||||
if (query != null && query.hasReadPreference()) {
|
|
||||||
return query.getReadPreference();
|
|
||||||
}
|
|
||||||
return readPreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Document} built by the {@link NearQuery}.
|
* Returns the {@link Document} built by the {@link NearQuery}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -34,16 +34,10 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Order;
|
import org.springframework.data.domain.Sort.Order;
|
||||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||||
import org.springframework.data.mongodb.core.ReadConcernAware;
|
|
||||||
import org.springframework.data.mongodb.core.ReadPreferenceAware;
|
|
||||||
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
|
||||||
import org.springframework.data.mongodb.util.BsonUtils;
|
import org.springframework.data.mongodb.util.BsonUtils;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoDB Query object representing criteria, projection, sorting and query hints.
|
* MongoDB Query object representing criteria, projection, sorting and query hints.
|
||||||
*
|
*
|
||||||
@@ -54,7 +48,7 @@ import com.mongodb.ReadPreference;
|
|||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Anton Barkan
|
* @author Anton Barkan
|
||||||
*/
|
*/
|
||||||
public class Query implements ReadConcernAware, ReadPreferenceAware {
|
public class Query {
|
||||||
|
|
||||||
private static final String RESTRICTED_TYPES_KEY = "_$RESTRICTED_TYPES";
|
private static final String RESTRICTED_TYPES_KEY = "_$RESTRICTED_TYPES";
|
||||||
|
|
||||||
@@ -64,9 +58,6 @@ public class Query implements ReadConcernAware, ReadPreferenceAware {
|
|||||||
private Sort sort = Sort.unsorted();
|
private Sort sort = Sort.unsorted();
|
||||||
private long skip;
|
private long skip;
|
||||||
private int limit;
|
private int limit;
|
||||||
private @Nullable ReadConcern readConcern;
|
|
||||||
private @Nullable ReadPreference readPreference;
|
|
||||||
|
|
||||||
private @Nullable String hint;
|
private @Nullable String hint;
|
||||||
|
|
||||||
private Meta meta = new Meta();
|
private Meta meta = new Meta();
|
||||||
@@ -169,59 +160,6 @@ public class Query implements ReadConcernAware, ReadPreferenceAware {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the query to use the given {@link ReadConcern} when being executed.
|
|
||||||
*
|
|
||||||
* @param readConcern must not be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 3.1
|
|
||||||
*/
|
|
||||||
public Query withReadConcern(ReadConcern readConcern) {
|
|
||||||
|
|
||||||
Assert.notNull(readConcern, "ReadConcern must not be null");
|
|
||||||
this.readConcern = readConcern;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the query to use the given {@link ReadPreference} when being executed.
|
|
||||||
*
|
|
||||||
* @param readPreference must not be {@literal null}.
|
|
||||||
* @return this.
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public Query withReadPreference(ReadPreference readPreference) {
|
|
||||||
|
|
||||||
Assert.notNull(readPreference, "ReadPreference must not be null");
|
|
||||||
this.readPreference = readPreference;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadConcern() {
|
|
||||||
return this.readConcern != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadConcern getReadConcern() {
|
|
||||||
return this.readConcern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasReadPreference() {
|
|
||||||
return this.readPreference != null || getMeta().getFlags().contains(CursorOption.SECONDARY_READS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadPreference getReadPreference() {
|
|
||||||
|
|
||||||
if (readPreference == null) {
|
|
||||||
return getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.readPreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the query to use the given {@link Document hint} when being executed.
|
* Configures the query to use the given {@link Document hint} when being executed.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ class QueryUtils {
|
|||||||
combinedSort.putAll((Document) invocation.proceed());
|
combinedSort.putAll((Document) invocation.proceed());
|
||||||
return combinedSort;
|
return combinedSort;
|
||||||
});
|
});
|
||||||
factory.setInterfaces(new Class[0]);
|
|
||||||
|
|
||||||
return (Query) factory.getProxy(query.getClass().getClassLoader());
|
return (Query) factory.getProxy(query.getClass().getClassLoader());
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,7 @@ class QueryUtils {
|
|||||||
if(parameters.isEmpty()) {
|
if(parameters.isEmpty()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(Class<?> parameterType : parameters) {
|
for(Class<?> parameterType : parameters) {
|
||||||
if(ClassUtils.isAssignable(type, parameterType)) {
|
if(ClassUtils.isAssignable(type, parameterType)) {
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ import org.springframework.util.CollectionUtils;
|
|||||||
import com.mongodb.MongoClientSettings;
|
import com.mongodb.MongoClientSettings;
|
||||||
import com.mongodb.MongoException;
|
import com.mongodb.MongoException;
|
||||||
import com.mongodb.MongoNamespace;
|
import com.mongodb.MongoNamespace;
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
import com.mongodb.ReadPreference;
|
||||||
import com.mongodb.ServerAddress;
|
import com.mongodb.ServerAddress;
|
||||||
import com.mongodb.ServerCursor;
|
import com.mongodb.ServerCursor;
|
||||||
@@ -121,7 +120,16 @@ import com.mongodb.client.MongoClient;
|
|||||||
import com.mongodb.client.MongoCollection;
|
import com.mongodb.client.MongoCollection;
|
||||||
import com.mongodb.client.MongoCursor;
|
import com.mongodb.client.MongoCursor;
|
||||||
import com.mongodb.client.MongoDatabase;
|
import com.mongodb.client.MongoDatabase;
|
||||||
import com.mongodb.client.model.*;
|
import com.mongodb.client.model.CountOptions;
|
||||||
|
import com.mongodb.client.model.CreateCollectionOptions;
|
||||||
|
import com.mongodb.client.model.DeleteOptions;
|
||||||
|
import com.mongodb.client.model.FindOneAndDeleteOptions;
|
||||||
|
import com.mongodb.client.model.FindOneAndReplaceOptions;
|
||||||
|
import com.mongodb.client.model.FindOneAndUpdateOptions;
|
||||||
|
import com.mongodb.client.model.MapReduceAction;
|
||||||
|
import com.mongodb.client.model.ReplaceOptions;
|
||||||
|
import com.mongodb.client.model.TimeSeriesGranularity;
|
||||||
|
import com.mongodb.client.model.UpdateOptions;
|
||||||
import com.mongodb.client.result.DeleteResult;
|
import com.mongodb.client.result.DeleteResult;
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
|
|
||||||
@@ -174,7 +182,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
when(collection.estimatedDocumentCount(any())).thenReturn(1L);
|
when(collection.estimatedDocumentCount(any())).thenReturn(1L);
|
||||||
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
|
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
|
||||||
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
|
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
|
||||||
when(collection.withReadConcern(any())).thenReturn(collection);
|
|
||||||
when(collection.withReadPreference(any())).thenReturn(collection);
|
when(collection.withReadPreference(any())).thenReturn(collection);
|
||||||
when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))).thenReturn(updateResult);
|
when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))).thenReturn(updateResult);
|
||||||
when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern);
|
when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern);
|
||||||
@@ -197,8 +204,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
when(aggregateIterable.map(any())).thenReturn(aggregateIterable);
|
when(aggregateIterable.map(any())).thenReturn(aggregateIterable);
|
||||||
when(aggregateIterable.maxTime(anyLong(), any())).thenReturn(aggregateIterable);
|
when(aggregateIterable.maxTime(anyLong(), any())).thenReturn(aggregateIterable);
|
||||||
when(aggregateIterable.into(any())).thenReturn(Collections.emptyList());
|
when(aggregateIterable.into(any())).thenReturn(Collections.emptyList());
|
||||||
when(aggregateIterable.hint(any())).thenReturn(aggregateIterable);
|
|
||||||
when(aggregateIterable.hintString(any())).thenReturn(aggregateIterable);
|
|
||||||
when(distinctIterable.collation(any())).thenReturn(distinctIterable);
|
when(distinctIterable.collation(any())).thenReturn(distinctIterable);
|
||||||
when(distinctIterable.map(any())).thenReturn(distinctIterable);
|
when(distinctIterable.map(any())).thenReturn(distinctIterable);
|
||||||
when(distinctIterable.into(any())).thenReturn(Collections.emptyList());
|
when(distinctIterable.into(any())).thenReturn(Collections.emptyList());
|
||||||
@@ -471,34 +476,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
verify(collection, never()).withReadPreference(any());
|
verify(collection, never()).withReadPreference(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void aggregateShouldHonorOptionsReadConcernWhenSet() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().readConcern(ReadConcern.SNAPSHOT).build();
|
|
||||||
template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class);
|
|
||||||
|
|
||||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void aggregateShouldHonorOptionsReadPreferenceWhenSet() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.secondary()).build();
|
|
||||||
template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class);
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void aggregateStreamShouldHonorOptionsReadPreferenceWhenSet() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.secondary()).build();
|
|
||||||
template.aggregateStream(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1",
|
|
||||||
Wrapper.class);
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-2153
|
@Test // DATAMONGO-2153
|
||||||
void aggregateShouldHonorOptionsComment() {
|
void aggregateShouldHonorOptionsComment() {
|
||||||
|
|
||||||
@@ -520,16 +497,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
verify(aggregateIterable).hint(hint);
|
verify(aggregateIterable).hint(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4238
|
|
||||||
void aggregateShouldHonorOptionsHintString() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().hint("index-1").build();
|
|
||||||
|
|
||||||
template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class);
|
|
||||||
|
|
||||||
verify(aggregateIterable).hintString("index-1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-3542
|
@Test // GH-3542
|
||||||
void aggregateShouldUseRelaxedMappingByDefault() {
|
void aggregateShouldUseRelaxedMappingByDefault() {
|
||||||
|
|
||||||
@@ -579,28 +546,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void geoNearShouldHonorReadPreferenceFromQuery() {
|
|
||||||
|
|
||||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
|
||||||
query.withReadPreference(ReadPreference.secondary());
|
|
||||||
|
|
||||||
template.geoNear(query, Wrapper.class);
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void geoNearShouldHonorReadConcernFromQuery() {
|
|
||||||
|
|
||||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
|
||||||
query.withReadConcern(ReadConcern.SNAPSHOT);
|
|
||||||
|
|
||||||
template.geoNear(query, Wrapper.class);
|
|
||||||
|
|
||||||
verify(collection).withReadConcern(eq(ReadConcern.SNAPSHOT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-1166, DATAMONGO-2264
|
@Test // DATAMONGO-1166, DATAMONGO-2264
|
||||||
void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
|
void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
|
||||||
|
|
||||||
@@ -845,24 +790,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
verify(findIterable).batchSize(1234);
|
verify(findIterable).batchSize(1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void findShouldUseReadConcernWhenPresent() {
|
|
||||||
|
|
||||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadConcern(ReadConcern.SNAPSHOT),
|
|
||||||
AutogenerateableId.class);
|
|
||||||
|
|
||||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void findShouldUseReadPreferenceWhenPresent() {
|
|
||||||
|
|
||||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadPreference(ReadPreference.secondary()),
|
|
||||||
AutogenerateableId.class);
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-1518
|
@Test // DATAMONGO-1518
|
||||||
void executeQueryShouldUseCollationWhenPresent() {
|
void executeQueryShouldUseCollationWhenPresent() {
|
||||||
|
|
||||||
@@ -1109,8 +1036,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class,
|
||||||
PersonProjection.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||||
@@ -1119,8 +1045,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class,
|
||||||
PersonProjection.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||||
@@ -1129,8 +1054,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class,
|
||||||
PersonSpELProjection.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||||
@@ -1139,8 +1063,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733, DATAMONGO-2041
|
@Test // DATAMONGO-1733, DATAMONGO-2041
|
||||||
void appliesFieldsToDtoProjection() {
|
void appliesFieldsToDtoProjection() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class,
|
||||||
Jedi.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||||
@@ -1149,8 +1072,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class,
|
||||||
Jedi.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||||
@@ -1159,8 +1081,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class,
|
||||||
Person.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||||
@@ -1169,8 +1090,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
|||||||
@Test // DATAMONGO-1733
|
@Test // DATAMONGO-1733
|
||||||
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
||||||
|
|
||||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class,
|
||||||
PersonExtended.class,
|
|
||||||
CursorPreparer.NO_OP_PREPARER);
|
CursorPreparer.NO_OP_PREPARER);
|
||||||
|
|
||||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ import org.mockito.junit.jupiter.MockitoSettings;
|
|||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import org.reactivestreams.Subscriber;
|
import org.reactivestreams.Subscriber;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.support.StaticApplicationContext;
|
import org.springframework.context.support.StaticApplicationContext;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
import org.springframework.data.geo.Point;
|
|
||||||
import org.springframework.data.mapping.MappingException;
|
import org.springframework.data.mapping.MappingException;
|
||||||
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
|
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
|
||||||
import org.springframework.data.mapping.context.MappingContext;
|
import org.springframework.data.mapping.context.MappingContext;
|
||||||
@@ -98,7 +98,6 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import com.mongodb.MongoClientSettings;
|
import com.mongodb.MongoClientSettings;
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
import com.mongodb.ReadPreference;
|
||||||
import com.mongodb.client.model.CountOptions;
|
import com.mongodb.client.model.CountOptions;
|
||||||
import com.mongodb.client.model.CreateCollectionOptions;
|
import com.mongodb.client.model.CreateCollectionOptions;
|
||||||
@@ -167,7 +166,6 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
when(db.runCommand(any(), any(Class.class))).thenReturn(runCommandPublisher);
|
when(db.runCommand(any(), any(Class.class))).thenReturn(runCommandPublisher);
|
||||||
when(db.createCollection(any(), any(CreateCollectionOptions.class))).thenReturn(runCommandPublisher);
|
when(db.createCollection(any(), any(CreateCollectionOptions.class))).thenReturn(runCommandPublisher);
|
||||||
when(collection.withReadPreference(any())).thenReturn(collection);
|
when(collection.withReadPreference(any())).thenReturn(collection);
|
||||||
when(collection.withReadConcern(any())).thenReturn(collection);
|
|
||||||
when(collection.find(any(Class.class))).thenReturn(findPublisher);
|
when(collection.find(any(Class.class))).thenReturn(findPublisher);
|
||||||
when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher);
|
when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher);
|
||||||
when(collection.aggregate(anyList())).thenReturn(aggregatePublisher);
|
when(collection.aggregate(anyList())).thenReturn(aggregatePublisher);
|
||||||
@@ -385,33 +383,11 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build()));
|
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void geoNearShouldHonorReadPreferenceFromQuery() {
|
|
||||||
|
|
||||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
|
||||||
query.withReadPreference(ReadPreference.secondary());
|
|
||||||
|
|
||||||
template.geoNear(query, Wrapper.class).subscribe();
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void geoNearShouldHonorReadConcernFromQuery() {
|
|
||||||
|
|
||||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
|
||||||
query.withReadConcern(ReadConcern.SNAPSHOT);
|
|
||||||
|
|
||||||
template.geoNear(query, Wrapper.class).subscribe();
|
|
||||||
|
|
||||||
verify(collection).withReadConcern(eq(ReadConcern.SNAPSHOT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class,
|
||||||
PersonProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
||||||
}
|
}
|
||||||
@@ -419,8 +395,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document("bar", 1), Person.class,
|
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class,
|
||||||
PersonProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
||||||
}
|
}
|
||||||
@@ -428,8 +404,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class,
|
||||||
PersonSpELProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher, never()).projection(any());
|
verify(findPublisher, never()).projection(any());
|
||||||
}
|
}
|
||||||
@@ -437,8 +413,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719, DATAMONGO-2041
|
@Test // DATAMONGO-1719, DATAMONGO-2041
|
||||||
void appliesFieldsToDtoProjection() {
|
void appliesFieldsToDtoProjection() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class,
|
||||||
Jedi.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
||||||
}
|
}
|
||||||
@@ -446,8 +422,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document("bar", 1), Person.class,
|
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class,
|
||||||
Jedi.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
||||||
}
|
}
|
||||||
@@ -455,8 +431,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class,
|
||||||
Person.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher, never()).projection(any());
|
verify(findPublisher, never()).projection(any());
|
||||||
}
|
}
|
||||||
@@ -464,8 +440,8 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
@Test // DATAMONGO-1719
|
@Test // DATAMONGO-1719
|
||||||
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
||||||
|
|
||||||
template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
|
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class,
|
||||||
PersonExtended.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||||
|
|
||||||
verify(findPublisher, never()).projection(any());
|
verify(findPublisher, never()).projection(any());
|
||||||
}
|
}
|
||||||
@@ -654,26 +630,6 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build()));
|
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void aggreateShouldUseReadConcern() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().readConcern(ReadConcern.SNAPSHOT).build();
|
|
||||||
template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class,
|
|
||||||
Document.class).subscribe();
|
|
||||||
|
|
||||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4286
|
|
||||||
void aggreateShouldUseReadReadPreference() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.primaryPreferred()).build();
|
|
||||||
template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class,
|
|
||||||
Document.class).subscribe();
|
|
||||||
|
|
||||||
verify(collection).withReadPreference(ReadPreference.primaryPreferred());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-1854
|
@Test // DATAMONGO-1854
|
||||||
void aggreateShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() {
|
void aggreateShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() {
|
||||||
|
|
||||||
@@ -710,17 +666,6 @@ public class ReactiveMongoTemplateUnitTests {
|
|||||||
verify(aggregatePublisher).hint(hint);
|
verify(aggregatePublisher).hint(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4238
|
|
||||||
void aggregateShouldHonorOptionsHintString() {
|
|
||||||
|
|
||||||
AggregationOptions options = AggregationOptions.builder().hint("index-1").build();
|
|
||||||
|
|
||||||
template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class,
|
|
||||||
Document.class).subscribe();
|
|
||||||
|
|
||||||
verify(aggregatePublisher).hintString("index-1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-2390
|
@Test // DATAMONGO-2390
|
||||||
void aggregateShouldNoApplyZeroOrNegativeMaxTime() {
|
void aggregateShouldNoApplyZeroOrNegativeMaxTime() {
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class AggregationOptionsTests {
|
|||||||
assertThat(aggregationOptions.isExplain()).isTrue();
|
assertThat(aggregationOptions.isExplain()).isTrue();
|
||||||
assertThat(aggregationOptions.getCursor()).contains(new Document("batchSize", 1));
|
assertThat(aggregationOptions.getCursor()).contains(new Document("batchSize", 1));
|
||||||
assertThat(aggregationOptions.getHint()).contains(dummyHint);
|
assertThat(aggregationOptions.getHint()).contains(dummyHint);
|
||||||
assertThat(aggregationOptions.getHintObject()).contains(dummyHint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAMONGO-1637, DATAMONGO-2153, DATAMONGO-1836
|
@Test // DATAMONGO-1637, DATAMONGO-2153, DATAMONGO-1836
|
||||||
@@ -74,7 +73,6 @@ class AggregationOptionsTests {
|
|||||||
assertThat(aggregationOptions.getCursorBatchSize()).isEqualTo(1);
|
assertThat(aggregationOptions.getCursorBatchSize()).isEqualTo(1);
|
||||||
assertThat(aggregationOptions.getComment()).contains("hola");
|
assertThat(aggregationOptions.getComment()).contains("hola");
|
||||||
assertThat(aggregationOptions.getHint()).contains(dummyHint);
|
assertThat(aggregationOptions.getHint()).contains(dummyHint);
|
||||||
assertThat(aggregationOptions.getHintObject()).contains(dummyHint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAMONGO-960, DATAMONGO-2153, DATAMONGO-1836
|
@Test // DATAMONGO-960, DATAMONGO-2153, DATAMONGO-1836
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.assertj.core.data.Offset;
|
import org.assertj.core.data.Offset;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -60,7 +61,6 @@ import org.springframework.data.mongodb.core.TestEntities;
|
|||||||
import org.springframework.data.mongodb.core.Venue;
|
import org.springframework.data.mongodb.core.Venue;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
|
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
|
||||||
import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities;
|
import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities;
|
||||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let;
|
|
||||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||||
@@ -90,7 +90,6 @@ import com.mongodb.client.MongoCollection;
|
|||||||
* @author Maninder Singh
|
* @author Maninder Singh
|
||||||
* @author Sergey Shcherbakov
|
* @author Sergey Shcherbakov
|
||||||
* @author Minsu Kim
|
* @author Minsu Kim
|
||||||
* @author Sangyong Choi
|
|
||||||
*/
|
*/
|
||||||
@ExtendWith(MongoTemplateExtension.class)
|
@ExtendWith(MongoTemplateExtension.class)
|
||||||
public class AggregationTests {
|
public class AggregationTests {
|
||||||
@@ -500,7 +499,7 @@ public class AggregationTests {
|
|||||||
/*
|
/*
|
||||||
//complex mongodb aggregation framework example from
|
//complex mongodb aggregation framework example from
|
||||||
https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state
|
https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state
|
||||||
|
|
||||||
db.zipcodes.aggregate(
|
db.zipcodes.aggregate(
|
||||||
{
|
{
|
||||||
$group: {
|
$group: {
|
||||||
@@ -1519,47 +1518,8 @@ public class AggregationTests {
|
|||||||
assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1");
|
assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-3322
|
|
||||||
@EnableIfMongoServerVersion(isGreaterThanEqual = "5.0")
|
|
||||||
void shouldLookupPeopleCorrectlyWithPipeline() {
|
|
||||||
createUsersWithReferencedPersons();
|
|
||||||
|
|
||||||
TypedAggregation<User> agg = newAggregation(User.class, //
|
|
||||||
lookup().from("person").localField("_id").foreignField("firstname").pipeline(match(where("firstname").is("u1"))).as("linkedPerson"), //
|
|
||||||
sort(ASC, "id"));
|
|
||||||
|
|
||||||
AggregationResults<Document> results = mongoTemplate.aggregate(agg, User.class, Document.class);
|
|
||||||
|
|
||||||
List<Document> mappedResults = results.getMappedResults();
|
|
||||||
|
|
||||||
Document firstItem = mappedResults.get(0);
|
|
||||||
|
|
||||||
assertThat(firstItem).containsEntry("_id", "u1");
|
|
||||||
assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-3322
|
|
||||||
@EnableIfMongoServerVersion(isGreaterThanEqual = "5.0")
|
|
||||||
void shouldLookupPeopleCorrectlyWithPipelineAndLet() {
|
|
||||||
createUsersWithReferencedPersons();
|
|
||||||
|
|
||||||
TypedAggregation<User> agg = newAggregation(User.class, //
|
|
||||||
lookup().from("person").localField("_id").foreignField("firstname").let(Let.ExpressionVariable.newVariable("the_id").forField("_id")).pipeline(
|
|
||||||
match(ctx -> new Document("$expr", new Document("$eq", List.of("$$the_id", "u1"))))).as("linkedPerson"),
|
|
||||||
sort(ASC, "id"));
|
|
||||||
|
|
||||||
AggregationResults<Document> results = mongoTemplate.aggregate(agg, User.class, Document.class);
|
|
||||||
|
|
||||||
List<Document> mappedResults = results.getMappedResults();
|
|
||||||
|
|
||||||
Document firstItem = mappedResults.get(0);
|
|
||||||
|
|
||||||
assertThat(firstItem).containsEntry("_id", "u1");
|
|
||||||
assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // DATAMONGO-1326
|
@Test // DATAMONGO-1326
|
||||||
void shouldGroupByAndLookupPeopleCorrectly() {
|
void shouldGroupByAndLookupPeopleCorectly() {
|
||||||
|
|
||||||
createUsersWithReferencedPersons();
|
createUsersWithReferencedPersons();
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,11 @@
|
|||||||
package org.springframework.data.mongodb.core.aggregation;
|
package org.springframework.data.mongodb.core.aggregation;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
|
||||||
import static org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable.*;
|
|
||||||
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link LookupOperation}.
|
* Unit tests for {@link LookupOperation}.
|
||||||
@@ -67,7 +62,7 @@ public class LookupOperationUnitTests {
|
|||||||
|
|
||||||
Document lookupClause = extractDocumentFromLookupOperation(lookupOperation);
|
Document lookupClause = extractDocumentFromLookupOperation(lookupOperation);
|
||||||
|
|
||||||
org.assertj.core.api.Assertions.assertThat(lookupClause).containsEntry("from", "a") //
|
assertThat(lookupClause).containsEntry("from", "a") //
|
||||||
.containsEntry("localField", "b") //
|
.containsEntry("localField", "b") //
|
||||||
.containsEntry("foreignField", "c") //
|
.containsEntry("foreignField", "c") //
|
||||||
.containsEntry("as", "d");
|
.containsEntry("as", "d");
|
||||||
@@ -119,7 +114,7 @@ public class LookupOperationUnitTests {
|
|||||||
|
|
||||||
Document lookupClause = extractDocumentFromLookupOperation(lookupOperation);
|
Document lookupClause = extractDocumentFromLookupOperation(lookupOperation);
|
||||||
|
|
||||||
org.assertj.core.api.Assertions.assertThat(lookupClause).containsEntry("from", "a") //
|
assertThat(lookupClause).containsEntry("from", "a") //
|
||||||
.containsEntry("localField", "b") //
|
.containsEntry("localField", "b") //
|
||||||
.containsEntry("foreignField", "c") //
|
.containsEntry("foreignField", "c") //
|
||||||
.containsEntry("as", "d");
|
.containsEntry("as", "d");
|
||||||
@@ -134,86 +129,4 @@ public class LookupOperationUnitTests {
|
|||||||
assertThat(lookupOperation.getFields().exposesSingleFieldOnly()).isTrue();
|
assertThat(lookupOperation.getFields().exposesSingleFieldOnly()).isTrue();
|
||||||
assertThat(lookupOperation.getFields().getField("d")).isNotNull();
|
assertThat(lookupOperation.getFields().getField("d")).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-3322
|
|
||||||
void buildsLookupWithLetAndPipeline() {
|
|
||||||
|
|
||||||
LookupOperation lookupOperation = LookupOperation.newLookup().from("warehouses")
|
|
||||||
.let(newVariable("order_item").forField("item"), newVariable("order_qty").forField("ordered"))
|
|
||||||
.pipeline(match(ctx -> new Document("$expr",
|
|
||||||
new Document("$and", List.of(Document.parse("{ $eq: [ \"$stock_item\", \"$$order_item\" ] }"),
|
|
||||||
Document.parse("{ $gte: [ \"$instock\", \"$$order_qty\" ] }"))))))
|
|
||||||
.as("stockdata");
|
|
||||||
|
|
||||||
assertThat(lookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("""
|
|
||||||
{ $lookup: {
|
|
||||||
from: "warehouses",
|
|
||||||
let: { order_item: "$item", order_qty: "$ordered" },
|
|
||||||
pipeline: [
|
|
||||||
{ $match:
|
|
||||||
{ $expr:
|
|
||||||
{ $and:
|
|
||||||
[
|
|
||||||
{ $eq: [ "$stock_item", "$$order_item" ] },
|
|
||||||
{ $gte: [ "$instock", "$$order_qty" ] }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
as: "stockdata"
|
|
||||||
}}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-3322
|
|
||||||
void buildsLookupWithJustPipeline() {
|
|
||||||
|
|
||||||
LookupOperation lookupOperation = LookupOperation.newLookup().from("holidays") //
|
|
||||||
.pipeline( //
|
|
||||||
match(Criteria.where("year").is(2018)), //
|
|
||||||
project().andExclude("_id").and(ctx -> new Document("name", "$name").append("date", "$date")).as("date"), //
|
|
||||||
Aggregation.replaceRoot("date") //
|
|
||||||
).as("holidays");
|
|
||||||
|
|
||||||
assertThat(lookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("""
|
|
||||||
{ $lookup:
|
|
||||||
{
|
|
||||||
from: "holidays",
|
|
||||||
pipeline: [
|
|
||||||
{ $match: { year: 2018 } },
|
|
||||||
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
|
|
||||||
{ $replaceRoot: { newRoot: "$date" } }
|
|
||||||
],
|
|
||||||
as: "holidays"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-3322
|
|
||||||
void buildsLookupWithLocalAndForeignFieldAsWellAsLetAndPipeline() {
|
|
||||||
|
|
||||||
LookupOperation lookupOperation = Aggregation.lookup().from("restaurants") //
|
|
||||||
.localField("restaurant_name")
|
|
||||||
.foreignField("name")
|
|
||||||
.let(newVariable("orders_drink").forField("drink")) //
|
|
||||||
.pipeline(match(ctx -> new Document("$expr", new Document("$in", List.of("$$orders_drink", "$beverages")))))
|
|
||||||
.as("matches");
|
|
||||||
|
|
||||||
assertThat(lookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("""
|
|
||||||
{ $lookup: {
|
|
||||||
from: "restaurants",
|
|
||||||
localField: "restaurant_name",
|
|
||||||
foreignField: "name",
|
|
||||||
let: { orders_drink: "$drink" },
|
|
||||||
pipeline: [{
|
|
||||||
$match: {
|
|
||||||
$expr: { $in: [ "$$orders_drink", "$beverages" ] }
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
as: "matches"
|
|
||||||
}}
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import static org.springframework.data.mongodb.core.messaging.SubscriptionUtils.
|
|||||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||||
|
|
||||||
|
import com.mongodb.client.model.ChangeStreamPreAndPostImagesOptions;
|
||||||
|
import com.mongodb.client.model.CreateCollectionOptions;
|
||||||
|
import com.mongodb.client.model.changestream.FullDocumentBeforeChange;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -39,10 +42,8 @@ import org.junit.jupiter.api.AfterAll;
|
|||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junitpioneer.jupiter.RepeatFailedTest;
|
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
import org.springframework.data.mongodb.core.ChangeStreamOptions;
|
import org.springframework.data.mongodb.core.ChangeStreamOptions;
|
||||||
import org.springframework.data.mongodb.core.CollectionOptions;
|
import org.springframework.data.mongodb.core.CollectionOptions;
|
||||||
@@ -61,7 +62,7 @@ import org.springframework.data.mongodb.test.util.Template;
|
|||||||
|
|
||||||
import com.mongodb.client.model.changestream.ChangeStreamDocument;
|
import com.mongodb.client.model.changestream.ChangeStreamDocument;
|
||||||
import com.mongodb.client.model.changestream.FullDocument;
|
import com.mongodb.client.model.changestream.FullDocument;
|
||||||
import com.mongodb.client.model.changestream.FullDocumentBeforeChange;
|
import org.junitpioneer.jupiter.RepeatFailedTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration test for subscribing to a {@link com.mongodb.operation.ChangeStreamBatchCursor} inside the
|
* Integration test for subscribing to a {@link com.mongodb.operation.ChangeStreamBatchCursor} inside the
|
||||||
@@ -701,7 +702,7 @@ class ChangeStreamTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4187
|
@Test // GH-4187
|
||||||
@Disabled("Flakey test failing occasionally due to timing issues")
|
@EnableIfMongoServerVersion(isLessThan = "6.0")
|
||||||
void readsFullDocumentBeforeChangeWhenOptionDeclaredRequiredAndMongoVersionIsLessThan6() throws InterruptedException {
|
void readsFullDocumentBeforeChangeWhenOptionDeclaredRequiredAndMongoVersionIsLessThan6() throws InterruptedException {
|
||||||
|
|
||||||
CollectingMessageListener<ChangeStreamDocument<Document>, User> messageListener = new CollectingMessageListener<>();
|
CollectingMessageListener<ChangeStreamDocument<Document>, User> messageListener = new CollectingMessageListener<>();
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ import org.springframework.data.geo.Metrics;
|
|||||||
import org.springframework.data.geo.Point;
|
import org.springframework.data.geo.Point;
|
||||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
|
||||||
|
|
||||||
import com.mongodb.ReadConcern;
|
|
||||||
import com.mongodb.ReadPreference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link NearQuery}.
|
* Unit tests for {@link NearQuery}.
|
||||||
@@ -233,58 +229,4 @@ public class NearQueryUnitTests {
|
|||||||
|
|
||||||
assertThat(query.toDocument()).containsEntry("maxDistance", 1000D).containsEntry("distanceMultiplier", 0.00062137D);
|
assertThat(query.toDocument()).containsEntry("maxDistance", 1000D).containsEntry("distanceMultiplier", 0.00062137D);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void fetchesReadPreferenceFromUnderlyingQueryObject() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0))
|
|
||||||
.query(new Query().withReadPreference(ReadPreference.nearest()));
|
|
||||||
|
|
||||||
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.nearest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void fetchesReadConcernFromUnderlyingQueryObject() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).query(new Query().withReadConcern(ReadConcern.SNAPSHOT));
|
|
||||||
|
|
||||||
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.SNAPSHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void usesReadPreferenceFromNearQueryIfUnderlyingQueryDoesNotDefineAny() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadPreference(ReadPreference.nearest())
|
|
||||||
.query(new Query());
|
|
||||||
|
|
||||||
assertThat(((Query) ReflectionTestUtils.getField(nearQuery, "query")).getReadPreference()).isNull();
|
|
||||||
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.nearest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void usesReadConcernFromNearQueryIfUnderlyingQueryDoesNotDefineAny() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadConcern(ReadConcern.SNAPSHOT).query(new Query());
|
|
||||||
|
|
||||||
assertThat(((Query) ReflectionTestUtils.getField(nearQuery, "query")).getReadConcern()).isNull();
|
|
||||||
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.SNAPSHOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void readPreferenceFromUnderlyingQueryOverridesNearQueryOne() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadPreference(ReadPreference.nearest())
|
|
||||||
.query(new Query().withReadPreference(ReadPreference.primary()));
|
|
||||||
|
|
||||||
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.primary());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // GH-4277
|
|
||||||
void readConcernFromUnderlyingQueryOverridesNearQueryOne() {
|
|
||||||
|
|
||||||
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadConcern(ReadConcern.SNAPSHOT)
|
|
||||||
.query(new Query().withReadConcern(ReadConcern.MAJORITY));
|
|
||||||
|
|
||||||
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.MAJORITY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,10 +342,7 @@ class QueryTests {
|
|||||||
source.limit(10);
|
source.limit(10);
|
||||||
source.setSortObject(new Document("_id", 1));
|
source.setSortObject(new Document("_id", 1));
|
||||||
|
|
||||||
ProxyFactory proxyFactory = new ProxyFactory(source);
|
Query target = Query.of((Query) new ProxyFactory(source).getProxy());
|
||||||
proxyFactory.setInterfaces(new Class[0]);
|
|
||||||
|
|
||||||
Query target = Query.of((Query) proxyFactory.getProxy());
|
|
||||||
|
|
||||||
compareQueries(target, source);
|
compareQueries(target, source);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.data.domain.Example;
|
import org.springframework.data.domain.Example;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||||
@@ -119,11 +120,7 @@ class SimpleReactiveMongoRepositoryUnitTests {
|
|||||||
@Test // DATAMONGO-1854
|
@Test // DATAMONGO-1854
|
||||||
void shouldAddDefaultCollationToFindOneForExampleIfPresent() {
|
void shouldAddDefaultCollationToFindOneForExampleIfPresent() {
|
||||||
|
|
||||||
when(entityInformation.getCollectionName()).thenReturn("testdummy");
|
when(mongoOperations.find(any(), any(), any())).thenReturn(flux);
|
||||||
doReturn(flux).when(mongoOperations).find(any(Query.class), eq(TestDummy.class), eq("testdummy"));
|
|
||||||
when(flux.buffer(anyInt())).thenReturn(flux);
|
|
||||||
when(flux.map(any())).thenReturn(flux);
|
|
||||||
when(flux.next()).thenReturn(mono);
|
|
||||||
|
|
||||||
Collation collation = Collation.of("en_US");
|
Collation collation = Collation.of("en_US");
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ This section provides some basic introduction to Spring and Document databases.
|
|||||||
[[get-started:first-steps:spring]]
|
[[get-started:first-steps:spring]]
|
||||||
== Learning Spring
|
== Learning Spring
|
||||||
|
|
||||||
Spring Data uses Spring framework's link:{springDocsUrl}/core.html[core] functionality, including:
|
Spring Data uses Spring framework's https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html[core] functionality, including:
|
||||||
|
|
||||||
* link:{springDocsUrl}/core.html#beans[IoC] container
|
* https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#beans[IoC] container
|
||||||
* link:{springDocsUrl}/core.html#validation[type conversion system]
|
* https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#validation[type conversion system]
|
||||||
* link:{springDocsUrl}/core.html#expressions[expression language]
|
* https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#expressions[expression language]
|
||||||
* link:{springDocsUrl}/integration.html#jmx[JMX integration]
|
* https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/integration.html#jmx[JMX integration]
|
||||||
* link:{springDocsUrl}/data-access.html#dao-exceptions[DAO exception hierarchy].
|
* https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/data-access.html#dao-exceptions[DAO exception hierarchy].
|
||||||
|
|
||||||
While you need not know the Spring APIs, understanding the concepts behind them is important. At a minimum, the idea behind Inversion of Control (IoC) should be familiar, and you should be familiar with whatever IoC container you choose to use.
|
While you need not know the Spring APIs, understanding the concepts behind them is important. At a minimum, the idea behind Inversion of Control (IoC) should be familiar, and you should be familiar with whatever IoC container you choose to use.
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ CAUTION: Changing state of `MongoTemplate` during runtime (as you might think wo
|
|||||||
[[mongo.transactions.tx-manager]]
|
[[mongo.transactions.tx-manager]]
|
||||||
== Transactions with `MongoTransactionManager`
|
== Transactions with `MongoTransactionManager`
|
||||||
|
|
||||||
`MongoTransactionManager` is the gateway to the well known Spring transaction support. It lets applications use link:{springDocsUrl}/data-access.html#transaction[the managed transaction features of Spring].
|
`MongoTransactionManager` is the gateway to the well known Spring transaction support. It lets applications use https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/data-access.html#transaction[the managed transaction features of Spring].
|
||||||
The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` detects the session and operates on these resources which are associated with the transaction accordingly. `MongoTemplate` can also participate in other, ongoing transactions. The following example shows how to create and use transactions with a `MongoTransactionManager`:
|
The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` detects the session and operates on these resources which are associated with the transaction accordingly. `MongoTemplate` can also participate in other, ongoing transactions. The following example shows how to create and use transactions with a `MongoTransactionManager`:
|
||||||
|
|
||||||
.Transactions with `MongoTransactionManager`
|
.Transactions with `MongoTransactionManager`
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[[mongo.jmx]]
|
[[mongo.jmx]]
|
||||||
= JMX support
|
= JMX support
|
||||||
|
|
||||||
The JMX support for MongoDB exposes the results of running the 'serverStatus' command on the admin database for a single MongoDB server instance. It also exposes an administrative MBean, `MongoAdmin`, that lets you perform administrative operations, such as dropping or creating a database. The JMX features build upon the JMX feature set available in the Spring Framework. See link:{springDocsUrl}/integration.html#jmx[here] for more details.
|
The JMX support for MongoDB exposes the results of running the 'serverStatus' command on the admin database for a single MongoDB server instance. It also exposes an administrative MBean, `MongoAdmin`, that lets you perform administrative operations, such as dropping or creating a database. The JMX features build upon the JMX feature set available in the Spring Framework. See https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/integration.html#jmx[here] for more details.
|
||||||
|
|
||||||
[[mongodb:jmx-configuration]]
|
[[mongodb:jmx-configuration]]
|
||||||
== MongoDB JMX Configuration
|
== MongoDB JMX Configuration
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ The snippet above is handy for providing simple type hints. To gain more fine-gr
|
|||||||
|
|
||||||
The `MappingMongoConverter` checks to see if any Spring converters 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`.
|
The `MappingMongoConverter` checks to see if any Spring converters 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 link:{springDocsUrl}/core.html#validation[here].
|
NOTE: For more information on the Spring type conversion service, see the reference docs https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#validation[here].
|
||||||
|
|
||||||
[[mongo.custom-converters.writer]]
|
[[mongo.custom-converters.writer]]
|
||||||
=== Saving by Using a Registered Spring Converter
|
=== Saving by Using a Registered Spring Converter
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
The repository layer offers means to interact with <<mongo.aggregation, the aggregation framework>> via annotated repository query methods.
|
The repository layer offers means to interact with <<mongo.aggregation, the aggregation framework>> via annotated repository query methods.
|
||||||
Similar to the <<mongodb.repositories.queries.json-based, JSON based queries>>, you can define a pipeline using the `org.springframework.data.mongodb.repository.Aggregation` annotation.
|
Similar to the <<mongodb.repositories.queries.json-based, JSON based queries>>, you can define a pipeline using the `org.springframework.data.mongodb.repository.Aggregation` annotation.
|
||||||
The definition may contain simple placeholders like `?0` as well as link:{springDocsUrl}/core.html#expressions[SpEL expressions] `?#{ … }`.
|
The definition may contain simple placeholders like `?0` as well as https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#expressions[SpEL expressions] `?#{ … }`.
|
||||||
|
|
||||||
.Aggregating Repository Method
|
.Aggregating Repository Method
|
||||||
====
|
====
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ public class AppConfig {
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
This approach lets you use the standard `com.mongodb.client.MongoClient` instance, with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.client.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 the use of `@Repository` is described in link:{springDocsUrl}/data-access.html[Spring's DAO support features].
|
This approach lets you use the standard `com.mongodb.client.MongoClient` instance, with the container using Spring's `MongoClientFactoryBean`. As compared to instantiating a `com.mongodb.client.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 the use of `@Repository` is described in https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/data-access.html[Spring's DAO support features].
|
||||||
|
|
||||||
The following example shows an example of a Java-based bean metadata that supports exception translation on `@Repository` annotated classes:
|
The following example shows an example of a Java-based bean metadata that supports exception translation on `@Repository` annotated classes:
|
||||||
|
|
||||||
@@ -2258,7 +2258,7 @@ Therefore a given `Query` will be rewritten for `count` operations using `Reacti
|
|||||||
|
|
||||||
You can query MongoDB by using Map-Reduce, which is useful for batch processing, for data aggregation, and for when the query language does not fulfill your needs.
|
You can query MongoDB by using Map-Reduce, which is useful for batch processing, for data aggregation, and for when the query language does not fulfill your needs.
|
||||||
|
|
||||||
Spring provides integration with MongoDB's Map-Reduce by providing methods on `MongoOperations` to simplify the creation and running of Map-Reduce operations.It can convert the results of a Map-Reduce operation to a POJO and integrates with Spring's link:{springDocsUrl}/core.html#resources[Resource abstraction].This lets you place your JavaScript files on the file system, classpath, HTTP server, or any other Spring Resource implementation and then reference the JavaScript resources through an easy URI style syntax -- for example, `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 running of Map-Reduce operations.It can convert the results of a Map-Reduce operation to a POJO and integrates with Spring's https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/core.html#resources[Resource abstraction].This lets you place your JavaScript files on the file system, classpath, HTTP server, or any other Spring Resource implementation and then reference the JavaScript resources through an easy URI style syntax -- for example, `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]]
|
[[mongo.mapreduce.example]]
|
||||||
=== Example Usage
|
=== Example Usage
|
||||||
@@ -2664,7 +2664,7 @@ include::./mongo-entity-callbacks.adoc[leveloffset=+2]
|
|||||||
|
|
||||||
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 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 link:{springDocsUrl}/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 that you can be sure 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 that no information is lost.
|
The motivation behind mapping to Spring's https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/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 that you can be sure 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 that 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, and 12012 to `InvalidDataAccessApiUsageException`. Look into the implementation for more details on the mapping.
|
Some of the mappings performed by the `MongoExceptionTranslator` are `com.mongodb.Network to DataAccessResourceFailureException` and `MongoException` error codes 1003, 12001, 12010, 12011, and 12012 to `InvalidDataAccessApiUsageException`. Look into the implementation for more details on the mapping.
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ public class AppConfig {
|
|||||||
|
|
||||||
This approach lets you use the standard `com.mongodb.reactivestreams.client.MongoClient` API (which you may already know).
|
This approach lets you use the standard `com.mongodb.reactivestreams.client.MongoClient` API (which you may already know).
|
||||||
|
|
||||||
An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container by 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 link:{springDocsUrl}/data-access.html[Spring's DAO support features].
|
An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container by 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 https://docs.spring.io/spring-framework/docs/{springVersion}/reference/html/data-access.html[Spring's DAO support features].
|
||||||
|
|
||||||
The following example shows Java-based bean metadata that supports exception translation on `@Repository` annotated classes:
|
The following example shows Java-based bean metadata that supports exception translation on `@Repository` annotated classes:
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Spring Data MongoDB 4.1 M2 (2023.0.0)
|
Spring Data MongoDB 4.0.2 (2022.0.2)
|
||||||
Copyright (c) [2010-2019] Pivotal Software, Inc.
|
Copyright (c) [2010-2019] Pivotal Software, Inc.
|
||||||
|
|
||||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||||
|
|||||||
Reference in New Issue
Block a user