Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46b54da74b | ||
|
|
f35392f6dd | ||
|
|
5e241c6ea5 | ||
|
|
1078294e3e | ||
|
|
e3ec68b07c | ||
|
|
f93d7e7359 | ||
|
|
aacd04a42f | ||
|
|
59de671387 | ||
|
|
54101a4619 | ||
|
|
e9df28024a | ||
|
|
48c6e1eed5 | ||
|
|
9cdc79a89a | ||
|
|
f2bf878fbe | ||
|
|
4d8019abca | ||
|
|
47864e0cf9 | ||
|
|
ed83c7625e | ||
|
|
0d4b5de2a5 | ||
|
|
24e9841beb | ||
|
|
f130616e68 | ||
|
|
32da9f4336 | ||
|
|
5b83286da4 |
4
.mvn/wrapper/maven-wrapper.properties
vendored
4
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1,2 +1,2 @@
|
||||
#Tue Feb 22 13:59:04 CET 2022
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
|
||||
#Fri Jun 03 09:39:35 CEST 2022
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
|
||||
|
||||
86
Jenkinsfile
vendored
86
Jenkinsfile
vendored
@@ -116,15 +116,13 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.0:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.0:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,15 +147,13 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.4:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.4:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,15 +169,13 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-5.0:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-5.0:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,15 +191,13 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.4:${p['java.lts.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-4.4:${p['java.lts.tag']}").inside(p['docker.java.inside.basic']) {
|
||||
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
|
||||
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
|
||||
sh 'sleep 10'
|
||||
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
|
||||
sh 'sleep 15'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,18 +224,16 @@ pipeline {
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -v'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
"-Dartifactory.staging-repository=libs-snapshot-local " +
|
||||
"-Dartifactory.build-name=spring-data-mongodb " +
|
||||
"-Dartifactory.build-number=${BUILD_NUMBER} " +
|
||||
'-Dmaven.test.skip=true clean deploy -U -B'
|
||||
}
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -v'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
"-Dartifactory.staging-repository=libs-snapshot-local " +
|
||||
"-Dartifactory.build-name=spring-data-mongodb " +
|
||||
"-Dartifactory.build-number=${BUILD_NUMBER} " +
|
||||
'-Dmaven.test.skip=true clean deploy -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
image:https://spring.io/badges/spring-data-mongodb/ga.svg[Spring Data MongoDB,link=https://projects.spring.io/spring-data-mongodb#quick-start] image:https://spring.io/badges/spring-data-mongodb/snapshot.svg[Spring Data MongoDB,link=https://projects.spring.io/spring-data-mongodb#quick-start]
|
||||
image:https://spring.io/badges/spring-data-mongodb/ga.svg[Spring Data MongoDB,link=https://spring.io/projects/spring-data-mongodb#quick-start] image:https://spring.io/badges/spring-data-mongodb/snapshot.svg[Spring Data MongoDB,link=https://spring.io/projects/spring-data-mongodb#quick-start]
|
||||
|
||||
= Spring Data MongoDB image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-mongodb%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-mongodb/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]]
|
||||
|
||||
The primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
The primary goal of the https://spring.io/projects/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
|
||||
The Spring Data MongoDB project aims to provide a familiar and consistent Spring-based programming model for new datastores while retaining store-specific features and capabilities.
|
||||
The Spring Data MongoDB project provides integration with the MongoDB document database.
|
||||
|
||||
25
pom.xml
25
pom.xml
@@ -5,17 +5,17 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>3.4.0-RC1</version>
|
||||
<version>3.4.1</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
<description>MongoDB support for Spring Data</description>
|
||||
<url>https://projects.spring.io/spring-data-mongodb</url>
|
||||
<url>https://spring.io/projects/spring-data-mongodb</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.7.0-RC1</version>
|
||||
<version>2.7.1</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -26,8 +26,8 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>2.7.0-RC1</springdata.commons>
|
||||
<mongo>4.6.0</mongo>
|
||||
<springdata.commons>2.7.1</springdata.commons>
|
||||
<mongo>4.6.1</mongo>
|
||||
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
|
||||
<jmh.version>1.19</jmh.version>
|
||||
</properties>
|
||||
@@ -112,6 +112,17 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/spring-projects/spring-data-mongodb.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:spring-projects/spring-data-mongodb.git</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-data-mongodb</url>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/spring-projects/spring-data-mongodb/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>benchmarks</id>
|
||||
@@ -134,8 +145,8 @@
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
<id>spring-libs-release</id>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype-libs-snapshot</id>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>3.4.0-RC1</version>
|
||||
<version>3.4.1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>3.4.0-RC1</version>
|
||||
<version>3.4.1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>3.4.0-RC1</version>
|
||||
<version>3.4.1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -965,9 +965,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass());
|
||||
TypeInformation<?> type = prop.getTypeInformation();
|
||||
|
||||
if (conversions.hasPropertyValueConverter(prop)) {
|
||||
if (conversions.getPropertyValueConversions().hasValueConverter(prop)) {
|
||||
accessor.put(prop,
|
||||
conversions.getPropertyValueConverter(prop).write(obj, new MongoConversionContext(prop, this)));
|
||||
conversions.getPropertyValueConversions().getValueConverter(prop).write(obj, new MongoConversionContext(prop, this)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1301,9 +1301,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersistentProperty property) {
|
||||
DocumentAccessor accessor = new DocumentAccessor(bson);
|
||||
|
||||
if (conversions.hasPropertyValueConverter(property)) {
|
||||
if (conversions.getPropertyValueConversions().hasValueConverter(property)) {
|
||||
accessor.put(property,
|
||||
conversions.getPropertyValueConverter(property).write(value, new MongoConversionContext(property, this)));
|
||||
conversions.getPropertyValueConversions().getValueConverter(property)
|
||||
.write(value, new MongoConversionContext(property, this)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1970,8 +1971,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return null;
|
||||
}
|
||||
|
||||
if (context.conversions.hasPropertyValueConverter(property)) {
|
||||
return (T) context.conversions.getPropertyValueConverter(property).read(value,
|
||||
if (context.conversions.getPropertyValueConversions().hasValueConverter(property)) {
|
||||
return (T) context.conversions.getPropertyValueConversions().getValueConverter(property).read(value,
|
||||
new MongoConversionContext(property, context.sourceConverter));
|
||||
}
|
||||
|
||||
|
||||
@@ -434,8 +434,9 @@ public class QueryMapper {
|
||||
|
||||
Object value = applyFieldTargetTypeHintToValue(documentField, sourceValue);
|
||||
|
||||
if(documentField.getProperty() != null && converter.getCustomConversions().hasPropertyValueConverter(documentField.getProperty())) {
|
||||
return converter.getCustomConversions().getPropertyValueConverter(documentField.getProperty()).write(value, new MongoConversionContext(documentField.getProperty(), converter));
|
||||
if(documentField.getProperty() != null && converter.getCustomConversions().getPropertyValueConversions().hasValueConverter(documentField.getProperty())) {
|
||||
return converter.getCustomConversions().getPropertyValueConversions().getValueConverter(documentField.getProperty())
|
||||
.write(value, new MongoConversionContext(documentField.getProperty(), converter));
|
||||
}
|
||||
|
||||
if (documentField.isIdField() && !documentField.isAssociation()) {
|
||||
|
||||
@@ -124,9 +124,10 @@ public class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set number of documents to skip before returning results.
|
||||
* Set number of documents to skip before returning results. Use {@literal zero} or a {@literal negative} value to
|
||||
* avoid skipping.
|
||||
*
|
||||
* @param skip
|
||||
* @param skip number of documents to skip. Use {@literal zero} or a {@literal negative} value to avoid skipping.
|
||||
* @return this.
|
||||
*/
|
||||
public Query skip(long skip) {
|
||||
@@ -135,9 +136,10 @@ public class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of returned documents to {@code limit}.
|
||||
* Limit the number of returned documents to {@code limit}. A {@literal zero} or {@literal negative} value is
|
||||
* considered as unlimited.
|
||||
*
|
||||
* @param limit
|
||||
* @param limit number of documents to return. Use {@literal zero} or {@literal negative} for unlimited.
|
||||
* @return this.
|
||||
*/
|
||||
public Query limit(int limit) {
|
||||
@@ -314,7 +316,7 @@ public class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of documents to skip.
|
||||
* Get the number of documents to skip. {@literal Zero} or a {@literal negative} value indicates no skip.
|
||||
*
|
||||
* @return number of documents to skip
|
||||
*/
|
||||
@@ -323,7 +325,8 @@ public class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of documents to be return.
|
||||
* Get the maximum number of documents to be return. {@literal Zero} or a {@literal negative} value indicates no
|
||||
* limit.
|
||||
*
|
||||
* @return number of documents to return.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2022 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.util.json;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.3.5
|
||||
*/
|
||||
class EvaluationContextExpressionEvaluator implements SpELExpressionEvaluator {
|
||||
|
||||
ValueProvider valueProvider;
|
||||
ExpressionParser expressionParser;
|
||||
Supplier<EvaluationContext> evaluationContext;
|
||||
|
||||
public EvaluationContextExpressionEvaluator(ValueProvider valueProvider, ExpressionParser expressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this.valueProvider = valueProvider;
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T evaluate(String expression) {
|
||||
return evaluateExpression(expression, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext(String expressionString) {
|
||||
return evaluationContext != null ? evaluationContext.get() : new StandardEvaluationContext();
|
||||
}
|
||||
|
||||
public SpelExpression getParsedExpression(String expressionString) {
|
||||
return (SpelExpression) (expressionParser != null ? expressionParser : new SpelExpressionParser())
|
||||
.parseExpression(expressionString);
|
||||
}
|
||||
|
||||
public <T> T evaluateExpression(String expressionString, Map<String, Object> variables) {
|
||||
|
||||
SpelExpression expression = getParsedExpression(expressionString);
|
||||
EvaluationContext ctx = getEvaluationContext(expressionString);
|
||||
variables.entrySet().forEach(entry -> ctx.setVariable(entry.getKey(), entry.getValue()));
|
||||
|
||||
Object result = expression.getValue(ctx, Object.class);
|
||||
return (T) result;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.util.json;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -58,13 +59,7 @@ public class ParameterBindingContext {
|
||||
*/
|
||||
public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
return (T) expressionParser.parseExpression(expressionString).getValue(evaluationContext.get(), Object.class);
|
||||
}
|
||||
});
|
||||
this(valueProvider, new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, evaluationContext));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,20 +82,20 @@ public class ParameterBindingContext {
|
||||
* @return
|
||||
* @since 3.1
|
||||
*/
|
||||
public static ParameterBindingContext forExpressions(ValueProvider valueProvider,
|
||||
ExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
|
||||
public static ParameterBindingContext forExpressions(ValueProvider valueProvider, ExpressionParser expressionParser,
|
||||
Function<ExpressionDependencies, EvaluationContext> contextFunction) {
|
||||
|
||||
return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
return new ParameterBindingContext(valueProvider,
|
||||
new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, null) {
|
||||
|
||||
Expression expression = expressionParser.parseExpression(expressionString);
|
||||
ExpressionDependencies dependencies = ExpressionDependencies.discover(expression);
|
||||
EvaluationContext evaluationContext = contextFunction.apply(dependencies);
|
||||
@Override
|
||||
public EvaluationContext getEvaluationContext(String expressionString) {
|
||||
|
||||
return (T) expression.getValue(evaluationContext, Object.class);
|
||||
}
|
||||
});
|
||||
Expression expression = getParsedExpression(expressionString);
|
||||
ExpressionDependencies dependencies = ExpressionDependencies.discover(expression);
|
||||
return contextFunction.apply(dependencies);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -113,6 +108,16 @@ public class ParameterBindingContext {
|
||||
return expressionEvaluator.evaluate(expressionString);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object evaluateExpression(String expressionString, Map<String, Object> variables) {
|
||||
|
||||
if (expressionEvaluator instanceof EvaluationContextExpressionEvaluator) {
|
||||
return ((EvaluationContextExpressionEvaluator) expressionEvaluator).evaluateExpression(expressionString,
|
||||
variables);
|
||||
}
|
||||
return expressionEvaluator.evaluate(expressionString);
|
||||
}
|
||||
|
||||
public ValueProvider getValueProvider() {
|
||||
return valueProvider;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,12 @@ import static java.lang.String.*;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
@@ -64,6 +68,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
private static final Pattern ENTIRE_QUERY_BINDING_PATTERN = Pattern.compile("^\\?(\\d+)$|^[\\?:]#\\{.*\\}$");
|
||||
private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+)");
|
||||
private static final Pattern EXPRESSION_BINDING_PATTERN = Pattern.compile("[\\?:]#\\{.*\\}");
|
||||
private static final Pattern SPEL_PARAMETER_BINDING_PATTERN = Pattern.compile("('\\?(\\d+)'|\\?(\\d+))");
|
||||
|
||||
private final ParameterBindingContext bindingContext;
|
||||
|
||||
@@ -372,14 +377,24 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
String binding = regexMatcher.group();
|
||||
String expression = binding.substring(3, binding.length() - 1);
|
||||
|
||||
Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression);
|
||||
Matcher inSpelMatcher = SPEL_PARAMETER_BINDING_PATTERN.matcher(expression); // ?0 '?0'
|
||||
Map<String, Object> innerSpelVariables = new HashMap<>();
|
||||
|
||||
while (inSpelMatcher.find()) {
|
||||
|
||||
int index = computeParameterIndex(inSpelMatcher.group());
|
||||
expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString());
|
||||
String group = inSpelMatcher.group();
|
||||
int index = computeParameterIndex(group);
|
||||
Object value = getBindableValueForIndex(index);
|
||||
String varName = "__QVar" + innerSpelVariables.size();
|
||||
expression = expression.replace(group, "#" + varName);
|
||||
if(group.startsWith("'")) { // retain the string semantic
|
||||
innerSpelVariables.put(varName, nullSafeToString(value));
|
||||
} else {
|
||||
innerSpelVariables.put(varName, value);
|
||||
}
|
||||
}
|
||||
|
||||
Object value = evaluateExpression(expression);
|
||||
Object value = evaluateExpression(expression, innerSpelVariables);
|
||||
bindableValue.setValue(value);
|
||||
bindableValue.setType(bsonTypeForValue(value));
|
||||
return bindableValue;
|
||||
@@ -408,14 +423,24 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
String binding = regexMatcher.group();
|
||||
String expression = binding.substring(3, binding.length() - 1);
|
||||
|
||||
Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression);
|
||||
Matcher inSpelMatcher = SPEL_PARAMETER_BINDING_PATTERN.matcher(expression);
|
||||
Map<String, Object> innerSpelVariables = new HashMap<>();
|
||||
|
||||
while (inSpelMatcher.find()) {
|
||||
|
||||
int index = computeParameterIndex(inSpelMatcher.group());
|
||||
expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString());
|
||||
String group = inSpelMatcher.group();
|
||||
int index = computeParameterIndex(group);
|
||||
Object value = getBindableValueForIndex(index);
|
||||
String varName = "__QVar" + innerSpelVariables.size();
|
||||
expression = expression.replace(group, "#" + varName);
|
||||
if(group.startsWith("'")) { // retain the string semantic
|
||||
innerSpelVariables.put(varName, nullSafeToString(value));
|
||||
} else {
|
||||
innerSpelVariables.put(varName, value);
|
||||
}
|
||||
}
|
||||
|
||||
computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression)));
|
||||
computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression, innerSpelVariables)));
|
||||
|
||||
bindableValue.setValue(computedValue);
|
||||
bindableValue.setType(BsonType.STRING);
|
||||
@@ -452,7 +477,7 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
}
|
||||
|
||||
private static int computeParameterIndex(String parameter) {
|
||||
return NumberUtils.parseNumber(parameter.replace("?", ""), Integer.class);
|
||||
return NumberUtils.parseNumber(parameter.replace("?", "").replace("'", ""), Integer.class);
|
||||
}
|
||||
|
||||
private Object getBindableValueForIndex(int index) {
|
||||
@@ -504,7 +529,12 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
|
||||
@Nullable
|
||||
private Object evaluateExpression(String expressionString) {
|
||||
return bindingContext.evaluateExpression(expressionString);
|
||||
return bindingContext.evaluateExpression(expressionString, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object evaluateExpression(String expressionString, Map<String,Object> variables) {
|
||||
return bindingContext.evaluateExpression(expressionString, variables);
|
||||
}
|
||||
|
||||
// Spring Data Customization END
|
||||
|
||||
@@ -61,7 +61,7 @@ class MongoCustomConversionsUnitTests {
|
||||
registry -> registry.registerConverter(Foo.class, "name", mock(PropertyValueConverter.class)));
|
||||
});
|
||||
|
||||
assertThat(conversions.hasPropertyValueConverter(persistentProperty)).isTrue();
|
||||
assertThat(conversions.getPropertyValueConversions().hasValueConverter(persistentProperty)).isTrue();
|
||||
}
|
||||
|
||||
static class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {
|
||||
|
||||
@@ -25,14 +25,15 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.Document;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
@@ -369,11 +370,11 @@ class ParameterBindingJsonReaderUnitTests {
|
||||
new SpelExpressionParser());
|
||||
}
|
||||
|
||||
@Test // GH-3871
|
||||
public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonString() {
|
||||
@Test // GH-3871, GH-4089
|
||||
public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsDocument() {
|
||||
|
||||
Object[] args = new Object[] { "expected", "unexpected" };
|
||||
String json = "?#{ true ? \"{ 'name': ?0 }\" : \"{ 'name' : ?1 }\" }";
|
||||
String json = "?#{ true ? { 'name': ?0 } : { 'name' : ?1 } }";
|
||||
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
|
||||
.getEvaluationContext(args);
|
||||
|
||||
@@ -384,25 +385,27 @@ class ParameterBindingJsonReaderUnitTests {
|
||||
assertThat(target).isEqualTo(new Document("name", "expected"));
|
||||
}
|
||||
|
||||
@Test // GH-3871
|
||||
public void throwsExceptionWhenbindEntireQueryUsingSpelExpressionResultsInInvalidJsonString() {
|
||||
@Test // GH-3871, GH-4089
|
||||
public void throwsExceptionWhenBindEntireQueryUsingSpelExpressionIsMalFormatted() {
|
||||
|
||||
Object[] args = new Object[] { "expected", "unexpected" };
|
||||
String json = "?#{ true ? \"{ 'name': ?0 { }\" : \"{ 'name' : ?1 }\" }";
|
||||
String json = "?#{ true ? { 'name': ?0 { } } : { 'name' : ?1 } }";
|
||||
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
|
||||
.getEvaluationContext(args);
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json,
|
||||
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
|
||||
assertThatExceptionOfType(ParseException.class).isThrownBy(() -> {
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json,
|
||||
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()));
|
||||
new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-3871
|
||||
@Test // GH-3871, GH-4089
|
||||
public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonStringContainingUUID() {
|
||||
|
||||
Object[] args = new Object[] { "UUID('cfbca728-4e39-4613-96bc-f920b5c37e16')", "unexpected" };
|
||||
String json = "?#{ true ? \"{ 'name': ?0 }\" : \"{ 'name' : ?1 }\" }";
|
||||
Object[] args = new Object[] { UUID.fromString("cfbca728-4e39-4613-96bc-f920b5c37e16"), "unexpected" };
|
||||
String json = "?#{ true ? { 'name': ?0 } : { 'name' : ?1 } }";
|
||||
StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT
|
||||
.getEvaluationContext(args);
|
||||
|
||||
@@ -411,7 +414,7 @@ class ParameterBindingJsonReaderUnitTests {
|
||||
|
||||
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
|
||||
|
||||
assertThat(target.get("name")).isInstanceOf(BsonBinary.class);
|
||||
assertThat(target.get("name")).isInstanceOf(UUID.class);
|
||||
}
|
||||
|
||||
@Test // GH-3871
|
||||
@@ -481,6 +484,69 @@ class ParameterBindingJsonReaderUnitTests {
|
||||
assertThat(target).isEqualTo(new Document("parent", null));
|
||||
}
|
||||
|
||||
|
||||
@Test // GH-4089
|
||||
void retainsSpelArgumentTypeViaArgumentIndex() {
|
||||
|
||||
String source = "new java.lang.Object()";
|
||||
Document target = parse("{ arg0 : ?#{[0]} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void retainsSpelArgumentTypeViaParameterPlaceholder() {
|
||||
|
||||
String source = "new java.lang.Object()";
|
||||
Document target = parse("{ arg0 : :#{?0} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void errorsOnNonDocument() {
|
||||
|
||||
String source = "new java.lang.Object()";
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> parse(":#{?0}", source));
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void bindsFullDocument() {
|
||||
|
||||
Document source = new Document();
|
||||
assertThat(parse(":#{?0}", source)).isSameAs(source);
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void enforcesStringSpelArgumentTypeViaParameterPlaceholderWhenQuoted() {
|
||||
|
||||
Integer source = 10;
|
||||
Document target = parse("{ arg0 : :#{'?0'} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo("10");
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void enforcesSpelArgumentTypeViaParameterPlaceholderWhenQuoted() {
|
||||
|
||||
String source = "new java.lang.Object()";
|
||||
Document target = parse("{ arg0 : :#{'?0'} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void retainsSpelArgumentTypeViaParameterPlaceholderWhenValueContainsSingleQuotes() {
|
||||
|
||||
String source = "' + new java.lang.Object() + '";
|
||||
Document target = parse("{ arg0 : :#{?0} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test // GH-4089
|
||||
void retainsSpelArgumentTypeViaParameterPlaceholderWhenValueContainsDoubleQuotes() {
|
||||
|
||||
String source = "\\\" + new java.lang.Object() + \\\"";
|
||||
Document target = parse("{ arg0 : :#{?0} }", source);
|
||||
assertThat(target.get("arg0")).isEqualTo(source);
|
||||
}
|
||||
|
||||
private static Document parse(String json, Object... args) {
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
|
||||
|
||||
@@ -49,6 +49,7 @@ In terms of document stores, you need at least version 3.6 of https://www.mongod
|
||||
The following compatibility matrix summarizes Spring Data versions to MongoDB driver/database versions.
|
||||
Database versions show the highest supported server version that pass the Spring Data test suite.
|
||||
You can use newer server versions unless your application uses functionality that is affected by <<compatibility.changes,changes in the MongoDB server>>.
|
||||
See also the https://www.mongodb.com/docs/drivers/java/sync/current/compatibility/[official MongoDB driver compatibility matrix] for driver- and server version compatibility.
|
||||
|
||||
[cols="h,m,m,m", options="header"]
|
||||
|===
|
||||
@@ -58,6 +59,16 @@ You can use newer server versions unless your application uses functionality tha
|
||||
|Driver Version
|
||||
|Server Version
|
||||
|
||||
|2021.2
|
||||
|3.4.x
|
||||
|4.6.x
|
||||
|5.0.x
|
||||
|
||||
|2021.1
|
||||
|3.3.x
|
||||
|4.4.x
|
||||
|5.0.x
|
||||
|
||||
|2021.0
|
||||
|3.2.x
|
||||
|4.1.x
|
||||
@@ -115,4 +126,4 @@ Professional Support :: Professional, from-the-source support, with guaranteed r
|
||||
[[get-started:up-to-date]]
|
||||
== Following Development
|
||||
|
||||
For information on the Spring Data Mongo source code repository, nightly builds, and snapshot artifacts, see the Spring Data Mongo https://projects.spring.io/spring-data-mongodb/[homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on https://stackoverflow.com/questions/tagged/spring-data[Stack Overflow]. To follow developer activity, look for the mailing list information on the Spring Data Mongo https://projects.spring.io/spring-data-mongodb/[homepage]. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data https://github.com/spring-projects/spring-data-mongodb/issues[issue tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community https://spring.io[Portal]. You can also follow the Spring https://spring.io/blog[blog] or the project team on Twitter (https://twitter.com/SpringData[SpringData]).
|
||||
For information on the Spring Data Mongo source code repository, nightly builds, and snapshot artifacts, see the Spring Data Mongo https://spring.io/projects/spring-data-mongodb/[homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on https://stackoverflow.com/questions/tagged/spring-data[Stack Overflow]. To follow developer activity, look for the mailing list information on the Spring Data Mongo https://spring.io/projects/spring-data-mongodb/[homepage]. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data https://github.com/spring-projects/spring-data-mongodb/issues[issue tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community https://spring.io[Portal]. You can also follow the Spring https://spring.io/blog[blog] or the project team on Twitter (https://twitter.com/SpringData[SpringData]).
|
||||
|
||||
@@ -162,7 +162,7 @@ calling `get()` before the actual conversion
|
||||
|
||||
| `URL`
|
||||
| converter
|
||||
| `{"website" : "https://projects.spring.io/spring-data-mongodb/" }`
|
||||
| `{"website" : "https://spring.io/projects/spring-data-mongodb/" }`
|
||||
|
||||
| `Locale`
|
||||
| converter
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[[mongo.property-converters]]
|
||||
== Property Converters - Mapping specific fields
|
||||
|
||||
While <<mongo.custom-converters, type-based conversion>> already offers ways to influence the conversion and representation of certain types within the target store it has its limitations when only certain values or properties of a particular type should be considered for conversion.
|
||||
Property-based converters allow configuring conversion rules on a per-property basis, either declarative, via `@ValueConverter`, or programmatic by registering a `PropertyValueConverter` for a specific property.
|
||||
While <<mongo.custom-converters, type-based conversion>> already offers ways to influence the conversion and representation of certain types within the target store, it has limitations when only certain values or properties of a particular type should be considered for conversion.
|
||||
Property-based converters allow configuring conversion rules on a per-property basis, either declaratively (via `@ValueConverter`) or programmatically (by registering a `PropertyValueConverter` for a specific property).
|
||||
|
||||
A `PropertyValueConverter` can transform a given value into its store representation (**write**) and back (**read**) as shown in the snippet below.
|
||||
The additional `ValueConversionContext` provides additional information, such as mapping metadata and direct `read`/`write` methods.
|
||||
A `PropertyValueConverter` can transform a given value into its store representation (write) and back (read) as the following listing shows.
|
||||
The additional `ValueConversionContext` provides additional information, such as mapping metadata and direct `read` and `write` methods.
|
||||
|
||||
.A simple PropertyValueConverter
|
||||
====
|
||||
@@ -26,18 +26,18 @@ class ReversingValueConverter implements PropertyValueConverter<String, String,
|
||||
----
|
||||
====
|
||||
|
||||
`PropertyValueConverter` instances can be obtained via `CustomConversions#getPropertyValueConverter(…)` delegating to `PropertyValueConversions`, typically using a `PropertyValueConverterFactory` providing the actual converter.
|
||||
Depending on the applications needs, multiple instances of `PropertyValueConverterFactory` can be chained or decorated, for example to apply caching.
|
||||
By default, a caching implementation is used that is capable of serving types with a default constructor or enum values.
|
||||
A set of predefined factories is available through `PropertyValueConverterFactory` factory methods.
|
||||
Use `PropertyValueConverterFactory.beanFactoryAware(…)` to obtain a `PropertyValueConverter` instances from an `ApplicationContext`.
|
||||
You can obtain `PropertyValueConverter` instances from `CustomConversions#getPropertyValueConverter(…)` by delegating to `PropertyValueConversions`, typically by using a `PropertyValueConverterFactory` to provide the actual converter.
|
||||
Depending on your application's needs, you can chain or decorate multiple instances of `PropertyValueConverterFactory` -- for example, to apply caching.
|
||||
By default, Spring Data MongoDB uses a caching implementation that can serve types with a default constructor or enum values.
|
||||
A set of predefined factories is available through the factory methods in `PropertyValueConverterFactory`.
|
||||
You can use `PropertyValueConverterFactory.beanFactoryAware(…)` to obtain a `PropertyValueConverter` instance from an `ApplicationContext`.
|
||||
|
||||
You can change the default behavior through `ConverterConfiguration`.
|
||||
|
||||
[[mongo.property-converters.declarative]]
|
||||
=== Declarative Value Converter
|
||||
|
||||
The most straight forward usage of a `PropertyValueConverter` is by annotating properties with the `@ValueConverter` annotation that defines the converter type.
|
||||
The most straight forward usage of a `PropertyValueConverter` is by annotating properties with the `@ValueConverter` annotation that defines the converter type:
|
||||
|
||||
.Declarative PropertyValueConverter
|
||||
====
|
||||
@@ -54,8 +54,8 @@ class Person {
|
||||
[[mongo.property-converters.programmatic]]
|
||||
=== Programmatic Value Converter Registration
|
||||
|
||||
Programmatic registration registers `PropertyValueConverter` instances for properties within an entity model using a `PropertyValueConverterRegistrar` as shown below.
|
||||
The difference to declarative registration is that programmatic registration happens entirely outside of the entity model.
|
||||
Programmatic registration registers `PropertyValueConverter` instances for properties within an entity model by using a `PropertyValueConverterRegistrar`, as the following example shows.
|
||||
The difference between declarative registration and programmatic registration is that programmatic registration happens entirely outside of the entity model.
|
||||
Such an approach is useful if you cannot or do not want to annotate the entity model.
|
||||
|
||||
.Programmatic PropertyValueConverter registration
|
||||
@@ -76,25 +76,22 @@ registrar.registerConverter(Person.class, Person::getSsn())
|
||||
<2> Type safe variant that allows to register a converter and its conversion functions.
|
||||
====
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Dot-notation (such as `registerConverter(Person.class, "address.street", …)`) nagivating across properties into subdocuments is *not* supported when registering converters.
|
||||
====
|
||||
WARNING: Dot notation (such as `registerConverter(Person.class, "address.street", …)`) for nagivating across properties into subdocuments is *not* supported when registering converters.
|
||||
|
||||
[[mongo.property-converters.value-conversions]]
|
||||
=== MongoDB property value conversions
|
||||
|
||||
The above sections outlined the purpose an overall structure of `PropertyValueConverters`.
|
||||
This section will focus on MongoDB specific aspects.
|
||||
The preceding sections outlined the purpose an overall structure of `PropertyValueConverters`.
|
||||
This section focuses on MongoDB specific aspects.
|
||||
|
||||
==== MongoValueConverter and MongoConversionContext
|
||||
|
||||
`MongoValueConverter` offers a pre typed `PropertyValueConverter` interface leveraging the `MongoConversionContext`.
|
||||
`MongoValueConverter` offers a pre-typed `PropertyValueConverter` interface that uses `MongoConversionContext`.
|
||||
|
||||
==== MongoCustomConversions configuration
|
||||
|
||||
`MongoCustomConversions` are by default capable of handling declarative value converters depending on the configured `PropertyValueConverterFactory`.
|
||||
`MongoConverterConfigurationAdapter` is there to help set up programmatic value conversions or define the `PropertyValueConverterFactory` to be used.
|
||||
By default, `MongoCustomConversions` can handle declarative value converters, depending on the configured `PropertyValueConverterFactory`.
|
||||
`MongoConverterConfigurationAdapter` helps to set up programmatic value conversions or define the `PropertyValueConverterFactory` to be used.
|
||||
|
||||
.Configuration Sample
|
||||
====
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
= MongoDB Repositories
|
||||
|
||||
[[mongo-repo-intro]]
|
||||
== Introduction
|
||||
|
||||
This chapter points out the specialties for repository support for MongoDB. This chapter builds on the core repository support explained in <<repositories>>. You should have a sound understanding of the basic concepts explained there.
|
||||
This chapter points out the specialties for repository support for MongoDB.
|
||||
This chapter builds on the core repository support explained in <<repositories>>.
|
||||
You should have a sound understanding of the basic concepts explained there.
|
||||
|
||||
[[mongo-repo-usage]]
|
||||
== Usage
|
||||
|
||||
To access domain entities stored in a MongoDB, you can use our sophisticated repository support that eases implementation quite significantly.To do so, create an interface for your repository, as the following example shows:
|
||||
To access domain entities stored in a MongoDB, you can use our sophisticated repository support that eases implementation quite significantly.
|
||||
To do so, create an interface for your repository, as the following example shows:
|
||||
|
||||
.Sample Person entity
|
||||
====
|
||||
@@ -28,7 +29,8 @@ public class Person {
|
||||
----
|
||||
====
|
||||
|
||||
Note that the domain type shown in the preceding example has a property named `id` of type `String`.The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID. Currently, we support `String`, `ObjectId`, and `BigInteger` as ID types.
|
||||
Note that the domain type shown in the preceding example has a property named `id` of type `String`.The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID.
|
||||
Currently, we support `String`, `ObjectId`, and `BigInteger` as ID types.
|
||||
Please see <<mongo-template.id-handling, ID mapping>> for more information about on how the `id` field is handled in the mapping layer.
|
||||
|
||||
Now that we have a domain object, we can define an interface that uses it, as follows:
|
||||
@@ -44,12 +46,12 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
Right now this interface serves only to provide type information, but we can add additional methods to it later.
|
||||
|
||||
To start using the repository, use the `@EnableMongoRepositories` annotation.
|
||||
That annotation carries the same attributes as the namespace element.If no base package is configured, the infrastructure scans the package of the annotated configuration class.The following example shows how to use Java configuration for a repository:
|
||||
That annotation carries the same attributes as the namespace element.
|
||||
If no base package is configured, the infrastructure scans the package of the annotated configuration class.
|
||||
The following example shows how to use Java configuration for a repository:
|
||||
|
||||
.Java configuration for repositories
|
||||
====
|
||||
@@ -100,11 +102,12 @@ If you would rather go with XML based configuration add the following content:
|
||||
----
|
||||
====
|
||||
|
||||
This namespace element causes the base packages to be scanned for interfaces that extend `MongoRepository` and create Spring beans for each one found.By default, the repositories get a `MongoTemplate` Spring bean wired that is called `mongoTemplate`, so you only need to configure `mongo-template-ref` explicitly if you deviate from this convention.
|
||||
This namespace element causes the base packages to be scanned for interfaces that extend `MongoRepository` and create Spring beans for each one found.
|
||||
By default, the repositories get a `MongoTemplate` Spring bean wired that is called `mongoTemplate`, so you only need to configure `mongo-template-ref` explicitly if you deviate from this convention.
|
||||
|
||||
|
||||
|
||||
Because our domain repository extends `PagingAndSortingRepository`, it provides you with CRUD operations as well as methods for paginated and sorted access to the entities.Working with the repository instance is just a matter of dependency injecting it into a client.Consequently, accessing the second page of `Person` objects at a page size of 10 would resemble the following code:
|
||||
Because our domain repository extends `PagingAndSortingRepository`, it provides you with CRUD operations as well as methods for paginated and sorted access to the entities.
|
||||
Working with the repository instance is just a matter of dependency injecting it into a client .
|
||||
Consequently, accessing the second page of `Person` objects at a page size of 10 would resemble the following code:
|
||||
|
||||
.Paging access to Person entities
|
||||
====
|
||||
@@ -126,12 +129,15 @@ public class PersonRepositoryTests {
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into test cases.Inside the test method, we use the repository to query the datastore.We hand the repository a `PageRequest` instance that requests the first page of `Person` objects at a page size of 10.
|
||||
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into test cases.
|
||||
Inside the test method, we use the repository to query the datastore.
|
||||
We hand the repository a `PageRequest` instance that requests the first page of `Person` objects at a page size of 10.
|
||||
|
||||
[[mongodb.repositories.queries]]
|
||||
== Query Methods
|
||||
|
||||
Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
|
||||
Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.
|
||||
Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
|
||||
|
||||
.PersonRepository with query methods
|
||||
====
|
||||
@@ -150,10 +156,16 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str
|
||||
Stream<Person> findAllBy(); <5>
|
||||
}
|
||||
----
|
||||
<1> The `findByLastname` method shows a query for all people with the given last name. The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`. Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> Applies pagination to a query. You can equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and Spring Data automatically pages the query accordingly.
|
||||
<3> Shows that you can query based on properties that are not primitive types. Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
|
||||
<4> Uses the `First` keyword to restrict the query to only the first result. Unlike <3>, this method does not throw an exception if more than one match is found.
|
||||
|
||||
<1> The `findByLastname` method shows a query for all people with the given last name.
|
||||
The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`.
|
||||
Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> Applies pagination to a query.
|
||||
You can equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and Spring Data automatically pages the query accordingly.
|
||||
<3> Shows that you can query based on properties that are not primitive types.
|
||||
Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
|
||||
<4> Uses the `First` keyword to restrict the query to only the first result.
|
||||
Unlike <3>, this method does not throw an exception if more than one match is found.
|
||||
<5> Uses a Java 8 `Stream` that reads and converts individual elements while iterating the stream.
|
||||
====
|
||||
|
||||
@@ -161,7 +173,7 @@ NOTE: We do not support referring to parameters that are mapped as `DBRef` in th
|
||||
|
||||
The following table shows the keywords that are supported for query methods:
|
||||
|
||||
[cols="1,2,3", options="header"]
|
||||
[cols="1,2,3",options="header"]
|
||||
.Supported keywords for query methods
|
||||
|===
|
||||
| Keyword
|
||||
@@ -292,13 +304,13 @@ NOTE: If the property criterion compares a document, the order of the fields and
|
||||
[[mongodb.repositories.queries.update]]
|
||||
=== Repository Update Methods
|
||||
|
||||
The keywords in the preceding table can also be used to create queries that identify matching documents for running updates on them.
|
||||
The actual update action is defined via the `@Update` annotation on the method itself as shown in the snippet below. +
|
||||
Please note that the naming schema for derived queries starts with `find`.
|
||||
Using _update_ (as in `updateAllByLastname(...)`) is only allowed in combination with `@Query`.
|
||||
You can also use the keywords in the preceding table to create queries that identify matching documents for running updates on them.
|
||||
The actual update action is defined by the `@Update` annotation on the method itself, as the following listing shows.
|
||||
Note that the naming schema for derived queries starts with `find`.
|
||||
Using `update` (as in `updateAllByLastname(...)`) is allowed only in combination with `@Query`.
|
||||
|
||||
The update is applied to *all* matching documents and it is *not* possible to limit the scope by passing in a `Page` nor using any of the <<repositories.limit-query-result,limiting keywords>>. +
|
||||
The return type can be either `void` or a _numeric_ type, such as `long` which holds the number of modified documents.
|
||||
The update is applied to *all* matching documents and it is *not* possible to limit the scope by passing in a `Page` or by using any of the <<repositories.limit-query-result,limiting keywords>>.
|
||||
The return type can be either `void` or a _numeric_ type, such as `long`, to hold the number of modified documents.
|
||||
|
||||
.Update Methods
|
||||
====
|
||||
@@ -326,18 +338,17 @@ public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
void updateAllByLastname(String lastname, int increment); <6>
|
||||
}
|
||||
----
|
||||
<1> The filter query for the update is derived from the method name. The update is as is and does not bind any parameters.
|
||||
<2> The actual increment value is defined by the _increment_ method argument that is bound to the `?1` placeholder.
|
||||
<3> It is possible to use SpEL for parameter binding.
|
||||
|
||||
<1> The filter query for the update is derived from the method name.
|
||||
The update is "`as is`" and does not bind any parameters.
|
||||
<2> The actual increment value is defined by the `increment` method argument that is bound to the `?1` placeholder.
|
||||
<3> Use the Spring Expression Language (SpEL) for parameter binding.
|
||||
<4> Use the `pipeline` attribute to issue <<mongo-template.aggregation-update,aggregation pipeline updates>>.
|
||||
<5> The update may contain complex objects.
|
||||
<6> Combine a <<mongodb.repositories.queries.json-based,string based query>> with an update.
|
||||
====
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Repository updates do not emit persistence nor mapping lifecycle events.
|
||||
====
|
||||
WARNING: Repository updates do not emit persistence nor mapping lifecycle events.
|
||||
|
||||
[[mongodb.repositories.queries.delete]]
|
||||
=== Repository Delete Queries
|
||||
@@ -360,6 +371,7 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
Optional<Person> deleteByBirthdate(Date birthdate); <4>
|
||||
}
|
||||
----
|
||||
|
||||
<1> Using a return type of `List` retrieves and returns all matching documents before actually deleting them.
|
||||
<2> A numeric return type directly removes the matching documents, returning the total number of documents removed.
|
||||
<3> A single domain type result retrieves and removes the first matching document.
|
||||
@@ -369,7 +381,8 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
[[mongodb.repositories.queries.geo-spatial]]
|
||||
=== Geo-spatial Repository Queries
|
||||
|
||||
As you saw in the preceding table of keywords, a few keywords trigger geo-spatial operations within a MongoDB query. The `Near` keyword allows some further modification, as the next few examples show.
|
||||
As you saw in the preceding table of keywords, a few keywords trigger geo-spatial operations within a MongoDB query.
|
||||
The `Near` keyword allows some further modification, as the next few examples show.
|
||||
|
||||
The following example shows how to define a `near` query that finds all persons with a given distance of a given point:
|
||||
|
||||
@@ -385,7 +398,8 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
----
|
||||
====
|
||||
|
||||
Adding a `Distance` parameter to the query method allows restricting results to those within the given distance. If the `Distance` was set up containing a `Metric`, we transparently use `$nearSphere` instead of `$code`, as the following example shows:
|
||||
Adding a `Distance` parameter to the query method allows restricting results to those within the given distance.
|
||||
If the `Distance` was set up containing a `Metric`, we transparently use `$nearSphere` instead of `$code`, as the following example shows:
|
||||
|
||||
.Using `Distance` with `Metrics`
|
||||
====
|
||||
@@ -398,9 +412,12 @@ Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
----
|
||||
====
|
||||
|
||||
Using a `Distance` with a `Metric` causes a `$nearSphere` (instead of a plain `$near`) clause to be added. Beyond that, the actual distance gets calculated according to the `Metrics` used.
|
||||
Using a `Distance` with a `Metric` causes a `$nearSphere` (instead of a plain `$near`) clause to be added.
|
||||
Beyond that, the actual distance gets calculated according to the `Metrics` used.
|
||||
|
||||
(Note that `Metric` does not refer to metric units of measure. It could be miles rather than kilometers. Rather, `metric` refers to the concept of a system of measurement, regardless of which system you use.)
|
||||
(Note that `Metric` does not refer to metric units of measure.
|
||||
It could be miles rather than kilometers.
|
||||
Rather, `metric` refers to the concept of a system of measurement, regardless of which system you use.)
|
||||
|
||||
NOTE: Using `@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)` on the target property forces usage of the `$nearSphere` operator.
|
||||
|
||||
@@ -461,12 +478,14 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
}
|
||||
----
|
||||
|
||||
The query in the preceding example returns only the `firstname`, `lastname` and `Id` properties of the `Person` objects. The `age` property, a `java.lang.Integer`, is not set and its value is therefore null.
|
||||
The query in the preceding example returns only the `firstname`, `lastname` and `Id` properties of the `Person` objects.
|
||||
The `age` property, a `java.lang.Integer`, is not set and its value is therefore null.
|
||||
|
||||
[[mongodb.repositories.queries.sort]]
|
||||
=== Sorting Query Method results
|
||||
|
||||
MongoDB repositories allow various approaches to define sorting order. Let's take a look at the following example:
|
||||
MongoDB repositories allow various approaches to define sorting order.
|
||||
Let's take a look at the following example:
|
||||
|
||||
.Sorting Query Results
|
||||
====
|
||||
@@ -485,11 +504,16 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
List<Person> findByLastname(String lastname, Sort sort); <4>
|
||||
}
|
||||
----
|
||||
|
||||
<1> Static sorting derived from method name. `SortByAgeDesc` results in `{ age : -1 }` for the sort parameter.
|
||||
<2> Dynamic sorting using a method argument. `Sort.by(DESC, "age")` creates `{ age : -1 }` for the sort parameter.
|
||||
<3> Static sorting via `Query` annotation. Sort parameter applied as stated in the `sort` attribute.
|
||||
<2> Dynamic sorting using a method argument.
|
||||
`Sort.by(DESC, "age")` creates `{ age : -1 }` for the sort parameter.
|
||||
<3> Static sorting via `Query` annotation.
|
||||
Sort parameter applied as stated in the `sort` attribute.
|
||||
<4> Default sorting via `Query` annotation combined with dynamic one via a method argument. `Sort.unsorted()`
|
||||
results in `{ age : -1 }`. Using `Sort.by(ASC, "age")` overrides the defaults and creates `{ age : 1 }`. `Sort.by
|
||||
results in `{ age : -1 }`.
|
||||
Using `Sort.by(ASC, "age")` overrides the defaults and creates `{ age : 1 }`.
|
||||
`Sort.by
|
||||
(ASC, "firstname")` alters the default and results in `{ age : -1, firstname : 1 }`.
|
||||
====
|
||||
|
||||
@@ -499,7 +523,8 @@ results in `{ age : -1 }`. Using `Sort.by(ASC, "age")` overrides the defaults an
|
||||
Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime.
|
||||
SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments.
|
||||
|
||||
Expressions expose method arguments through an array that contains all the arguments.The following query uses `[0]`
|
||||
Expressions expose method arguments through an array that contains all the arguments.
|
||||
The following query uses `[0]`
|
||||
to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding):
|
||||
|
||||
[source,java]
|
||||
@@ -511,8 +536,8 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
}
|
||||
----
|
||||
|
||||
Expressions can be used to invoke functions, evaluate conditionals, and construct values.SpEL expressions
|
||||
used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows:
|
||||
Expressions can be used to invoke functions, evaluate conditionals, and construct values.
|
||||
SpEL expressions used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -523,12 +548,14 @@ public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
}
|
||||
----
|
||||
|
||||
SpEL in query strings can be a powerful way to enhance queries.However, they can also accept a broad range of unwanted arguments.
|
||||
You should make sure to sanitize strings before passing them to the query to avoid unwanted changes to your query.
|
||||
WARNING: SpEL in query strings can be a powerful way to enhance queries.
|
||||
However, they can also accept a broad range of unwanted arguments.
|
||||
Make sure to sanitize strings before passing them to the query to avoid creation of vulnerabilities or unwanted changes to your query.
|
||||
|
||||
Expression support is extensible through the Query SPI: `org.springframework.data.repository.query.spi.EvaluationContextExtension`.
|
||||
The Query SPI can contribute properties and functions and can customize the root object.Extensions are retrieved from the application context
|
||||
at the time of SpEL evaluation when the query is built.The following example shows how to use `EvaluationContextExtension`:
|
||||
The Query SPI can contribute properties and functions and can customize the root object.
|
||||
Extensions are retrieved from the application context at the time of SpEL evaluation when the query is built.
|
||||
The following example shows how to use `EvaluationContextExtension`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -575,7 +602,9 @@ Page<Person> page = repository.findAll(person.lastname.contains("a"),
|
||||
PageRequest.of(0, 2, Direction.ASC, "lastname"));
|
||||
----
|
||||
|
||||
`QPerson` is a class that is generated by the Java annotation post-processing tool.It is a `Predicate` that lets you write type-safe queries.Notice that there are no strings in the query other than the `C0123` value.
|
||||
`QPerson` is a class that is generated by the Java annotation post-processing tool.
|
||||
It is a `Predicate` that lets you write type-safe queries.
|
||||
Notice that there are no strings in the query other than the `C0123` value.
|
||||
|
||||
You can use the generated `Predicate` class by using the `QuerydslPredicateExecutor` interface, which the following listing shows:
|
||||
|
||||
@@ -608,11 +637,16 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
[[mongodb.repositories.queries.full-text]]
|
||||
=== Full-text Search Queries
|
||||
|
||||
MongoDB's full-text search feature is store-specific and, therefore, can be found on `MongoRepository` rather than on the more general `CrudRepository`. We need a document with a full-text index (see "`<<mapping-usage-indexes.text-index>>`" to learn how to create a full-text index).
|
||||
MongoDB's full-text search feature is store-specific and, therefore, can be found on `MongoRepository` rather than on the more general `CrudRepository`.
|
||||
We need a document with a full-text index (see "`<<mapping-usage-indexes.text-index>>`" to learn how to create a full-text index).
|
||||
|
||||
Additional methods on `MongoRepository` take `TextCriteria` as an input parameter. In addition to those explicit methods, it is also possible to add a `TextCriteria`-derived repository method. The criteria are added as an additional `AND` criteria. Once the entity contains a `@TextScore`-annotated property, the document's full-text score can be retrieved. Furthermore, the `@TextScore` annotated also makes it possible to sort by the document's score, as the following example shows:
|
||||
Additional methods on `MongoRepository` take `TextCriteria` as an input parameter.
|
||||
In addition to those explicit methods, it is also possible to add a `TextCriteria`-derived repository method.
|
||||
The criteria are added as an additional `AND` criteria.
|
||||
Once the entity contains a `@TextScore`-annotated property, the document's full-text score can be retrieved.
|
||||
Furthermore, the `@TextScore` annotated also makes it possible to sort by the document's score, as the following example shows:
|
||||
|
||||
[source, java]
|
||||
[source,java]
|
||||
----
|
||||
@Document
|
||||
class FullTextDocument {
|
||||
@@ -652,7 +686,11 @@ include::./mongo-repositories-aggregation.adoc[]
|
||||
[[mongodb.repositories.misc.cdi-integration]]
|
||||
== CDI Integration
|
||||
|
||||
Instances of the repository interfaces are usually created by a container, and Spring is the most natural choice when working with Spring Data. As of version 1.3.0, Spring Data MongoDB ships with a custom CDI extension that lets you use the repository abstraction in CDI environments. The extension is part of the JAR. To activate it, drop the Spring Data MongoDB JAR into your classpath. You can now set up the infrastructure by implementing a CDI Producer for the `MongoTemplate`, as the following example shows:
|
||||
Instances of the repository interfaces are usually created by a container, and Spring is the most natural choice when working with Spring Data.
|
||||
As of version 1.3.0, Spring Data MongoDB ships with a custom CDI extension that lets you use the repository abstraction in CDI environments.
|
||||
The extension is part of the JAR.
|
||||
To activate it, drop the Spring Data MongoDB JAR into your classpath.
|
||||
You can now set up the infrastructure by implementing a CDI Producer for the `MongoTemplate`, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -668,7 +706,8 @@ class MongoTemplateProducer {
|
||||
}
|
||||
----
|
||||
|
||||
The Spring Data MongoDB CDI extension picks up the `MongoTemplate` available as a CDI bean and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus, obtaining an instance of a Spring Data repository is a matter of declaring an `@Inject`-ed property, as the following example shows:
|
||||
The Spring Data MongoDB CDI extension picks up the `MongoTemplate` available as a CDI bean and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container.
|
||||
Thus, obtaining an instance of a Spring Data repository is a matter of declaring an `@Inject`-ed property, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 3.4 RC1 (2021.2.0)
|
||||
Spring Data MongoDB 3.4.1 (2021.2.1)
|
||||
Copyright (c) [2010-2019] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -34,6 +34,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user