Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4db26ffd1 | ||
|
|
b14a3166e3 | ||
|
|
bd7af4c55b | ||
|
|
491304f2c7 | ||
|
|
51d5a3b61a | ||
|
|
2c193ec325 | ||
|
|
427d4f5bd5 | ||
|
|
8f8d792b61 | ||
|
|
f16809a363 | ||
|
|
58b33e949b | ||
|
|
47481c4597 | ||
|
|
1475cde337 | ||
|
|
4734a2925c | ||
|
|
a6a0bde6f2 | ||
|
|
30a8608135 | ||
|
|
c77facda90 | ||
|
|
b4c213b8c2 | ||
|
|
2230b51a79 | ||
|
|
7258cb8d1d | ||
|
|
e6bae5d124 | ||
|
|
41bb619dc7 | ||
|
|
6c42c4c828 | ||
|
|
58050405a3 | ||
|
|
8834c5e97d | ||
|
|
7526f3bd2e | ||
|
|
3d623d8181 | ||
|
|
ada8c4ec8d | ||
|
|
dd8fc1a591 | ||
|
|
e1f19f69bd | ||
|
|
566e69a825 | ||
|
|
6342ef1806 | ||
|
|
9d4d47f503 | ||
|
|
f22036851e | ||
|
|
d5006bb693 | ||
|
|
82fdbe8cc2 | ||
|
|
747625b5c3 | ||
|
|
3dee8de66d | ||
|
|
e3b98693d4 | ||
|
|
80ff3760ef | ||
|
|
d19ea88670 | ||
|
|
d3b9f91478 | ||
|
|
7fb5c7d97c | ||
|
|
18487ef252 | ||
|
|
1aa2ee5f54 | ||
|
|
b9282c8d32 | ||
|
|
3ac379a4b8 | ||
|
|
0470dd6268 | ||
|
|
270d373083 | ||
|
|
697f5ad7c6 | ||
|
|
028aeb327f | ||
|
|
6568fa2d2e | ||
|
|
34986df70b | ||
|
|
c3383432f7 | ||
|
|
e9498c86ca | ||
|
|
09f8dc6843 | ||
|
|
9f22195330 | ||
|
|
dd944b0881 | ||
|
|
cf0f891c8b | ||
|
|
fbf84fed0e | ||
|
|
251a953957 | ||
|
|
cdc78592ee | ||
|
|
ab97e58793 | ||
|
|
a4eeb9f305 | ||
|
|
de6c649c83 | ||
|
|
e90c6b0790 | ||
|
|
d2e68cd925 | ||
|
|
7ed48f5e76 | ||
|
|
2359357977 | ||
|
|
a90f238574 | ||
|
|
1c9188f7e1 | ||
|
|
a2f7c3f482 | ||
|
|
3440bf6c4d | ||
|
|
deed19187f | ||
|
|
c5f2abe037 | ||
|
|
6cce16414e | ||
|
|
a85855a307 | ||
|
|
31390d41e0 | ||
|
|
117ab7c033 | ||
|
|
73fbaaf3bd | ||
|
|
17937b0475 | ||
|
|
46943716ee | ||
|
|
25af5b5f79 | ||
|
|
a5a4c6d8c4 | ||
|
|
5885d084be | ||
|
|
d8fdc18265 | ||
|
|
840bde65e8 | ||
|
|
f2ee7d90c4 | ||
|
|
898489fecf | ||
|
|
3575d5461e | ||
|
|
4fa09d80db | ||
|
|
bb84b92d1d | ||
|
|
af85b46e7d | ||
|
|
96fbe49cdb | ||
|
|
6b36c792b9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ src/ant/.ant-targets-upload-dist.xml
|
||||
atlassian-ide-plugin.xml
|
||||
/.gradle/
|
||||
/.idea/
|
||||
*.graphml
|
||||
@@ -16,7 +16,9 @@ env:
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- mongodb-3.4-precise
|
||||
- mongodb-upstart
|
||||
- sourceline: 'deb [arch=amd64] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse'
|
||||
key_url: 'https://www.mongodb.org/static/pgp/server-3.4.asc'
|
||||
packages:
|
||||
- mongodb-org-server
|
||||
- mongodb-org-shell
|
||||
|
||||
2
lombok.config
Normal file
2
lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
lombok.nonNull.exceptionType = IllegalArgumentException
|
||||
lombok.log.fieldName = LOG
|
||||
21
pom.xml
21
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,22 +15,22 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>spring-data-mongodb</module>
|
||||
<module>spring-data-mongodb-cross-store</module>
|
||||
<module>spring-data-mongodb-log4j</module>
|
||||
<module>spring-data-mongodb-distribution</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>2.0.0.M3</springdata.commons>
|
||||
<springdata.commons>2.0.0.RC1</springdata.commons>
|
||||
<mongo>3.4.2</mongo>
|
||||
<mongo.reactivestreams>1.3.0</mongo.reactivestreams>
|
||||
<mongo.reactivestreams>1.5.0</mongo.reactivestreams>
|
||||
<jmh.version>1.19</jmh.version>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
@@ -160,6 +160,17 @@
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>benchmarks</id>
|
||||
<modules>
|
||||
<module>spring-data-mongodb</module>
|
||||
<module>spring-data-mongodb-cross-store</module>
|
||||
<module>spring-data-mongodb-log4j</module>
|
||||
<module>spring-data-mongodb-distribution</module>
|
||||
<module>spring-data-mongodb-benchmarks</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
|
||||
76
spring-data-mongodb-benchmarks/README.md
Normal file
76
spring-data-mongodb-benchmarks/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Benchmarks
|
||||
|
||||
Benchmarks are based on [JMH](http://openjdk.java.net/projects/code-tools/jmh/).
|
||||
|
||||
# Running Benchmarks
|
||||
|
||||
Running benchmarks is disabled by default and can be activated via the `benchmarks` profile.
|
||||
To run the benchmarks with default settings use.
|
||||
|
||||
```bash
|
||||
mvn -P benchmarks clean test
|
||||
```
|
||||
|
||||
A basic report will be printed to the CLI.
|
||||
|
||||
```bash
|
||||
# Run complete. Total time: 00:00:15
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
MappingMongoConverterBenchmark.readObject thrpt 10 1920157,631 ± 64310,809 ops/s
|
||||
MappingMongoConverterBenchmark.writeObject thrpt 10 782732,857 ± 53804,130 ops/s
|
||||
```
|
||||
|
||||
## Running all Benchmarks of a specific class
|
||||
|
||||
To run all Benchmarks of a specific class, just provide its simple class name via the `benchmark` command line argument.
|
||||
|
||||
```bash
|
||||
mvn -P benchmarks clean test -D benchmark=MappingMongoConverterBenchmark
|
||||
```
|
||||
|
||||
## Running a single Benchmark
|
||||
|
||||
To run a single Benchmark provide its containing class simple name followed by `#` and the method name via the `benchmark` command line argument.
|
||||
|
||||
```bash
|
||||
mvn -P benchmarks clean test -D benchmark=MappingMongoConverterBenchmark#readObjectWith2Properties
|
||||
```
|
||||
|
||||
# Saving Benchmark Results
|
||||
|
||||
A detailed benchmark report is stored in JSON format in the `/target/reports/performance` directory.
|
||||
To store the report in a different location use the `benchmarkReportDir` command line argument.
|
||||
|
||||
## MongoDB
|
||||
|
||||
Results can be directly piped to MongoDB by providing a valid [Connection String](https://docs.mongodb.com/manual/reference/connection-string/) via the `publishTo` command line argument.
|
||||
|
||||
```bash
|
||||
mvn -P benchmarks clean test -D publishTo=mongodb://127.0.0.1:27017
|
||||
```
|
||||
|
||||
NOTE: If the uri does not explicitly define a database the default `spring-data-mongodb-benchmarks` is used.
|
||||
|
||||
## HTTP Endpoint
|
||||
|
||||
The benchmark report can also be posted as `application/json` to an HTTP Endpoint by providing a valid URl via the `publishTo` command line argument.
|
||||
|
||||
```bash
|
||||
mvn -P benchmarks clean test -D publishTo=http://127.0.0.1:8080/capture-benchmarks
|
||||
```
|
||||
|
||||
# Customizing Benchmarks
|
||||
|
||||
Following options can be set via command line.
|
||||
|
||||
Option | Default Value
|
||||
--- | ---
|
||||
warmupIterations | 10
|
||||
warmupTime | 1 (seconds)
|
||||
measurementIterations | 10
|
||||
measurementTime | 1 (seconds)
|
||||
forks | 1
|
||||
benchmarkReportDir | /target/reports/performance (always relative to project root dir)
|
||||
benchmark | .* (single benchmark via `classname#benchmark`)
|
||||
publishTo | \[not set\] (mongodb-uri or http-endpoint)
|
||||
111
spring-data-mongodb-benchmarks/pom.xml
Normal file
111
spring-data-mongodb-benchmarks/pom.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-mongodb-benchmarks</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Spring Data MongoDB - Microbenchmarks</name>
|
||||
|
||||
<properties>
|
||||
<!-- Skip tests by default; run only if -DskipTests=false is specified or benchmarks profile is activated -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
||||
<profile>
|
||||
<id>benchmarks</id>
|
||||
<properties>
|
||||
<skipTests>false</skipTests>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<phase>never</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<testSourceDirectory>${project.build.sourceDirectory}</testSourceDirectory>
|
||||
<testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
|
||||
<excludes>
|
||||
<exclude>**/AbstractMicrobenchmark.java</exclude>
|
||||
<exclude>**/*$*.class</exclude>
|
||||
<exclude>**/generated/*.class</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*Benchmark*</include>
|
||||
</includes>
|
||||
<systemPropertyVariables>
|
||||
<benchmarkReportDir>${project.build.directory}/reports/performance</benchmarkReportDir>
|
||||
<project.version>${project.version}</project.version>
|
||||
<git.dirty>${git.dirty}</git.dirty>
|
||||
<git.commit.id>${git.commit.id}</git.commit.id>
|
||||
<git.branch>${git.branch}</git.branch>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
|
||||
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.microbenchmark.AbstractMicrobenchmark;
|
||||
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class ProjectionsBenchmark extends AbstractMicrobenchmark {
|
||||
|
||||
private static final String DB_NAME = "projections-benchmark";
|
||||
private static final String COLLECTION_NAME = "projections";
|
||||
|
||||
private MongoTemplate template;
|
||||
private MongoClient client;
|
||||
private MongoCollection<Document> mongoCollection;
|
||||
|
||||
private Person source;
|
||||
|
||||
private FindWithQuery<Person> asPerson;
|
||||
private FindWithQuery<DtoProjection> asDtoProjection;
|
||||
private FindWithQuery<ClosedProjection> asClosedProjection;
|
||||
private FindWithQuery<OpenProjection> asOpenProjection;
|
||||
|
||||
private TerminatingFind<Person> asPersonWithFieldsRestriction;
|
||||
private Document fields = new Document("firstname", 1);
|
||||
|
||||
@Setup
|
||||
public void setUp() {
|
||||
|
||||
client = new MongoClient(new ServerAddress());
|
||||
template = new MongoTemplate(client, DB_NAME);
|
||||
|
||||
source = new Person();
|
||||
source.firstname = "luke";
|
||||
source.lastname = "skywalker";
|
||||
|
||||
source.address = new Address();
|
||||
source.address.street = "melenium falcon 1";
|
||||
source.address.city = "deathstar";
|
||||
|
||||
template.save(source, COLLECTION_NAME);
|
||||
|
||||
asPerson = template.query(Person.class).inCollection(COLLECTION_NAME);
|
||||
asDtoProjection = template.query(Person.class).inCollection(COLLECTION_NAME).as(DtoProjection.class);
|
||||
asClosedProjection = template.query(Person.class).inCollection(COLLECTION_NAME).as(ClosedProjection.class);
|
||||
asOpenProjection = template.query(Person.class).inCollection(COLLECTION_NAME).as(OpenProjection.class);
|
||||
|
||||
asPersonWithFieldsRestriction = template.query(Person.class).inCollection(COLLECTION_NAME)
|
||||
.matching(new BasicQuery(new Document(), fields));
|
||||
|
||||
mongoCollection = client.getDatabase(DB_NAME).getCollection(COLLECTION_NAME);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
|
||||
client.dropDatabase(DB_NAME);
|
||||
client.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the baseline for comparison by using the plain MongoDB java driver api without any additional fluff.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object baseline() {
|
||||
return mongoCollection.find().first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read into the domain type including all fields.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object readIntoDomainType() {
|
||||
return asPerson.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read into the domain type but restrict query to only return one field.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object readIntoDomainTypeRestrictingToOneField() {
|
||||
return asPersonWithFieldsRestriction.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read into dto projection that only needs to map one field back.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object readIntoDtoProjectionWithOneField() {
|
||||
return asDtoProjection.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read into closed interface projection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object readIntoClosedProjectionWithOneField() {
|
||||
return asClosedProjection.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read into an open projection backed by the mapped domain object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Benchmark // DATAMONGO-1733
|
||||
public Object readIntoOpenProjection() {
|
||||
return asOpenProjection.all();
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
String firstname;
|
||||
String lastname;
|
||||
Address address;
|
||||
}
|
||||
|
||||
static class Address {
|
||||
|
||||
String city;
|
||||
String street;
|
||||
}
|
||||
|
||||
static class DtoProjection {
|
||||
|
||||
@Field("firstname") String name;
|
||||
}
|
||||
|
||||
static interface ClosedProjection {
|
||||
|
||||
String getFirstname();
|
||||
}
|
||||
|
||||
static interface OpenProjection {
|
||||
|
||||
@Value("#{target.firstname}")
|
||||
String name();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.microbenchmark.AbstractMicrobenchmark;
|
||||
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
public class DbRefMappingBenchmark extends AbstractMicrobenchmark {
|
||||
|
||||
private static final String DB_NAME = "dbref-loading-benchmark";
|
||||
|
||||
private MongoClient client;
|
||||
private MongoTemplate template;
|
||||
|
||||
private Query queryObjectWithDBRef;
|
||||
private Query queryObjectWithDBRefList;
|
||||
|
||||
@Setup
|
||||
public void setUp() throws Exception {
|
||||
|
||||
client = new MongoClient(new ServerAddress());
|
||||
template = new MongoTemplate(client, DB_NAME);
|
||||
|
||||
List<RefObject> refObjects = new ArrayList<>();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
RefObject o = new RefObject();
|
||||
template.save(o);
|
||||
refObjects.add(o);
|
||||
}
|
||||
|
||||
ObjectWithDBRef singleDBRef = new ObjectWithDBRef();
|
||||
singleDBRef.ref = refObjects.iterator().next();
|
||||
template.save(singleDBRef);
|
||||
|
||||
ObjectWithDBRef multipleDBRefs = new ObjectWithDBRef();
|
||||
multipleDBRefs.refList = refObjects;
|
||||
template.save(multipleDBRefs);
|
||||
|
||||
queryObjectWithDBRef = query(where("id").is(singleDBRef.id));
|
||||
queryObjectWithDBRefList = query(where("id").is(multipleDBRefs.id));
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
|
||||
client.dropDatabase(DB_NAME);
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public ObjectWithDBRef readSingleDbRef() {
|
||||
return template.findOne(queryObjectWithDBRef, ObjectWithDBRef.class);
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public ObjectWithDBRef readMultipleDbRefs() {
|
||||
return template.findOne(queryObjectWithDBRefList, ObjectWithDBRef.class);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class ObjectWithDBRef {
|
||||
|
||||
private @Id ObjectId id;
|
||||
private @DBRef RefObject ref;
|
||||
private @DBRef List<RefObject> refList;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class RefObject {
|
||||
|
||||
private @Id String id;
|
||||
private String someValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.microbenchmark.AbstractMicrobenchmark;
|
||||
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
public class MappingMongoConverterBenchmark extends AbstractMicrobenchmark {
|
||||
|
||||
private static final String DB_NAME = "mapping-mongo-converter-benchmark";
|
||||
|
||||
private MongoClient client;
|
||||
private MongoMappingContext mappingContext;
|
||||
private MappingMongoConverter converter;
|
||||
private Document documentWith2Properties, documentWith2PropertiesAnd1Nested;
|
||||
private Customer objectWith2PropertiesAnd1Nested;
|
||||
|
||||
private Document documentWithFlatAndComplexPropertiesPlusListAndMap;
|
||||
private SlightlyMoreComplexObject objectWithFlatAndComplexPropertiesPlusListAndMap;
|
||||
|
||||
@Setup
|
||||
public void setUp() throws Exception {
|
||||
|
||||
client = new MongoClient(new ServerAddress());
|
||||
|
||||
this.mappingContext = new MongoMappingContext();
|
||||
this.mappingContext.setInitialEntitySet(Collections.singleton(Customer.class));
|
||||
this.mappingContext.afterPropertiesSet();
|
||||
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(new SimpleMongoDbFactory(client, DB_NAME));
|
||||
|
||||
this.converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
this.converter.setCustomConversions(new MongoCustomConversions(Collections.emptyList()));
|
||||
this.converter.afterPropertiesSet();
|
||||
|
||||
// just a flat document
|
||||
this.documentWith2Properties = new Document("firstname", "Dave").append("lastname", "Matthews");
|
||||
|
||||
// document with a nested one
|
||||
Document address = new Document("zipCode", "ABCDE").append("city", "Some Place");
|
||||
this.documentWith2PropertiesAnd1Nested = new Document("firstname", "Dave").//
|
||||
append("lastname", "Matthews").//
|
||||
append("address", address);
|
||||
|
||||
// object equivalent of documentWith2PropertiesAnd1Nested
|
||||
this.objectWith2PropertiesAnd1Nested = new Customer("Dave", "Matthews", new Address("zipCode", "City"));
|
||||
|
||||
// a bit more challenging object with list & map conversion.
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap = new SlightlyMoreComplexObject();
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.id = UUID.randomUUID().toString();
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.addressList = Arrays.asList(new Address("zip-1", "city-1"),
|
||||
new Address("zip-2", "city-2"));
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.customer = objectWith2PropertiesAnd1Nested;
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap = new LinkedHashMap<>();
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("dave", objectWith2PropertiesAnd1Nested);
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("deborah",
|
||||
new Customer("Deborah Anne", "Dyer", new Address("?", "london")));
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.customerMap.put("eddie",
|
||||
new Customer("Eddie", "Vedder", new Address("??", "Seattle")));
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.intOne = Integer.MIN_VALUE;
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.intTwo = Integer.MAX_VALUE;
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.location = new Point(-33.865143, 151.209900);
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.renamedField = "supercalifragilisticexpialidocious";
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.stringOne = "¯\\_(ツ)_/¯";
|
||||
objectWithFlatAndComplexPropertiesPlusListAndMap.stringTwo = " (╯°□°)╯︵ ┻━┻";
|
||||
|
||||
// JSON equivalent of objectWithFlatAndComplexPropertiesPlusListAndMap
|
||||
documentWithFlatAndComplexPropertiesPlusListAndMap = Document.parse(
|
||||
"{ \"_id\" : \"517f6aee-e9e0-44f0-88ed-f3694a019f27\", \"intOne\" : -2147483648, \"intTwo\" : 2147483647, \"stringOne\" : \"¯\\\\_(ツ)_/¯\", \"stringTwo\" : \" (╯°□°)╯︵ ┻━┻\", \"explicit-field-name\" : \"supercalifragilisticexpialidocious\", \"location\" : { \"x\" : -33.865143, \"y\" : 151.2099 }, \"objectWith2PropertiesAnd1Nested\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"addressList\" : [{ \"zipCode\" : \"zip-1\", \"city\" : \"city-1\" }, { \"zipCode\" : \"zip-2\", \"city\" : \"city-2\" }], \"customerMap\" : { \"dave\" : { \"firstname\" : \"Dave\", \"lastname\" : \"Matthews\", \"address\" : { \"zipCode\" : \"zipCode\", \"city\" : \"City\" } }, \"deborah\" : { \"firstname\" : \"Deborah Anne\", \"lastname\" : \"Dyer\", \"address\" : { \"zipCode\" : \"?\", \"city\" : \"london\" } }, \"eddie\" : { \"firstname\" : \"Eddie\", \"lastname\" : \"Vedder\", \"address\" : { \"zipCode\" : \"??\", \"city\" : \"Seattle\" } } }, \"_class\" : \"org.springframework.data.mongodb.core.convert.MappingMongoConverterBenchmark$SlightlyMoreComplexObject\" }");
|
||||
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
|
||||
client.dropDatabase(DB_NAME);
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public Customer readObjectWith2Properties() {
|
||||
return converter.read(Customer.class, documentWith2Properties);
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public Customer readObjectWith2PropertiesAnd1NestedObject() {
|
||||
return converter.read(Customer.class, documentWith2PropertiesAnd1Nested);
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public Document writeObjectWith2PropertiesAnd1NestedObject() {
|
||||
|
||||
Document sink = new Document();
|
||||
converter.write(objectWith2PropertiesAnd1Nested, sink);
|
||||
return sink;
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public Object readObjectWithListAndMapsOfComplexType() {
|
||||
return converter.read(SlightlyMoreComplexObject.class, documentWithFlatAndComplexPropertiesPlusListAndMap);
|
||||
}
|
||||
|
||||
@Benchmark // DATAMONGO-1720
|
||||
public Object writeObjectWithListAndMapsOfComplexType() {
|
||||
|
||||
Document sink = new Document();
|
||||
converter.write(objectWithFlatAndComplexPropertiesPlusListAndMap, sink);
|
||||
return sink;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
static class Customer {
|
||||
|
||||
private @Id ObjectId id;
|
||||
private final String firstname, lastname;
|
||||
private final Address address;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
static class Address {
|
||||
private String zipCode, city;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class SlightlyMoreComplexObject {
|
||||
|
||||
@Id String id;
|
||||
int intOne, intTwo;
|
||||
String stringOne, stringTwo;
|
||||
@Field("explicit-field-name") String renamedField;
|
||||
Point location;
|
||||
Customer customer;
|
||||
List<Address> addressList;
|
||||
Map<String, Customer> customerMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.microbenchmark;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.results.format.ResultFormatType;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import org.openjdk.jmh.runner.options.TimeValue;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Warmup(iterations = AbstractMicrobenchmark.WARMUP_ITERATIONS)
|
||||
@Measurement(iterations = AbstractMicrobenchmark.MEASUREMENT_ITERATIONS)
|
||||
@Fork(AbstractMicrobenchmark.FORKS)
|
||||
@State(Scope.Thread)
|
||||
public class AbstractMicrobenchmark {
|
||||
|
||||
static final int WARMUP_ITERATIONS = 5;
|
||||
static final int MEASUREMENT_ITERATIONS = 10;
|
||||
static final int FORKS = 1;
|
||||
static final String[] JVM_ARGS = { "-server", "-XX:+HeapDumpOnOutOfMemoryError", "-Xms1024m", "-Xmx1024m",
|
||||
"-XX:MaxDirectMemorySize=1024m" };
|
||||
|
||||
private final StandardEnvironment environment = new StandardEnvironment();
|
||||
|
||||
/**
|
||||
* Run matching {@link org.openjdk.jmh.annotations.Benchmark} methods with options collected from
|
||||
* {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @throws Exception
|
||||
* @see #options(String)
|
||||
*/
|
||||
@Test
|
||||
public void run() throws Exception {
|
||||
|
||||
String includes = includes();
|
||||
|
||||
if (!includes.contains(org.springframework.util.ClassUtils.getShortName(getClass()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishResults(new Runner(options(includes).build()).run());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regex for all benchmarks to be included in the run. By default every benchmark within classes matching the
|
||||
* current ones short name. <br />
|
||||
* The {@literal benchmark} command line argument allows overriding the defaults using {@code #} as class / method
|
||||
* name separator.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @see org.springframework.util.ClassUtils#getShortName(Class)
|
||||
*/
|
||||
protected String includes() {
|
||||
|
||||
String tests = environment.getProperty("benchmark", String.class);
|
||||
|
||||
if (!StringUtils.hasText(tests)) {
|
||||
return ".*" + org.springframework.util.ClassUtils.getShortName(getClass()) + ".*";
|
||||
}
|
||||
|
||||
if (!tests.contains("#")) {
|
||||
return ".*" + tests + ".*";
|
||||
}
|
||||
|
||||
String[] args = tests.split("#");
|
||||
return ".*" + args[0] + "." + args[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all options for the {@link Runner}.
|
||||
*
|
||||
* @param includes regex for matching benchmarks to be included in the run.
|
||||
* @return never {@literal null}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ChainedOptionsBuilder options(String includes) throws Exception {
|
||||
|
||||
ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().include(includes).jvmArgs(jvmArgs());
|
||||
|
||||
optionsBuilder = warmup(optionsBuilder);
|
||||
optionsBuilder = measure(optionsBuilder);
|
||||
optionsBuilder = forks(optionsBuilder);
|
||||
optionsBuilder = report(optionsBuilder);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* JVM args to apply to {@link Runner} via its {@link org.openjdk.jmh.runner.options.Options}.
|
||||
*
|
||||
* @return {@link #JVM_ARGS} by default.
|
||||
*/
|
||||
protected String[] jvmArgs() {
|
||||
|
||||
String[] args = new String[JVM_ARGS.length];
|
||||
System.arraycopy(JVM_ARGS, 0, args, 0, JVM_ARGS.length);
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code warmupIterations} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getWarmupIterations() {
|
||||
return environment.getProperty("warmupIterations", Integer.class, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code measurementIterations} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getMeasurementIterations() {
|
||||
return environment.getProperty("measurementIterations", Integer.class, -1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code forks} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getForksCount() {
|
||||
return environment.getProperty("forks", Integer.class, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code benchmarkReportDir} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return {@literal null} if not set.
|
||||
*/
|
||||
protected String getReportDirectory() {
|
||||
return environment.getProperty("benchmarkReportDir");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code measurementTime} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected long getMeasurementTime() {
|
||||
return environment.getProperty("measurementTime", Long.class, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code warmupTime} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected long getWarmupTime() {
|
||||
return environment.getProperty("warmupTime", Long.class, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code project.version_yyyy-MM-dd_ClassName.json} eg.
|
||||
* {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String reportFilename() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (environment.containsProperty("project.version")) {
|
||||
|
||||
sb.append(environment.getProperty("project.version"));
|
||||
sb.append("_");
|
||||
}
|
||||
|
||||
sb.append(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
|
||||
sb.append("_");
|
||||
sb.append(org.springframework.util.ClassUtils.getShortName(getClass()));
|
||||
sb.append(".json");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply measurement options to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getMeasurementIterations()
|
||||
* @see #getMeasurementTime()
|
||||
*/
|
||||
private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int measurementIterations = getMeasurementIterations();
|
||||
long measurementTime = getMeasurementTime();
|
||||
|
||||
if (measurementIterations > 0) {
|
||||
optionsBuilder = optionsBuilder.measurementIterations(measurementIterations);
|
||||
}
|
||||
|
||||
if (measurementTime > 0) {
|
||||
optionsBuilder = optionsBuilder.measurementTime(TimeValue.seconds(measurementTime));
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply warmup options to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getWarmupIterations()
|
||||
* @see #getWarmupTime()
|
||||
*/
|
||||
private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int warmupIterations = getWarmupIterations();
|
||||
long warmupTime = getWarmupTime();
|
||||
|
||||
if (warmupIterations > 0) {
|
||||
optionsBuilder = optionsBuilder.warmupIterations(warmupIterations);
|
||||
}
|
||||
|
||||
if (warmupTime > 0) {
|
||||
optionsBuilder = optionsBuilder.warmupTime(TimeValue.seconds(warmupTime));
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply forks option to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getForksCount()
|
||||
*/
|
||||
private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int forks = getForksCount();
|
||||
|
||||
if (forks <= 0) {
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
return optionsBuilder.forks(forks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply report option to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @throws IOException if report file cannot be created.
|
||||
* @see #getReportDirectory()
|
||||
*/
|
||||
private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder) throws IOException {
|
||||
|
||||
String reportDir = getReportDirectory();
|
||||
|
||||
if (!StringUtils.hasText(reportDir)) {
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
String reportFilePath = reportDir + (reportDir.endsWith(File.separator) ? "" : File.separator) + reportFilename();
|
||||
File file = ResourceUtils.getFile(reportFilePath);
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
} else {
|
||||
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
optionsBuilder.resultFormat(ResultFormatType.JSON);
|
||||
optionsBuilder.result(reportFilePath);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish results to an external system.
|
||||
*
|
||||
* @param results must not be {@literal null}.
|
||||
*/
|
||||
private void publishResults(Collection<RunResult> results) {
|
||||
|
||||
if (CollectionUtils.isEmpty(results) || !environment.containsProperty("publishTo")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String uri = environment.getProperty("publishTo");
|
||||
try {
|
||||
ResultsWriter.forUri(uri).write(results);
|
||||
} catch (Exception e) {
|
||||
System.err.println(String.format("Cannot save benchmark results to '%s'. Error was %s.", uri, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.microbenchmark;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ResultsWriter} implementation of {@link URLConnection}.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class HttpResultsWriter implements ResultsWriter {
|
||||
|
||||
private final String url;
|
||||
|
||||
HttpResultsWriter(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void write(Collection<RunResult> results) {
|
||||
|
||||
if (CollectionUtils.isEmpty(results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
StandardEnvironment env = new StandardEnvironment();
|
||||
|
||||
String projectVersion = env.getProperty("project.version", "unknown");
|
||||
String gitBranch = env.getProperty("git.branch", "unknown");
|
||||
String gitDirty = env.getProperty("git.dirty", "no");
|
||||
String gitCommitId = env.getProperty("git.commit.id", "unknown");
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout((int) Duration.ofSeconds(1).toMillis());
|
||||
connection.setReadTimeout((int) Duration.ofSeconds(1).toMillis());
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.addRequestProperty("X-Project-Version", projectVersion);
|
||||
connection.addRequestProperty("X-Git-Branch", gitBranch);
|
||||
connection.addRequestProperty("X-Git-Dirty", gitDirty);
|
||||
connection.addRequestProperty("X-Git-Commit-Id", gitCommitId);
|
||||
|
||||
try (OutputStream output = connection.getOutputStream()) {
|
||||
output.write(ResultsWriter.jsonifyResults(results).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() >= 400) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Status %d %s", connection.getResponseCode(), connection.getResponseMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.microbenchmark;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* MongoDB specific {@link ResultsWriter} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
class MongoResultsWriter implements ResultsWriter {
|
||||
|
||||
private final String uri;
|
||||
|
||||
MongoResultsWriter(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Collection<RunResult> results) {
|
||||
|
||||
Date now = new Date();
|
||||
StandardEnvironment env = new StandardEnvironment();
|
||||
|
||||
String projectVersion = env.getProperty("project.version", "unknown");
|
||||
String gitBranch = env.getProperty("git.branch", "unknown");
|
||||
String gitDirty = env.getProperty("git.dirty", "no");
|
||||
String gitCommitId = env.getProperty("git.commit.id", "unknown");
|
||||
|
||||
MongoClientURI uri = new MongoClientURI(this.uri);
|
||||
MongoClient client = new MongoClient(uri);
|
||||
|
||||
String dbName = StringUtils.hasText(uri.getDatabase()) ? uri.getDatabase() : "spring-data-mongodb-benchmarks";
|
||||
MongoDatabase db = client.getDatabase(dbName);
|
||||
|
||||
for (BasicDBObject dbo : (List<BasicDBObject>) JSON.parse(ResultsWriter.jsonifyResults(results))) {
|
||||
|
||||
String collectionName = extractClass(dbo.get("benchmark").toString());
|
||||
|
||||
Document sink = new Document();
|
||||
sink.append("_version", projectVersion);
|
||||
sink.append("_branch", gitBranch);
|
||||
sink.append("_commit", gitCommitId);
|
||||
sink.append("_dirty", gitDirty);
|
||||
sink.append("_method", extractBenchmarkName(dbo.get("benchmark").toString()));
|
||||
sink.append("_date", now);
|
||||
sink.append("_snapshot", projectVersion.toLowerCase().contains("snapshot"));
|
||||
|
||||
sink.putAll(dbo);
|
||||
|
||||
db.getCollection(collectionName).insertOne(fixDocumentKeys(sink));
|
||||
}
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace {@code .} by {@code ,}.
|
||||
*
|
||||
* @param doc
|
||||
* @return
|
||||
*/
|
||||
private Document fixDocumentKeys(Document doc) {
|
||||
|
||||
Document sanitized = new Document();
|
||||
|
||||
for (Object key : doc.keySet()) {
|
||||
|
||||
Object value = doc.get(key);
|
||||
if (value instanceof Document) {
|
||||
value = fixDocumentKeys((Document) value);
|
||||
} else if (value instanceof BasicDBObject) {
|
||||
value = fixDocumentKeys(new Document((BasicDBObject) value));
|
||||
}
|
||||
|
||||
if (key instanceof String) {
|
||||
|
||||
String newKey = (String) key;
|
||||
if (newKey.contains(".")) {
|
||||
newKey = newKey.replace('.', ',');
|
||||
}
|
||||
|
||||
sanitized.put(newKey, value);
|
||||
} else {
|
||||
sanitized.put(ObjectUtils.nullSafeToString(key).replace('.', ','), value);
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
private static String extractClass(String source) {
|
||||
|
||||
String tmp = source.substring(0, source.lastIndexOf('.'));
|
||||
return tmp.substring(tmp.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
private static String extractBenchmarkName(String source) {
|
||||
return source.substring(source.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.microbenchmark;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.results.format.ResultFormatFactory;
|
||||
import org.openjdk.jmh.results.format.ResultFormatType;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ResultsWriter {
|
||||
|
||||
/**
|
||||
* Write the {@link RunResult}s.
|
||||
*
|
||||
* @param results can be {@literal null}.
|
||||
*/
|
||||
void write(Collection<RunResult> results);
|
||||
|
||||
/**
|
||||
* Get the uri specific {@link ResultsWriter}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
static ResultsWriter forUri(String uri) {
|
||||
return uri.startsWith("mongodb:") ? new MongoResultsWriter(uri) : new HttpResultsWriter(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {@link RunResult}s to JMH Json representation.
|
||||
*
|
||||
* @param results
|
||||
* @return json string representation of results.
|
||||
* @see org.openjdk.jmh.results.format.JSONResultFormat
|
||||
*/
|
||||
@SneakyThrows
|
||||
static String jsonifyResults(Collection<RunResult> results) {
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ResultFormatFactory.getInstance(ResultFormatType.JSON, new PrintStream(baos, true, "UTF-8")).writeOut(results);
|
||||
|
||||
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,14 +48,13 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- reactive -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,12 @@ package org.springframework.data.mongodb.crossstore;
|
||||
|
||||
import org.springframework.data.crossstore.ChangeSetBacked;
|
||||
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @deprecated will be removed without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface DocumentBacked extends ChangeSetBacked {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,9 @@ import com.mongodb.client.result.DeleteResult;
|
||||
* @author Oliver Gierke
|
||||
* @author Alex Vengrovsk
|
||||
* @author Mark Paluch
|
||||
* @deprecated will be removed without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
|
||||
private static final String ENTITY_CLASS = "_entity_class";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
* Aspect to turn an object annotated with @Document into a persistent document using Mongo.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @deprecated will be removed without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
public aspect MongoDocumentBacking {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDocumentBacking.class);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,9 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
* @deprecated will be removed without replacement.
|
||||
*/
|
||||
@Deprecated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD })
|
||||
public @interface RelatedDocument {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# MongoDB Log4J Appender
|
||||
|
||||
This module sets up a Log4J appender that puts logging events in MongoDB. It is fully configurable
|
||||
and connects directly to the MongoDB server using the driver. It has no dependency on any Spring package.
|
||||
|
||||
To use it, configure a host, port, (optionally) applicationId, and database property in your Log4J configuration:
|
||||
|
||||
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
log4j.appender.stdout.host = localhost
|
||||
log4j.appender.stdout.port = 27017
|
||||
log4j.appender.stdout.database = logs
|
||||
log4j.appender.stdout.collectionPattern = %c
|
||||
log4j.appender.stdout.applicationId = my.application
|
||||
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE
|
||||
|
||||
It will even support properties in your MDC (so long as they're Strings or support .toString()).
|
||||
|
||||
The collection name is configurable as well. If you don't specify anything, it will use the Category name.
|
||||
If you want to specify a collection name, you can give it a Log4J pattern layout format string which will have
|
||||
the following additional MDC variables in the context when the collection name is rendered:
|
||||
|
||||
"year" = Calendar.YEAR
|
||||
"month" = Calendar.MONTH + 1
|
||||
"day" = Calendar.DAY_OF_MONTH
|
||||
"hour" = Calendar.HOUR_OF_DAY
|
||||
"applicationId" = configured applicationId
|
||||
|
||||
An example log entry might look like:
|
||||
|
||||
{
|
||||
"_id" : ObjectId("4d89341a8ef397e06940d5cd"),
|
||||
"applicationId" : "my.application",
|
||||
"name" : "org.springframework.data.mongodb.log4j.MongoLog4jAppenderIntegrationTests",
|
||||
"level" : "DEBUG",
|
||||
"timestamp" : ISODate("2011-03-23T16:53:46.778Z"),
|
||||
"properties" : {
|
||||
"property" : "one"
|
||||
},
|
||||
"message" : "DEBUG message"
|
||||
}
|
||||
|
||||
To set WriteConcern levels for WARN or higher messages, set warnOrHigherWriteConcern to one of the following:
|
||||
|
||||
* FSYNC_SAFE
|
||||
* NONE
|
||||
* NORMAL
|
||||
* REPLICAS_SAFE
|
||||
* SAFE
|
||||
|
||||
[http://api.mongodb.org/java/2.5-pre-/com/mongodb/WriteConcern.html#field_detail](http://api.mongodb.org/java/2.5-pre-/com/mongodb/WriteConcern.html#field_detail)
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-mongodb-log4j</artifactId>
|
||||
<name>Spring Data MongoDB - Log4J Appender</name>
|
||||
|
||||
<properties>
|
||||
<log4j>1.2.16</log4j>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>${log4j}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.log4j;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.AppenderSkeleton;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.MDC;
|
||||
import org.apache.log4j.PatternLayout;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* Log4j appender writing log entries into a MongoDB instance.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Ricardo Espirito Santo
|
||||
*/
|
||||
public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
|
||||
public static final String LEVEL = "level";
|
||||
public static final String NAME = "name";
|
||||
public static final String APP_ID = "applicationId";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String PROPERTIES = "properties";
|
||||
public static final String TRACEBACK = "traceback";
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String YEAR = "year";
|
||||
public static final String MONTH = "month";
|
||||
public static final String DAY = "day";
|
||||
public static final String HOUR = "hour";
|
||||
|
||||
protected String host = "localhost";
|
||||
protected int port = 27017;
|
||||
protected String username;
|
||||
protected String password;
|
||||
protected String authenticationDatabase;
|
||||
protected String database = "logs";
|
||||
protected String collectionPattern = "%c";
|
||||
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
|
||||
protected String applicationId = System.getProperty("APPLICATION_ID", null);
|
||||
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.ACKNOWLEDGED;
|
||||
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.UNACKNOWLEDGED;
|
||||
protected Mongo mongo;
|
||||
protected DB db;
|
||||
|
||||
public MongoLog4jAppender() {}
|
||||
|
||||
public MongoLog4jAppender(boolean isActive) {
|
||||
super(isActive);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param username may be {@literal null} for unauthenticated access.
|
||||
* @since 1.10
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param password may be {@literal null} for unauthenticated access.
|
||||
* @since 1.10
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getAuthenticationDatabase() {
|
||||
return authenticationDatabase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authenticationDatabase may be {@literal null} to use {@link #getDatabase()} as authentication database.
|
||||
* @since 1.10
|
||||
*/
|
||||
public void setAuthenticationDatabase(String authenticationDatabase) {
|
||||
this.authenticationDatabase = authenticationDatabase;
|
||||
}
|
||||
|
||||
public String getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public void setDatabase(String database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
public String getCollectionPattern() {
|
||||
return collectionPattern;
|
||||
}
|
||||
|
||||
public void setCollectionPattern(String collectionPattern) {
|
||||
this.collectionPattern = collectionPattern;
|
||||
this.collectionLayout = new PatternLayout(collectionPattern);
|
||||
}
|
||||
|
||||
public String getApplicationId() {
|
||||
return applicationId;
|
||||
}
|
||||
|
||||
public void setApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
public String getWarnOrHigherWriteConcern() {
|
||||
return warnOrHigherWriteConcern.toString();
|
||||
}
|
||||
|
||||
public void setWarnOrHigherWriteConcern(String wc) {
|
||||
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc);
|
||||
}
|
||||
|
||||
public String getInfoOrLowerWriteConcern() {
|
||||
return infoOrLowerWriteConcern.toString();
|
||||
}
|
||||
|
||||
public void setInfoOrLowerWriteConcern(String wc) {
|
||||
this.infoOrLowerWriteConcern = WriteConcern.valueOf(wc);
|
||||
}
|
||||
|
||||
protected void connectToMongo() throws UnknownHostException {
|
||||
|
||||
this.mongo = createMongoClient();
|
||||
this.db = mongo.getDB(database);
|
||||
}
|
||||
|
||||
private MongoClient createMongoClient() throws UnknownHostException {
|
||||
|
||||
ServerAddress serverAddress = new ServerAddress(host, port);
|
||||
|
||||
if (null == password || null == username) {
|
||||
return new MongoClient(serverAddress);
|
||||
}
|
||||
|
||||
String authenticationDatabaseToUse = authenticationDatabase == null ? this.database : authenticationDatabase;
|
||||
MongoCredential mongoCredential = MongoCredential.createCredential(username,
|
||||
authenticationDatabaseToUse, password.toCharArray());
|
||||
List<MongoCredential> credentials = Collections.singletonList(mongoCredential);
|
||||
return new MongoClient(serverAddress, credentials);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
protected void append(final LoggingEvent event) {
|
||||
if (null == db) {
|
||||
try {
|
||||
connectToMongo();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
if (null != applicationId) {
|
||||
dbo.put(APP_ID, applicationId);
|
||||
MDC.put(APP_ID, applicationId);
|
||||
}
|
||||
dbo.put(NAME, event.getLogger().getName());
|
||||
dbo.put(LEVEL, event.getLevel().toString());
|
||||
Calendar tstamp = Calendar.getInstance();
|
||||
tstamp.setTimeInMillis(event.getTimeStamp());
|
||||
dbo.put(TIMESTAMP, tstamp.getTime());
|
||||
|
||||
// Copy properties into document
|
||||
Map<Object, Object> props = event.getProperties();
|
||||
if (null != props && !props.isEmpty()) {
|
||||
BasicDBObject propsDbo = new BasicDBObject();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
propsDbo.put(entry.getKey().toString(), entry.getValue().toString());
|
||||
}
|
||||
dbo.put(PROPERTIES, propsDbo);
|
||||
}
|
||||
|
||||
// Copy traceback info (if there is any) into the document
|
||||
String[] traceback = event.getThrowableStrRep();
|
||||
if (null != traceback && traceback.length > 0) {
|
||||
BasicDBList tbDbo = new BasicDBList();
|
||||
tbDbo.addAll(Arrays.asList(traceback));
|
||||
dbo.put(TRACEBACK, tbDbo);
|
||||
}
|
||||
|
||||
// Put the rendered message into the document
|
||||
dbo.put(MESSAGE, event.getRenderedMessage());
|
||||
|
||||
// Insert the document
|
||||
Calendar now = Calendar.getInstance();
|
||||
MDC.put(YEAR, now.get(Calendar.YEAR));
|
||||
MDC.put(MONTH, String.format("%1$02d", now.get(Calendar.MONTH) + 1));
|
||||
MDC.put(DAY, String.format("%1$02d", now.get(Calendar.DAY_OF_MONTH)));
|
||||
MDC.put(HOUR, String.format("%1$02d", now.get(Calendar.HOUR_OF_DAY)));
|
||||
|
||||
String coll = collectionLayout.format(event);
|
||||
|
||||
MDC.remove(YEAR);
|
||||
MDC.remove(MONTH);
|
||||
MDC.remove(DAY);
|
||||
MDC.remove(HOUR);
|
||||
if (null != applicationId) {
|
||||
MDC.remove(APP_ID);
|
||||
}
|
||||
|
||||
WriteConcern wc;
|
||||
if (event.getLevel().isGreaterOrEqual(Level.WARN)) {
|
||||
wc = warnOrHigherWriteConcern;
|
||||
} else {
|
||||
wc = infoOrLowerWriteConcern;
|
||||
}
|
||||
db.getCollection(coll).insert(dbo, wc);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.log4j.AppenderSkeleton#close()
|
||||
*/
|
||||
public void close() {
|
||||
|
||||
if (mongo != null) {
|
||||
mongo.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.log4j.AppenderSkeleton#requiresLayout()
|
||||
*/
|
||||
public boolean requiresLayout() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Infrastructure for to use MongoDB as a logging sink.
|
||||
*/
|
||||
package org.springframework.data.mongodb.log4j;
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.log4j;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.MDC;
|
||||
import org.apache.log4j.PropertyConfigurator;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoLog4jAppender} using authentication.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MongoLog4jAppenderAuthenticationIntegrationTests {
|
||||
|
||||
private final static String username = "admin";
|
||||
private final static String password = "test";
|
||||
private final static String authenticationDatabase = "logs";
|
||||
|
||||
MongoClient mongo;
|
||||
DB db;
|
||||
String collection;
|
||||
ServerAddress serverLocation;
|
||||
|
||||
Logger log;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
serverLocation = new ServerAddress("localhost", 27017);
|
||||
|
||||
mongo = new MongoClient(serverLocation);
|
||||
db = mongo.getDB("logs");
|
||||
|
||||
BasicDBList roles = new BasicDBList();
|
||||
roles.add("dbOwner");
|
||||
db.command(new BasicDBObjectBuilder().add("createUser", username).add("pwd", password).add("roles", roles).get());
|
||||
mongo.close();
|
||||
|
||||
mongo = new MongoClient(serverLocation, Collections
|
||||
.singletonList(MongoCredential.createCredential(username, authenticationDatabase, password.toCharArray())));
|
||||
db = mongo.getDB("logs");
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
|
||||
|
||||
LogManager.resetConfiguration();
|
||||
PropertyConfigurator.configure(getClass().getResource("/log4j-with-authentication.properties"));
|
||||
|
||||
log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
|
||||
if (db != null) {
|
||||
db.getCollection(collection).remove(new BasicDBObject());
|
||||
db.command(new BasicDBObject("dropUser", username));
|
||||
}
|
||||
|
||||
LogManager.resetConfiguration();
|
||||
PropertyConfigurator.configure(getClass().getResource("/log4j.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging() {
|
||||
|
||||
log.debug("DEBUG message");
|
||||
log.info("INFO message");
|
||||
log.warn("WARN message");
|
||||
log.error("ERROR message");
|
||||
|
||||
DBCursor msgs = db.getCollection(collection).find();
|
||||
assertThat(msgs.count(), is(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperties() {
|
||||
MDC.put("property", "one");
|
||||
log.debug("DEBUG message");
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.log4j;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.MDC;
|
||||
import org.apache.log4j.PropertyConfigurator;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoLog4jAppender}.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoLog4jAppenderIntegrationTests {
|
||||
|
||||
MongoClient mongo;
|
||||
DB db;
|
||||
String collection;
|
||||
ServerAddress serverLocation;
|
||||
Logger log;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
serverLocation = new ServerAddress("localhost", 27017);
|
||||
|
||||
mongo = new MongoClient(serverLocation);
|
||||
db = mongo.getDB("logs");
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
|
||||
|
||||
log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
db.getCollection(collection).remove(new BasicDBObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging() {
|
||||
|
||||
log.debug("DEBUG message");
|
||||
log.info("INFO message");
|
||||
log.warn("WARN message");
|
||||
log.error("ERROR message");
|
||||
|
||||
DBCursor msgs = db.getCollection(collection).find();
|
||||
assertThat(msgs.count(), is(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperties() {
|
||||
MDC.put("property", "one");
|
||||
log.debug("DEBUG message");
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
log4j.rootCategory=INFO, mongo
|
||||
|
||||
log4j.appender.mongo=org.springframework.data.mongodb.log4j.MongoLog4jAppender
|
||||
log4j.appender.mongo.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
log4j.appender.mongo.host = localhost
|
||||
log4j.appender.mongo.port = 27017
|
||||
log4j.appender.mongo.database = logs
|
||||
log4j.appender.mongo.username = admin
|
||||
log4j.appender.mongo.password = test
|
||||
log4j.appender.mongo.authenticationDatabase = logs
|
||||
log4j.appender.mongo.collectionPattern = %X{year}%X{month}
|
||||
log4j.appender.mongo.applicationId = my.application
|
||||
log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE
|
||||
|
||||
log4j.category.org.springframework.data.mongodb=DEBUG
|
||||
@@ -1,13 +0,0 @@
|
||||
log4j.rootCategory=INFO, mongo
|
||||
|
||||
log4j.appender.mongo=org.springframework.data.mongodb.log4j.MongoLog4jAppender
|
||||
log4j.appender.mongo.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
log4j.appender.mongo.host = localhost
|
||||
log4j.appender.mongo.port = 27017
|
||||
log4j.appender.mongo.database = logs
|
||||
log4j.appender.mongo.collectionPattern = %X{year}%X{month}
|
||||
log4j.appender.mongo.applicationId = my.application
|
||||
log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE
|
||||
|
||||
log4j.category.org.springframework.data.mongodb=DEBUG
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.0.0.M3</version>
|
||||
<version>2.0.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -106,14 +106,12 @@
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.addons</groupId>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>${reactor}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -231,11 +229,124 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.schauderhaft.degraph</groupId>
|
||||
<artifactId>degraph-check</artifactId>
|
||||
<version>0.1.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Kotlin extension -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nhaarman</groupId>
|
||||
<artifactId>mockito-kotlin</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin}</version>
|
||||
<configuration>
|
||||
<jvmTarget>${source.level}</jvmTarget>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
|
||||
<sourceDir>${project.basedir}/src/main/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
|
||||
<sourceDir>${project.basedir}/src/test/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>apt-maven-plugin</artifactId>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.web.config.SpringDataJacksonModules;
|
||||
* Configuration class to expose {@link GeoJsonModule} as a Spring bean.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class GeoJsonConfiguration implements SpringDataJacksonModules {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -29,7 +31,7 @@ import com.mongodb.MongoCredential;
|
||||
|
||||
/**
|
||||
* Parse a {@link String} to a Collection of {@link MongoCredential}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
@@ -39,10 +41,10 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
private static final Pattern GROUP_PATTERN = Pattern.compile("(\\\\?')(.*?)\\1");
|
||||
|
||||
private static final String AUTH_MECHANISM_KEY = "uri.authMechanism";
|
||||
private static final String USERNAME_PASSWORD_DELIMINATOR = ":";
|
||||
private static final String DATABASE_DELIMINATOR = "@";
|
||||
private static final String OPTIONS_DELIMINATOR = "?";
|
||||
private static final String OPTION_VALUE_DELIMINATOR = "&";
|
||||
private static final String USERNAME_PASSWORD_DELIMITER = ":";
|
||||
private static final String DATABASE_DELIMITER = "@";
|
||||
private static final String OPTIONS_DELIMITER = "?";
|
||||
private static final String OPTION_VALUE_DELIMITER = "&";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -55,7 +57,7 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
return;
|
||||
}
|
||||
|
||||
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
|
||||
List<MongoCredential> credentials = new ArrayList<>();
|
||||
|
||||
for (String credentialString : extractCredentialsString(text)) {
|
||||
|
||||
@@ -115,7 +117,7 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
private List<String> extractCredentialsString(String source) {
|
||||
|
||||
Matcher matcher = GROUP_PATTERN.matcher(source);
|
||||
List<String> list = new ArrayList<String>();
|
||||
List<String> list = new ArrayList<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
|
||||
@@ -132,39 +134,44 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private static String[] extractUserNameAndPassword(String text) {
|
||||
|
||||
int index = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
int index = text.lastIndexOf(DATABASE_DELIMITER);
|
||||
|
||||
index = index != -1 ? index : text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
index = index != -1 ? index : text.lastIndexOf(OPTIONS_DELIMITER);
|
||||
|
||||
return index == -1 ? new String[] {} : text.substring(0, index).split(USERNAME_PASSWORD_DELIMINATOR);
|
||||
if (index == -1) {
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
return Arrays.stream(text.substring(0, index).split(USERNAME_PASSWORD_DELIMITER))
|
||||
.map(MongoCredentialPropertyEditor::decodeParameter).toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static String extractDB(String text) {
|
||||
|
||||
int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
int dbSeparationIndex = text.lastIndexOf(DATABASE_DELIMITER);
|
||||
|
||||
if (dbSeperationIndex == -1) {
|
||||
if (dbSeparationIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String tmp = text.substring(dbSeperationIndex + 1);
|
||||
int optionsSeperationIndex = tmp.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
String tmp = text.substring(dbSeparationIndex + 1);
|
||||
int optionsSeparationIndex = tmp.lastIndexOf(OPTIONS_DELIMITER);
|
||||
|
||||
return optionsSeperationIndex > -1 ? tmp.substring(0, optionsSeperationIndex) : tmp;
|
||||
return optionsSeparationIndex > -1 ? tmp.substring(0, optionsSeparationIndex) : tmp;
|
||||
}
|
||||
|
||||
private static Properties extractOptions(String text) {
|
||||
|
||||
int optionsSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
int dbSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
int optionsSeparationIndex = text.lastIndexOf(OPTIONS_DELIMITER);
|
||||
int dbSeparationIndex = text.lastIndexOf(OPTIONS_DELIMITER);
|
||||
|
||||
if (optionsSeperationIndex == -1 || dbSeperationIndex > optionsSeperationIndex) {
|
||||
if (optionsSeparationIndex == -1 || dbSeparationIndex > optionsSeparationIndex) {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
Properties properties = new Properties();
|
||||
|
||||
for (String option : text.substring(optionsSeperationIndex + 1).split(OPTION_VALUE_DELIMINATOR)) {
|
||||
for (String option : text.substring(optionsSeparationIndex + 1).split(OPTION_VALUE_DELIMITER)) {
|
||||
String[] optionArgs = option.split("=");
|
||||
properties.put(optionArgs[0], optionArgs[1]);
|
||||
}
|
||||
@@ -195,4 +202,12 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
throw new IllegalArgumentException("Credentials need to specify username!");
|
||||
}
|
||||
}
|
||||
|
||||
private static String decodeParameter(String it) {
|
||||
try {
|
||||
return URLDecoder.decode(it, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("o_O UTF-8 not supported!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import com.mongodb.bulk.BulkWriteResult;
|
||||
* 2.6 and make use of low level bulk commands on the protocol level. This interface defines a fluent API to add
|
||||
* multiple single operations or list of similar operations in sequence which can then eventually be executed by calling
|
||||
* {@link #execute()}.
|
||||
*
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @since 1.9
|
||||
@@ -49,7 +49,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a single insert to the bulk operation.
|
||||
*
|
||||
*
|
||||
* @param documents the document to insert, must not be {@literal null}.
|
||||
* @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}.
|
||||
*/
|
||||
@@ -57,7 +57,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a list of inserts to the bulk operation.
|
||||
*
|
||||
*
|
||||
* @param documents List of documents to insert, must not be {@literal null}.
|
||||
* @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}.
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a single update to the bulk operation. For the update request, only the first matching document is updated.
|
||||
*
|
||||
*
|
||||
* @param query update criteria, must not be {@literal null}.
|
||||
* @param update {@link Update} operation to perform, must not be {@literal null}.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
@@ -74,7 +74,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a list of updates to the bulk operation. For each update request, only the first matching document is updated.
|
||||
*
|
||||
*
|
||||
* @param updates Update operations to perform.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a single update to the bulk operation. For the update request, all matching documents are updated.
|
||||
*
|
||||
*
|
||||
* @param query Update criteria.
|
||||
* @param update Update operation to perform.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
@@ -91,7 +91,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a list of updates to the bulk operation. For each update request, all matching documents are updated.
|
||||
*
|
||||
*
|
||||
* @param updates Update operations to perform.
|
||||
* @return The bulk operation.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
@@ -101,7 +101,7 @@ public interface BulkOperations {
|
||||
/**
|
||||
* Add a single upsert to the bulk operation. An upsert is an update if the set of matching documents is not empty,
|
||||
* else an insert.
|
||||
*
|
||||
*
|
||||
* @param query Update criteria.
|
||||
* @param update Update operation to perform.
|
||||
* @return The bulk operation.
|
||||
@@ -112,7 +112,7 @@ public interface BulkOperations {
|
||||
/**
|
||||
* Add a list of upserts to the bulk operation. An upsert is an update if the set of matching documents is not empty,
|
||||
* else an insert.
|
||||
*
|
||||
*
|
||||
* @param updates Updates/insert operations to perform.
|
||||
* @return The bulk operation.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
@@ -121,7 +121,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a single remove operation to the bulk operation.
|
||||
*
|
||||
*
|
||||
* @param remove the {@link Query} to select the documents to be removed, must not be {@literal null}.
|
||||
* @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}.
|
||||
*/
|
||||
@@ -129,7 +129,7 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Add a list of remove operations to the bulk operation.
|
||||
*
|
||||
*
|
||||
* @param removes the remove operations to perform, must not be {@literal null}.
|
||||
* @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}.
|
||||
*/
|
||||
@@ -137,9 +137,9 @@ public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Execute all bulk operations using the default write concern.
|
||||
*
|
||||
*
|
||||
* @return Result of the bulk operation providing counters for inserts/updates etc.
|
||||
* @throws {@link BulkOperationException} if an error occurred during bulk processing.
|
||||
* @throws org.springframework.data.mongodb.BulkOperationException if an error occurred during bulk processing.
|
||||
*/
|
||||
BulkWriteResult execute();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -28,10 +29,10 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class CollectionOptions {
|
||||
|
||||
private Integer maxDocuments;
|
||||
private Integer size;
|
||||
private Long maxDocuments;
|
||||
private Long size;
|
||||
private Boolean capped;
|
||||
private Optional<Collation> collation;
|
||||
private Collation collation;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>CollectionOptions</code> instance.
|
||||
@@ -40,12 +41,14 @@ public class CollectionOptions {
|
||||
* @param maxDocuments the maximum number of documents in the collection.
|
||||
* @param capped true to created a "capped" collection (fixed size with auto-FIFO behavior based on insertion order),
|
||||
* false otherwise.
|
||||
* @deprecated since 2.0 please use {@link CollectionOptions#empty()} as entry point.
|
||||
*/
|
||||
public CollectionOptions(Integer size, Integer maxDocuments, Boolean capped) {
|
||||
this(size, maxDocuments, capped, Optional.empty());
|
||||
@Deprecated
|
||||
public CollectionOptions(Long size, Long maxDocuments, Boolean capped) {
|
||||
this(size, maxDocuments, capped, null);
|
||||
}
|
||||
|
||||
private CollectionOptions(Integer size, Integer maxDocuments, Boolean capped, Optional<Collation> collation) {
|
||||
private CollectionOptions(Long size, Long maxDocuments, Boolean capped, Collation collation) {
|
||||
|
||||
this.maxDocuments = maxDocuments;
|
||||
this.size = size;
|
||||
@@ -53,8 +56,6 @@ public class CollectionOptions {
|
||||
this.collation = collation;
|
||||
}
|
||||
|
||||
private CollectionOptions() {}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} by just providing the {@link Collation} to use.
|
||||
*
|
||||
@@ -66,9 +67,7 @@ public class CollectionOptions {
|
||||
|
||||
Assert.notNull(collation, "Collation must not be null!");
|
||||
|
||||
CollectionOptions options = new CollectionOptions();
|
||||
options.setCollation(collation);
|
||||
return options;
|
||||
return new CollectionOptions(null, null, null, collation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,17 +77,17 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public static CollectionOptions empty() {
|
||||
return new CollectionOptions();
|
||||
return new CollectionOptions(null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}.
|
||||
* Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}. <br />
|
||||
* <strong>NOTE</strong> Using capped collections requires defining {@link #size(int)}.
|
||||
*
|
||||
* @param size the collection size in bytes, this data space is preallocated.
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions capped(int size) {
|
||||
public CollectionOptions capped() {
|
||||
return new CollectionOptions(size, maxDocuments, true, collation);
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ public class CollectionOptions {
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions maxDocuments(Integer maxDocuments) {
|
||||
public CollectionOptions maxDocuments(long maxDocuments) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ public class CollectionOptions {
|
||||
* @return new {@link CollectionOptions}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions size(int size) {
|
||||
public CollectionOptions size(long size) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
}
|
||||
|
||||
@@ -122,50 +121,44 @@ public class CollectionOptions {
|
||||
* @since 2.0
|
||||
*/
|
||||
public CollectionOptions collation(Collation collation) {
|
||||
return new CollectionOptions(size, maxDocuments, capped, Optional.ofNullable(collation));
|
||||
}
|
||||
|
||||
public Integer getMaxDocuments() {
|
||||
return maxDocuments;
|
||||
}
|
||||
|
||||
public void setMaxDocuments(Integer maxDocuments) {
|
||||
this.maxDocuments = maxDocuments;
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Boolean getCapped() {
|
||||
return capped;
|
||||
}
|
||||
|
||||
public void setCapped(Boolean capped) {
|
||||
this.capped = capped;
|
||||
return new CollectionOptions(size, maxDocuments, capped, collation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link Collation} options.
|
||||
* Get the max number of documents the collection should be limited to.
|
||||
*
|
||||
* @param collation
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
public Optional<Long> getMaxDocuments() {
|
||||
return Optional.ofNullable(maxDocuments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal size} in bytes the collection should be limited to.
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
*/
|
||||
public Optional<Long> getSize() {
|
||||
return Optional.ofNullable(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the collection should be capped.
|
||||
*
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
* @since 2.0
|
||||
*/
|
||||
public void setCollation(Collation collation) {
|
||||
this.collation = Optional.ofNullable(collation);
|
||||
public Optional<Boolean> getCapped() {
|
||||
return Optional.ofNullable(capped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Collation} settings.
|
||||
*
|
||||
* @return
|
||||
* @return {@link Optional#empty()} if not set.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Optional<Collation> getCollation() {
|
||||
return collation;
|
||||
return Optional.ofNullable(collation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,23 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mongodb.client.model.DeleteOptions;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.convert.UpdateMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.util.Pair;
|
||||
@@ -33,6 +42,8 @@ import com.mongodb.WriteConcern;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.model.BulkWriteOptions;
|
||||
import com.mongodb.client.model.DeleteManyModel;
|
||||
import com.mongodb.client.model.DeleteOneModel;
|
||||
import com.mongodb.client.model.DeleteOptions;
|
||||
import com.mongodb.client.model.InsertOneModel;
|
||||
import com.mongodb.client.model.UpdateManyModel;
|
||||
import com.mongodb.client.model.UpdateOneModel;
|
||||
@@ -41,50 +52,46 @@ import com.mongodb.client.model.WriteModel;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BulkOperations}.
|
||||
*
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.9
|
||||
*/
|
||||
class DefaultBulkOperations implements BulkOperations {
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final BulkMode bulkMode;
|
||||
private final String collectionName;
|
||||
private final BulkOperationContext bulkOperationContext;
|
||||
private final List<WriteModel<Document>> models = new ArrayList<>();
|
||||
|
||||
private PersistenceExceptionTranslator exceptionTranslator;
|
||||
private WriteConcernResolver writeConcernResolver;
|
||||
private WriteConcern defaultWriteConcern;
|
||||
|
||||
private BulkWriteOptions bulkOptions;
|
||||
|
||||
List<WriteModel<Document>> models = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, {@link BulkMode}, collection
|
||||
* name and {@link WriteConcern}.
|
||||
* Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, collection name and
|
||||
* {@link BulkOperationContext}.
|
||||
*
|
||||
* @param mongoOperations The underlying {@link MongoOperations}, must not be {@literal null}.
|
||||
* @param bulkMode must not be {@literal null}.
|
||||
* @param collectionName Name of the collection to work on, must not be {@literal null} or empty.
|
||||
* @param entityType the entity type, can be {@literal null}.
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param bulkOperationContext must not be {@literal null}.
|
||||
* @since 2.0
|
||||
*/
|
||||
DefaultBulkOperations(MongoOperations mongoOperations, BulkMode bulkMode, String collectionName,
|
||||
Class<?> entityType) {
|
||||
DefaultBulkOperations(MongoOperations mongoOperations, String collectionName,
|
||||
BulkOperationContext bulkOperationContext) {
|
||||
|
||||
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
|
||||
Assert.notNull(bulkMode, "BulkMode must not be null!");
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
Assert.hasText(collectionName, "CollectionName must not be null nor empty!");
|
||||
Assert.notNull(bulkOperationContext, "BulkOperationContext must not be null!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
this.bulkMode = bulkMode;
|
||||
this.collectionName = collectionName;
|
||||
|
||||
this.bulkOperationContext = bulkOperationContext;
|
||||
this.exceptionTranslator = new MongoExceptionTranslator();
|
||||
this.writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
|
||||
|
||||
this.bulkOptions = initBulkOperation();
|
||||
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,22 +103,12 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
this.exceptionTranslator = exceptionTranslator == null ? new MongoExceptionTranslator() : exceptionTranslator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link WriteConcernResolver} to be used. Defaults to {@link DefaultWriteConcernResolver}.
|
||||
*
|
||||
* @param writeConcernResolver can be {@literal null}.
|
||||
*/
|
||||
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
|
||||
this.writeConcernResolver = writeConcernResolver == null ? DefaultWriteConcernResolver.INSTANCE
|
||||
: writeConcernResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}.
|
||||
*
|
||||
* @param defaultWriteConcern can be {@literal null}.
|
||||
*/
|
||||
public void setDefaultWriteConcern(WriteConcern defaultWriteConcern) {
|
||||
void setDefaultWriteConcern(WriteConcern defaultWriteConcern) {
|
||||
this.defaultWriteConcern = defaultWriteConcern;
|
||||
}
|
||||
|
||||
@@ -134,6 +131,7 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
mongoOperations.getConverter().write(document, sink);
|
||||
|
||||
models.add(new InsertOneModel<>(sink));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -146,9 +144,7 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
|
||||
Assert.notNull(documents, "Documents must not be null!");
|
||||
|
||||
for (Object document : documents) {
|
||||
insert(document);
|
||||
}
|
||||
documents.forEach(this::insert);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -164,7 +160,7 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return updateOne(Arrays.asList(Pair.of(query, update)));
|
||||
return updateOne(Collections.singletonList(Pair.of(query, update)));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -194,7 +190,7 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return updateMulti(Arrays.asList(Pair.of(query, update)));
|
||||
return updateMulti(Collections.singletonList(Pair.of(query, update)));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -248,7 +244,8 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
DeleteOptions deleteOptions = new DeleteOptions();
|
||||
query.getCollation().map(Collation::toMongoCollation).ifPresent(deleteOptions::collation);
|
||||
|
||||
models.add(new DeleteManyModel(query.getQueryObject(), deleteOptions));
|
||||
models.add(new DeleteManyModel<>(query.getQueryObject(), deleteOptions));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -282,7 +279,7 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
collection = collection.withWriteConcern(defaultWriteConcern);
|
||||
}
|
||||
|
||||
return collection.bulkWrite(models, bulkOptions);
|
||||
return collection.bulkWrite(models.stream().map(this::mapWriteModel).collect(Collectors.toList()), bulkOptions);
|
||||
|
||||
} catch (BulkWriteException o_O) {
|
||||
|
||||
@@ -290,13 +287,13 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
throw toThrow == null ? o_O : toThrow;
|
||||
|
||||
} finally {
|
||||
this.bulkOptions = initBulkOperation();
|
||||
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs update and upsert bulk operations.
|
||||
*
|
||||
*
|
||||
* @param query the {@link Query} to determine documents to update.
|
||||
* @param update the {@link Update} to perform, must not be {@literal null}.
|
||||
* @param upsert whether to upsert.
|
||||
@@ -317,18 +314,81 @@ class DefaultBulkOperations implements BulkOperations {
|
||||
} else {
|
||||
models.add(new UpdateOneModel<>(query.getQueryObject(), update.getUpdateObject(), options));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private final BulkWriteOptions initBulkOperation() {
|
||||
private WriteModel<Document> mapWriteModel(WriteModel<Document> writeModel) {
|
||||
|
||||
if (writeModel instanceof UpdateOneModel) {
|
||||
|
||||
UpdateOneModel<Document> model = (UpdateOneModel<Document>) writeModel;
|
||||
|
||||
return new UpdateOneModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
|
||||
model.getOptions());
|
||||
}
|
||||
|
||||
if (writeModel instanceof UpdateManyModel) {
|
||||
|
||||
UpdateManyModel<Document> model = (UpdateManyModel<Document>) writeModel;
|
||||
|
||||
return new UpdateManyModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
|
||||
model.getOptions());
|
||||
}
|
||||
|
||||
if (writeModel instanceof DeleteOneModel) {
|
||||
|
||||
DeleteOneModel<Document> model = (DeleteOneModel<Document>) writeModel;
|
||||
|
||||
return new DeleteOneModel<>(getMappedQuery(model.getFilter()), model.getOptions());
|
||||
}
|
||||
|
||||
if (writeModel instanceof DeleteManyModel) {
|
||||
|
||||
DeleteManyModel<Document> model = (DeleteManyModel<Document>) writeModel;
|
||||
|
||||
return new DeleteManyModel<>(getMappedQuery(model.getFilter()), model.getOptions());
|
||||
}
|
||||
|
||||
return writeModel;
|
||||
}
|
||||
|
||||
private Bson getMappedUpdate(Bson update) {
|
||||
return bulkOperationContext.getUpdateMapper().getMappedObject(update, bulkOperationContext.getEntity());
|
||||
}
|
||||
|
||||
private Bson getMappedQuery(Bson query) {
|
||||
return bulkOperationContext.getQueryMapper().getMappedObject(query, bulkOperationContext.getEntity());
|
||||
}
|
||||
|
||||
private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
|
||||
|
||||
BulkWriteOptions options = new BulkWriteOptions();
|
||||
|
||||
switch (bulkMode) {
|
||||
case ORDERED:
|
||||
return options.ordered(true);
|
||||
case UNORDERED:
|
||||
return options.ordered(false);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("BulkMode was null!");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BulkOperationContext} holds information about
|
||||
* {@link org.springframework.data.mongodb.core.BulkOperations.BulkMode} the entity in use as well as references to
|
||||
* {@link QueryMapper} and {@link UpdateMapper}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@Value
|
||||
static class BulkOperationContext {
|
||||
|
||||
@NonNull BulkMode bulkMode;
|
||||
@NonNull Optional<? extends MongoPersistentEntity<?>> entity;
|
||||
@NonNull QueryMapper queryMapper;
|
||||
@NonNull UpdateMapper updateMapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,11 @@ import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -37,7 +38,7 @@ import com.mongodb.client.model.IndexOptions;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link IndexOperations}.
|
||||
*
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Komi Innocent
|
||||
@@ -55,7 +56,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultIndexOperations}.
|
||||
*
|
||||
*
|
||||
* @param mongoDbFactory must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param queryMapper must not be {@literal null}.
|
||||
@@ -89,7 +90,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
*/
|
||||
public String ensureIndex(final IndexDefinition indexDefinition) {
|
||||
|
||||
@@ -97,24 +98,22 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
Document indexOptions = indexDefinition.getIndexOptions();
|
||||
|
||||
if (indexOptions != null) {
|
||||
|
||||
IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
|
||||
|
||||
if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
|
||||
|
||||
Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
|
||||
|
||||
ops.partialFilterExpression( mapper.getMappedObject(
|
||||
(Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), lookupPersistentEntity(type, collectionName)));
|
||||
}
|
||||
|
||||
return collection.createIndex(indexDefinition.getIndexKeys(), ops);
|
||||
if (indexOptions == null) {
|
||||
return collection.createIndex(indexDefinition.getIndexKeys());
|
||||
}
|
||||
return collection.createIndex(indexDefinition.getIndexKeys());
|
||||
}
|
||||
|
||||
);
|
||||
IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
|
||||
|
||||
if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
|
||||
|
||||
Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
|
||||
|
||||
ops.partialFilterExpression(mapper.getMappedObject((Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
|
||||
lookupPersistentEntity(type, collectionName)));
|
||||
}
|
||||
|
||||
return collection.createIndex(indexDefinition.getIndexKeys(), ops);
|
||||
});
|
||||
}
|
||||
|
||||
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
|
||||
@@ -136,7 +135,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#dropIndex(java.lang.String)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexOperations#dropIndex(java.lang.String)
|
||||
*/
|
||||
public void dropIndex(final String name) {
|
||||
|
||||
@@ -149,7 +148,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#dropAllIndexes()
|
||||
* @see org.springframework.data.mongodb.core.index.IndexOperations#dropAllIndexes()
|
||||
*/
|
||||
public void dropAllIndexes() {
|
||||
dropIndex("*");
|
||||
@@ -157,7 +156,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#getIndexInfo()
|
||||
* @see org.springframework.data.mongodb.core.index.IndexOperations#getIndexInfo()
|
||||
*/
|
||||
public List<IndexInfo> getIndexInfo() {
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
|
||||
|
||||
/**
|
||||
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDbFactory}. TODO: Review
|
||||
@@ -38,7 +40,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
|
||||
* @see org.springframework.data.mongodb.core.index.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public IndexOperations indexOps(String collectionName) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,88 +15,138 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.reactivestreams.client.ListIndexesPublisher;
|
||||
|
||||
import org.springframework.data.mongodb.core.index.ReactiveIndexOperations;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.client.model.IndexOptions;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link IndexOperations}.
|
||||
* Default implementation of {@link ReactiveIndexOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.11
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
|
||||
|
||||
private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
|
||||
|
||||
private final ReactiveMongoOperations mongoOperations;
|
||||
private final String collectionName;
|
||||
private final QueryMapper queryMapper;
|
||||
private final Optional<Class<?>> type;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultReactiveIndexOperations}.
|
||||
*
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param queryMapper must not be {@literal null}.
|
||||
*/
|
||||
public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName) {
|
||||
public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
|
||||
QueryMapper queryMapper) {
|
||||
this(mongoOperations, collectionName, queryMapper, Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultReactiveIndexOperations}.
|
||||
*
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param queryMapper must not be {@literal null}.
|
||||
* @param type used for mapping potential partial index filter expression, must not be {@literal null}.
|
||||
*/
|
||||
public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
|
||||
QueryMapper queryMapper, Class<?> type) {
|
||||
this(mongoOperations, collectionName, queryMapper, Optional.of(type));
|
||||
}
|
||||
|
||||
private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
|
||||
QueryMapper queryMapper, Optional<Class<?>> type) {
|
||||
|
||||
Assert.notNull(mongoOperations, "ReactiveMongoOperations must not be null!");
|
||||
Assert.notNull(collectionName, "Collection must not be null!");
|
||||
Assert.notNull(queryMapper, "QueryMapper must not be null!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
this.collectionName = collectionName;
|
||||
this.queryMapper = queryMapper;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveIndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
*/
|
||||
public Mono<String> ensureIndex(final IndexDefinition indexDefinition) {
|
||||
|
||||
return mongoOperations.execute(collectionName, (ReactiveCollectionCallback<String>) collection -> {
|
||||
return mongoOperations.execute(collectionName, collection -> {
|
||||
|
||||
Document indexOptions = indexDefinition.getIndexOptions();
|
||||
|
||||
if (indexOptions != null) {
|
||||
return collection.createIndex(indexDefinition.getIndexKeys(),
|
||||
IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition));
|
||||
if (indexOptions == null) {
|
||||
return collection.createIndex(indexDefinition.getIndexKeys());
|
||||
}
|
||||
|
||||
return collection.createIndex(indexDefinition.getIndexKeys());
|
||||
IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
|
||||
|
||||
if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
|
||||
|
||||
Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
|
||||
|
||||
MongoPersistentEntity<?> entity = type
|
||||
.map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val))
|
||||
.orElseGet(() -> lookupPersistentEntity(collectionName));
|
||||
|
||||
ops = ops.partialFilterExpression(
|
||||
queryMapper.getMappedObject(indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY, Document.class), entity));
|
||||
}
|
||||
|
||||
return collection.createIndex(indexDefinition.getIndexKeys(), ops);
|
||||
|
||||
}).next();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveIndexOperations#dropIndex(java.lang.String)
|
||||
*/
|
||||
public Mono<Void> dropIndex(final String name) {
|
||||
private MongoPersistentEntity<?> lookupPersistentEntity(String collection) {
|
||||
|
||||
return mongoOperations.execute(collectionName, collection -> {
|
||||
Collection<? extends MongoPersistentEntity<?>> entities = queryMapper.getMappingContext().getPersistentEntities();
|
||||
|
||||
return Mono.from(collection.dropIndex(name));
|
||||
}).flatMap(success -> Mono.<Void>empty()).next();
|
||||
return entities.stream() //
|
||||
.filter(entity -> entity.getCollection().equals(collection)) //
|
||||
.findFirst() //
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveIndexOperations#dropAllIndexes()
|
||||
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#dropIndex(java.lang.String)
|
||||
*/
|
||||
public Mono<Void> dropIndex(final String name) {
|
||||
return mongoOperations.execute(collectionName, collection -> collection.dropIndex(name)).then();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#dropAllIndexes()
|
||||
*/
|
||||
public Mono<Void> dropAllIndexes() {
|
||||
return dropIndex("*");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveIndexOperations#getIndexInfo()
|
||||
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#getIndexInfo()
|
||||
*/
|
||||
public Flux<IndexInfo> getIndexInfo() {
|
||||
|
||||
return mongoOperations.execute(collectionName, collection -> {
|
||||
|
||||
ListIndexesPublisher<Document> indexesPublisher = collection.listIndexes(Document.class);
|
||||
|
||||
return Flux.from(indexesPublisher).map(IndexConverters.documentToIndexInfoConverter()::convert);
|
||||
});
|
||||
return mongoOperations.execute(collectionName, collection -> collection.listIndexes(Document.class)) //
|
||||
.map(IndexConverters.documentToIndexInfoConverter()::convert);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
|
||||
/**
|
||||
* {@link ExecutableAggregationOperation} allows creation and execution of MongoDB aggregation operations in a fluent
|
||||
* API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Aggregation} provided via {@code by} into the
|
||||
* MongoDB specific representation, as well as mapping back the resulting {@link org.bson.Document}. An alternative
|
||||
* input type for mapping the {@link Aggregation} can be provided by using
|
||||
* {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation}.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* aggregateAndReturn(Jedi.class)
|
||||
* .by(newAggregation(Human.class, project("These are not the droids you are looking for")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableAggregationOperation {
|
||||
|
||||
/**
|
||||
* Start creating an aggregation operation that returns results mapped to the given domain type. <br />
|
||||
* Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to specify a potentially different
|
||||
* input type for he aggregation.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableAggregation}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ExecutableAggregation<T> aggregateAndReturn(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Collection override (Optional).
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface AggregationWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link AggregationWithAggregation}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
AggregationWithAggregation<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingAggregation<T> {
|
||||
|
||||
/**
|
||||
* Apply pipeline operations as specified and get all matching elements.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
AggregationResults<T> all();
|
||||
|
||||
/**
|
||||
* Apply pipeline operations as specified and stream all matching elements. <br />
|
||||
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor}
|
||||
*
|
||||
* @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed.
|
||||
* Never {@literal null}.
|
||||
*/
|
||||
CloseableIterator<T> stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the aggregation with pipeline stages.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface AggregationWithAggregation<T> {
|
||||
|
||||
/**
|
||||
* Set the aggregation to be used.
|
||||
*
|
||||
* @param aggregation must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingAggregation}.
|
||||
* @throws IllegalArgumentException if aggregation is {@literal null}.
|
||||
*/
|
||||
TerminatingAggregation<T> by(Aggregation aggregation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExecutableAggregation<T> extends AggregationWithCollection<T>, AggregationWithAggregation<T> {}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutableAggregationOperation} operating directly on {@link MongoTemplate}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class ExecutableAggregationOperationSupport implements ExecutableAggregationOperation {
|
||||
|
||||
private final MongoTemplate template;
|
||||
|
||||
/**
|
||||
* Create new instance of {@link ExecutableAggregationOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if template is {@literal null}.
|
||||
*/
|
||||
ExecutableAggregationOperationSupport(MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation#aggregateAndReturn(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableAggregation<T> aggregateAndReturn(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ExecutableAggregationSupport<>(template, domainType, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ExecutableAggregationSupport<T>
|
||||
implements AggregationWithAggregation<T>, ExecutableAggregation<T>, TerminatingAggregation<T> {
|
||||
|
||||
@NonNull MongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Aggregation aggregation;
|
||||
String collection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation.AggregationWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public AggregationWithAggregation<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ExecutableAggregationSupport<>(template, domainType, aggregation, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation.AggregationWithAggregation#by(org.springframework.data.mongodb.core.aggregation.Aggregation)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingAggregation<T> by(Aggregation aggregation) {
|
||||
|
||||
Assert.notNull(aggregation, "Aggregation must not be null!");
|
||||
|
||||
return new ExecutableAggregationSupport<>(template, domainType, aggregation, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation.TerminatingAggregation#all()
|
||||
*/
|
||||
@Override
|
||||
public AggregationResults<T> all() {
|
||||
return template.aggregate(aggregation, getCollectionName(aggregation), domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation.TerminatingAggregation#stream()
|
||||
*/
|
||||
@Override
|
||||
public CloseableIterator<T> stream() {
|
||||
return template.aggregateStream(aggregation, getCollectionName(aggregation), domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName(Aggregation aggregation) {
|
||||
|
||||
if (StringUtils.hasText(collection)) {
|
||||
return collection;
|
||||
}
|
||||
|
||||
if (aggregation instanceof TypedAggregation) {
|
||||
|
||||
TypedAggregation<?> typedAggregation = (TypedAggregation<?>) aggregation;
|
||||
|
||||
if (typedAggregation.getInputType() != null) {
|
||||
return template.determineCollectionName(typedAggregation.getInputType());
|
||||
}
|
||||
}
|
||||
|
||||
return template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
/**
|
||||
* {@link ExecutableFindOperation} allows creation and execution of MongoDB find operations in a fluent API style.
|
||||
* <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
|
||||
* MongoDB specific representation. By default, the originating {@literal domainType} is also used for mapping back the
|
||||
* result from the {@link org.bson.Document}. However, it is possible to define an different {@literal returnType} via
|
||||
* {@code as} to mapping the result.<br />
|
||||
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
|
||||
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
|
||||
* collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* query(Human.class)
|
||||
* .inCollection("star-wars")
|
||||
* .as(Jedi.class)
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableFindOperation {
|
||||
|
||||
/**
|
||||
* Start creating a find operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableFind}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ExecutableFind<T> query(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Trigger find execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingFind<T> {
|
||||
|
||||
/**
|
||||
* Get exactly zero or one result.
|
||||
*
|
||||
* @return {@link Optional#empty()} if no match found.
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
default Optional<T> one() {
|
||||
return Optional.ofNullable(oneValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exactly zero or one result.
|
||||
*
|
||||
* @return {@literal null} if no match found.
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
T oneValue();
|
||||
|
||||
/**
|
||||
* Get the first or no result.
|
||||
*
|
||||
* @return {@link Optional#empty()} if no match found.
|
||||
*/
|
||||
default Optional<T> first() {
|
||||
return Optional.ofNullable(firstValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first or no result.
|
||||
*
|
||||
* @return {@literal null} if no match found.
|
||||
*/
|
||||
T firstValue();
|
||||
|
||||
/**
|
||||
* Get all matching elements.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
List<T> all();
|
||||
|
||||
/**
|
||||
* Stream all matching elements.
|
||||
*
|
||||
* @return a {@link Stream} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed. Never
|
||||
* {@literal null}.
|
||||
*/
|
||||
Stream<T> stream();
|
||||
|
||||
/**
|
||||
* Get the number of matching elements.
|
||||
*
|
||||
* @return total number of matching elements.
|
||||
*/
|
||||
long count();
|
||||
|
||||
/**
|
||||
* Check for the presence of matching elements.
|
||||
*
|
||||
* @return {@literal true} if at least one matching element exists.
|
||||
*/
|
||||
boolean exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger geonear execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingFindNear<T> {
|
||||
|
||||
/**
|
||||
* Find all matching elements and return them as {@link org.springframework.data.geo.GeoResult}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
GeoResults<T> all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual query execution.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface FindWithQuery<T> extends TerminatingFind<T> {
|
||||
|
||||
/**
|
||||
* Set the filter query to be used.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFind}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
TerminatingFind<T> matching(Query query);
|
||||
|
||||
/**
|
||||
* Set the filter query for the geoNear execution.
|
||||
*
|
||||
* @param nearQuery must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFindNear}.
|
||||
* @throws IllegalArgumentException if nearQuery is {@literal null}.
|
||||
*/
|
||||
TerminatingFindNear<T> near(NearQuery nearQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (Optional).
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface FindWithCollection<T> extends FindWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link FindWithProjection}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
FindWithProjection<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type override (Optional).
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface FindWithProjection<T> extends FindWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return new instance of {@link FindWithProjection}.
|
||||
* @throws IllegalArgumentException if resultType is {@literal null}.
|
||||
*/
|
||||
<R> FindWithQuery<R> as(Class<R> resultType);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExecutableFind} provides methods for constructing lookup operations in a fluent way.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExecutableFind<T> extends FindWithCollection<T>, FindWithProjection<T> {}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutableFindOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class ExecutableFindOperationSupport implements ExecutableFindOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final MongoTemplate template;
|
||||
|
||||
/**
|
||||
* Create new {@link ExecutableFindOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if template is {@literal null}.
|
||||
*/
|
||||
ExecutableFindOperationSupport(MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation#query(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableFind<T> query(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ExecutableFindSupport<>(template, domainType, domainType, null, ALL_QUERY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ExecutableFindSupport<T>
|
||||
implements ExecutableFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
|
||||
|
||||
@NonNull MongoTemplate template;
|
||||
@NonNull Class<?> domainType;
|
||||
Class<T> returnType;
|
||||
String collection;
|
||||
Query query;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public FindWithProjection<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection name must not be null nor empty!");
|
||||
|
||||
return new ExecutableFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithProjection#as(Class)
|
||||
*/
|
||||
@Override
|
||||
public <T1> FindWithQuery<T1> as(Class<T1> returnType) {
|
||||
|
||||
Assert.notNull(returnType, "ReturnType must not be null!");
|
||||
|
||||
return new ExecutableFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery#matching(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFind<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ExecutableFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#oneValue()
|
||||
*/
|
||||
@Override
|
||||
public T oneValue() {
|
||||
|
||||
List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(2));
|
||||
|
||||
if (ObjectUtils.isEmpty(result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.size() > 1) {
|
||||
throw new IncorrectResultSizeDataAccessException("Query " + asString() + " returned non unique result.", 1);
|
||||
}
|
||||
|
||||
return result.iterator().next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#firstValue()
|
||||
*/
|
||||
@Override
|
||||
public T firstValue() {
|
||||
|
||||
List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(1));
|
||||
|
||||
return ObjectUtils.isEmpty(result) ? null : result.iterator().next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#all()
|
||||
*/
|
||||
@Override
|
||||
public List<T> all() {
|
||||
return doFind(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#stream()
|
||||
*/
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return StreamUtils.createStreamFromIterator(doStream());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery#near(org.springframework.data.mongodb.core.query.NearQuery)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFindNear<T> near(NearQuery nearQuery) {
|
||||
return () -> template.geoNear(nearQuery, domainType, getCollectionName(), returnType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#count()
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return template.count(query, domainType, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind#exists()
|
||||
*/
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return template.exists(query, domainType, getCollectionName());
|
||||
}
|
||||
|
||||
private List<T> doFind(CursorPreparer preparer) {
|
||||
|
||||
Document queryObject = query.getQueryObject();
|
||||
Document fieldsObject = query.getFieldsObject();
|
||||
|
||||
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||
getCursorPreparer(query, preparer));
|
||||
}
|
||||
|
||||
private CloseableIterator<T> doStream() {
|
||||
return template.doStream(query, domainType, getCollectionName(), returnType);
|
||||
}
|
||||
|
||||
private CursorPreparer getCursorPreparer(Query query, CursorPreparer preparer) {
|
||||
return query == null || preparer != null ? preparer : template.new QueryCursorPreparer(query, domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
|
||||
private String asString() {
|
||||
return SerializationUtils.serializeToJsonSafely(query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
static class DelegatingQueryCursorPreparer implements CursorPreparer {
|
||||
|
||||
private final CursorPreparer delegate;
|
||||
private Optional<Integer> limit = Optional.empty();
|
||||
|
||||
DelegatingQueryCursorPreparer(CursorPreparer delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.CursorPreparer#prepare(com.mongodb.clientFindIterable)
|
||||
*/
|
||||
@Override
|
||||
public FindIterable<Document> prepare(FindIterable<Document> cursor) {
|
||||
|
||||
FindIterable<Document> target = delegate != null ? delegate.prepare(cursor) : cursor;
|
||||
return limit.map(target::limit).orElse(target);
|
||||
}
|
||||
|
||||
CursorPreparer limit(int limit) {
|
||||
|
||||
this.limit = Optional.of(limit);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
|
||||
import com.mongodb.bulk.BulkWriteResult;
|
||||
|
||||
/**
|
||||
* {@link ExecutableInsertOperation} allows creation and execution of MongoDB insert and bulk insert operations in a
|
||||
* fluent API style. <br />
|
||||
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
|
||||
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
|
||||
* collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* insert(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .one(luke);
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableInsertOperation {
|
||||
|
||||
/**
|
||||
* Start creating an insert operation for given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableInsert}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ExecutableInsert<T> insert(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Trigger insert execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingInsert<T> extends TerminatingBulkInsert<T> {
|
||||
|
||||
/**
|
||||
* Insert exactly one object.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if object is {@literal null}.
|
||||
*/
|
||||
void one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of objects.
|
||||
*
|
||||
* @param objects must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if objects is {@literal null}.
|
||||
*/
|
||||
void all(Collection<? extends T> objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger bulk insert execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingBulkInsert<T> {
|
||||
|
||||
/**
|
||||
* Bulk write collection of objects.
|
||||
*
|
||||
* @param objects must not be {@literal null}.
|
||||
* @return resulting {@link BulkWriteResult}.
|
||||
* @throws IllegalArgumentException if objects is {@literal null}.
|
||||
*/
|
||||
BulkWriteResult bulk(Collection<? extends T> objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface InsertWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link InsertWithBulkMode}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
InsertWithBulkMode<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface InsertWithBulkMode<T> extends TerminatingInsert<T> {
|
||||
|
||||
/**
|
||||
* Define the {@link BulkMode} to use for bulk insert operation.
|
||||
*
|
||||
* @param bulkMode must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingBulkInsert}.
|
||||
* @throws IllegalArgumentException if bulkMode is {@literal null}.
|
||||
*/
|
||||
TerminatingBulkInsert<T> withBulkMode(BulkMode bulkMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExecutableInsert<T> extends TerminatingInsert<T>, InsertWithCollection<T>, InsertWithBulkMode<T> {}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.bulk.BulkWriteResult;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutableInsertOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
|
||||
|
||||
private final MongoTemplate template;
|
||||
|
||||
/**
|
||||
* Create new {@link ExecutableInsertOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if template is {@literal null}.
|
||||
*/
|
||||
ExecutableInsertOperationSupport(MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.coreExecutableInsertOperation#insert(java.lan.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableInsert<T> insert(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ExecutableInsertSupport<>(template, domainType, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ExecutableInsertSupport<T> implements ExecutableInsert<T> {
|
||||
|
||||
@NonNull MongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
String collection;
|
||||
BulkMode bulkMode;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation.TerminatingInsert#insert(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public void one(T object) {
|
||||
|
||||
Assert.notNull(object, "Object must not be null!");
|
||||
|
||||
template.insert(object, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation.TerminatingInsert#all(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public void all(Collection<? extends T> objects) {
|
||||
|
||||
Assert.notNull(objects, "Objects must not be null!");
|
||||
|
||||
template.insert(objects, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation.TerminatingBulkInsert#bulk(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public BulkWriteResult bulk(Collection<? extends T> objects) {
|
||||
|
||||
Assert.notNull(objects, "Objects must not be null!");
|
||||
|
||||
return template.bulkOps(bulkMode != null ? bulkMode : BulkMode.ORDERED, domainType, getCollectionName())
|
||||
.insert(new ArrayList<>(objects)).execute();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation.InsertWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public InsertWithBulkMode<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
|
||||
return new ExecutableInsertSupport<>(template, domainType, collection, bulkMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation.InsertWithBulkMode#withBulkMode(org.springframework.data.mongodb.core.BulkMode)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingBulkInsert<T> withBulkMode(BulkMode bulkMode) {
|
||||
|
||||
Assert.notNull(bulkMode, "BulkMode must not be null!");
|
||||
|
||||
return new ExecutableInsertSupport<>(template, domainType, collection, bulkMode);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
|
||||
/**
|
||||
* {@link ExecutableRemoveOperation} allows creation and execution of MongoDB remove / findAndRemove operations in a
|
||||
* fluent API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
|
||||
* MongoDB specific representation. The collection to operate on is by default derived from the initial
|
||||
* {@literal domainType} and can be defined there via {@link org.springframework.data.mongodb.core.mapping.Document}.
|
||||
* Using {@code inCollection} allows to override the collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* remove(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableRemoveOperation {
|
||||
|
||||
/**
|
||||
* Start creating a remove operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableRemove}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ExecutableRemove<T> remove(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*
|
||||
* @param <T>
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface RemoveWithCollection<T> extends RemoveWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link RemoveWithCollection}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
RemoveWithQuery<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingRemove<T> {
|
||||
|
||||
/**
|
||||
* Remove all documents matching.
|
||||
*
|
||||
* @return the {@link DeleteResult}. Never {@literal null}.
|
||||
*/
|
||||
DeleteResult all();
|
||||
|
||||
/**
|
||||
* Remove and return all matching documents. <br/>
|
||||
* <strong>NOTE</strong> The entire list of documents will be fetched before sending the actual delete commands.
|
||||
* Also, {@link org.springframework.context.ApplicationEvent}s will be published for each and every delete
|
||||
* operation.
|
||||
*
|
||||
* @return empty {@link List} if no match found. Never {@literal null}.
|
||||
*/
|
||||
List<T> findAndRemove();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface RemoveWithQuery<T> extends TerminatingRemove<T> {
|
||||
|
||||
/**
|
||||
* Define the query filtering elements.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingRemove}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
TerminatingRemove<T> matching(Query query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExecutableRemove<T> extends RemoveWithCollection<T> {}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutableRemoveOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final MongoTemplate tempate;
|
||||
|
||||
/**
|
||||
* Create new {@link ExecutableRemoveOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if template is {@literal null}.
|
||||
*/
|
||||
ExecutableRemoveOperationSupport(MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.tempate = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation#remove(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableRemove<T> remove(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ExecutableRemoveSupport<>(tempate, domainType, ALL_QUERY, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ExecutableRemoveSupport<T> implements ExecutableRemove<T>, RemoveWithCollection<T> {
|
||||
|
||||
@NonNull MongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Query query;
|
||||
String collection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation.RemoveWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RemoveWithQuery<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ExecutableRemoveSupport<>(template, domainType, query, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation.RemoveWithQuery#matching(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingRemove<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ExecutableRemoveSupport<>(template, domainType, query, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation.TerminatingRemove#all()
|
||||
*/
|
||||
@Override
|
||||
public DeleteResult all() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.doRemove(collectionName, query, domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation.TerminatingRemove#findAndRemove()
|
||||
*/
|
||||
@Override
|
||||
public List<T> findAndRemove() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.doFindAndDelete(collectionName, query, domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
/**
|
||||
* {@link ExecutableUpdateOperation} allows creation and execution of MongoDB update / findAndModify operations in a
|
||||
* fluent API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching}, as well as
|
||||
* the {@link Update} via {@code apply} into the MongoDB specific representations. The collection to operate on is by
|
||||
* default derived from the initial {@literal domainType} and can be defined there via
|
||||
* {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
|
||||
* collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* update(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .apply(new Update().set("lastname", "skywalker"))
|
||||
* .upsert();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableUpdateOperation {
|
||||
|
||||
/**
|
||||
* Start creating an update operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableUpdate}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ExecutableUpdate<T> update(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Declare the {@link Update} to apply.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface UpdateWithUpdate<T> {
|
||||
|
||||
/**
|
||||
* Set the {@link Update} to be applied.
|
||||
*
|
||||
* @param update must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingUpdate}.
|
||||
* @throws IllegalArgumentException if update is {@literal null}.
|
||||
*/
|
||||
TerminatingUpdate<T> apply(Update update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly define the name of the collection to perform operation in.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface UpdateWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link UpdateWithCollection}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
UpdateWithQuery<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a filter query for the {@link Update}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface UpdateWithQuery<T> extends UpdateWithUpdate<T> {
|
||||
|
||||
/**
|
||||
* Filter documents by given {@literal query}.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link UpdateWithQuery}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
UpdateWithUpdate<T> matching(Query query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define {@link FindAndModifyOptions}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface FindAndModifyWithOptions<T> {
|
||||
|
||||
/**
|
||||
* Explicitly define {@link FindAndModifyOptions} for the {@link Update}.
|
||||
*
|
||||
* @param options must not be {@literal null}.
|
||||
* @return new instance of {@link FindAndModifyWithOptions}.
|
||||
* @throws IllegalArgumentException if options is {@literal null}.
|
||||
*/
|
||||
TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger findAndModify execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingFindAndModify<T> {
|
||||
|
||||
/**
|
||||
* Find, modify and return the first matching document.
|
||||
*
|
||||
* @return {@link Optional#empty()} if nothing found.
|
||||
*/
|
||||
default Optional<T> findAndModify() {
|
||||
return Optional.ofNullable(findAndModifyValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find, modify and return the first matching document.
|
||||
*
|
||||
* @return {@literal null} if nothing found.
|
||||
*/
|
||||
T findAndModifyValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger update execution by calling one of the terminating methods.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface TerminatingUpdate<T> extends TerminatingFindAndModify<T>, FindAndModifyWithOptions<T> {
|
||||
|
||||
/**
|
||||
* Update all matching documents in the collection.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
UpdateResult all();
|
||||
|
||||
/**
|
||||
* Update the first document in the collection.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
UpdateResult first();
|
||||
|
||||
/**
|
||||
* Creates a new document if no documents match the filter query or updates the matching ones.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
UpdateResult upsert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExecutableUpdate<T> extends UpdateWithCollection<T>, UpdateWithQuery<T>, UpdateWithUpdate<T> {}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ExecutableUpdateOperation}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final MongoTemplate template;
|
||||
|
||||
/**
|
||||
* Creates new {@link ExecutableUpdateOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
*/
|
||||
ExecutableUpdateOperationSupport(MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation#update(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableUpdate<T> update(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ExecutableUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ExecutableUpdateSupport<T>
|
||||
implements ExecutableUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T> {
|
||||
|
||||
@NonNull MongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Query query;
|
||||
Update update;
|
||||
String collection;
|
||||
FindAndModifyOptions options;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithUpdate#apply(Update)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingUpdate<T> apply(Update update) {
|
||||
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return new ExecutableUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public UpdateWithQuery<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ExecutableUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.FindAndModifyWithOptions#withOptions(org.springframework.data.mongodb.core.FindAndModifyOptions)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) {
|
||||
|
||||
Assert.notNull(options, "Options must not be null!");
|
||||
|
||||
return new ExecutableUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithQuery#matching(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public UpdateWithUpdate<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ExecutableUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.TerminatingUpdate#all()
|
||||
*/
|
||||
@Override
|
||||
public UpdateResult all() {
|
||||
return doUpdate(true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.TerminatingUpdate#first()
|
||||
*/
|
||||
@Override
|
||||
public UpdateResult first() {
|
||||
return doUpdate(false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.TerminatingUpdate#upsert()
|
||||
*/
|
||||
@Override
|
||||
public UpdateResult upsert() {
|
||||
return doUpdate(true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.TerminatingFindAndModify#findAndModifyValue()
|
||||
*/
|
||||
@Override
|
||||
public T findAndModifyValue() {
|
||||
return template.findAndModify(query, update, options, domainType, getCollectionName());
|
||||
}
|
||||
|
||||
private UpdateResult doUpdate(boolean multi, boolean upsert) {
|
||||
return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
|
||||
/**
|
||||
* @author Mark Pollak
|
||||
* @author Oliver Gierke
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.reactivestreams.client.FindPublisher;
|
||||
|
||||
/**
|
||||
@@ -28,7 +27,7 @@ interface FindPublisherPreparer {
|
||||
/**
|
||||
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
|
||||
*
|
||||
* @param cursor
|
||||
* @param findPublisher must not be {@literal null}.
|
||||
*/
|
||||
<T> FindPublisher<T> prepare(FindPublisher<T> findPublisher);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
/**
|
||||
* Stripped down interface providing access to a fluent API that specifies a basic set of MongoDB operations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface FluentMongoOperations extends ExecutableFindOperation, ExecutableInsertOperation,
|
||||
ExecutableUpdateOperation, ExecutableRemoveOperation, ExecutableAggregationOperation {}
|
||||
@@ -125,7 +125,7 @@ abstract class IndexConverters {
|
||||
return null;
|
||||
}
|
||||
|
||||
return org.springframework.data.mongodb.core.Collation.from(source).toMongoCollation();
|
||||
return org.springframework.data.mongodb.core.query.Collation.from(source).toMongoCollation();
|
||||
}
|
||||
|
||||
private static Converter<Document, IndexInfo> getDocumentIndexInfoConverter() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
|
||||
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
|
||||
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
||||
@@ -58,7 +59,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Thomas Darimont
|
||||
* @author Maninder Singh
|
||||
*/
|
||||
public interface MongoOperations {
|
||||
public interface MongoOperations extends FluentMongoOperations {
|
||||
|
||||
/**
|
||||
* The collection name used for the specified class by this template.
|
||||
@@ -282,7 +283,10 @@ public interface MongoOperations {
|
||||
ScriptOperations scriptOps();
|
||||
|
||||
/**
|
||||
* Returns a new {@link BulkOperations} for the given collection.
|
||||
* Returns a new {@link BulkOperations} for the given collection. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, etc. is not available for {@literal update} or
|
||||
* {@literal remove} operations in bulk mode due to the lack of domain type information. Use
|
||||
* {@link #bulkOps(BulkMode, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param mode the {@link BulkMode} to use for bulk operations, must not be {@literal null}.
|
||||
* @param collectionName the name of the collection to work on, must not be {@literal null} or empty.
|
||||
@@ -318,7 +322,7 @@ public interface MongoOperations {
|
||||
* If your collection does not contain a homogeneous collection of types, this operation will not be an efficient way
|
||||
* to map objects since the test for class type is done in the client and not on the server.
|
||||
*
|
||||
* @param entityClass the parameterized type of the returned list
|
||||
* @param entityClass the parametrized type of the returned list
|
||||
* @return the converted collection
|
||||
*/
|
||||
<T> List<T> findAll(Class<T> entityClass);
|
||||
@@ -332,7 +336,7 @@ public interface MongoOperations {
|
||||
* If your collection does not contain a homogeneous collection of types, this operation will not be an efficient way
|
||||
* to map objects since the test for class type is done in the client and not on the server.
|
||||
*
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @return the converted collection
|
||||
*/
|
||||
@@ -347,7 +351,7 @@ public interface MongoOperations {
|
||||
* @param inputCollectionName the collection where the group operation will read from
|
||||
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
|
||||
* reduce function.
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the group operation
|
||||
*/
|
||||
<T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
|
||||
@@ -362,7 +366,7 @@ public interface MongoOperations {
|
||||
* @param inputCollectionName the collection where the group operation will read from
|
||||
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
|
||||
* reduce function.
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the group operation
|
||||
*/
|
||||
<T> GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
|
||||
@@ -374,7 +378,7 @@ public interface MongoOperations {
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
|
||||
* {@literal null}.
|
||||
* @param collectionName The name of the input collection to use for the aggreation.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 1.3
|
||||
*/
|
||||
@@ -386,7 +390,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
|
||||
* {@literal null}.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 1.3
|
||||
*/
|
||||
@@ -399,7 +403,7 @@ public interface MongoOperations {
|
||||
* {@literal null}.
|
||||
* @param inputType the inputType where the aggregation operation will read from, must not be {@literal null} or
|
||||
* empty.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 1.3
|
||||
*/
|
||||
@@ -412,7 +416,7 @@ public interface MongoOperations {
|
||||
* {@literal null}.
|
||||
* @param collectionName the collection where the aggregation operation will read from, must not be {@literal null} or
|
||||
* empty.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 1.3
|
||||
*/
|
||||
@@ -431,7 +435,7 @@ public interface MongoOperations {
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
|
||||
* {@literal null}.
|
||||
* @param collectionName The name of the input collection to use for the aggreation.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 2.0
|
||||
*/
|
||||
@@ -449,7 +453,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations, must not be
|
||||
* {@literal null}.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 2.0
|
||||
*/
|
||||
@@ -468,7 +472,7 @@ public interface MongoOperations {
|
||||
* {@literal null}.
|
||||
* @param inputType the inputType where the aggregation operation will read from, must not be {@literal null} or
|
||||
* empty.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 2.0
|
||||
*/
|
||||
@@ -487,7 +491,7 @@ public interface MongoOperations {
|
||||
* {@literal null}.
|
||||
* @param collectionName the collection where the aggregation operation will read from, must not be {@literal null} or
|
||||
* empty.
|
||||
* @param outputType The parameterized type of the returned list, must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned list, must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @since 2.0
|
||||
*/
|
||||
@@ -500,7 +504,7 @@ public interface MongoOperations {
|
||||
* @param mapFunction The JavaScript map function
|
||||
* @param reduceFunction The JavaScript reduce function
|
||||
* @param mapReduceOptions Options that specify detailed map-reduce behavior
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the map reduce operation
|
||||
*/
|
||||
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
|
||||
@@ -513,7 +517,7 @@ public interface MongoOperations {
|
||||
* @param mapFunction The JavaScript map function
|
||||
* @param reduceFunction The JavaScript reduce function
|
||||
* @param mapReduceOptions Options that specify detailed map-reduce behavior
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the map reduce operation
|
||||
*/
|
||||
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
|
||||
@@ -528,7 +532,7 @@ public interface MongoOperations {
|
||||
* @param mapFunction The JavaScript map function
|
||||
* @param reduceFunction The JavaScript reduce function
|
||||
* @param mapReduceOptions Options that specify detailed map-reduce behavior
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the map reduce operation
|
||||
*/
|
||||
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
|
||||
@@ -542,7 +546,7 @@ public interface MongoOperations {
|
||||
* @param mapFunction The JavaScript map function
|
||||
* @param reduceFunction The JavaScript reduce function
|
||||
* @param mapReduceOptions Options that specify detailed map-reduce behavior
|
||||
* @param entityClass The parameterized type of the returned list
|
||||
* @param entityClass The parametrized type of the returned list
|
||||
* @return The results of the map reduce operation
|
||||
*/
|
||||
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
|
||||
@@ -585,7 +589,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @return the converted object
|
||||
*/
|
||||
<T> T findOne(Query query, Class<T> entityClass);
|
||||
@@ -602,14 +606,16 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @return the converted object
|
||||
*/
|
||||
<T> T findOne(Query query, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
* Determine result of given {@link Query} contains at least one element. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for query/field mapping, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #exists(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param collectionName name of the collection to check for objects.
|
||||
@@ -621,7 +627,7 @@ public interface MongoOperations {
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @return
|
||||
*/
|
||||
boolean exists(Query query, Class<?> entityClass);
|
||||
@@ -630,7 +636,7 @@ public interface MongoOperations {
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @param collectionName name of the collection to check for objects.
|
||||
* @return
|
||||
*/
|
||||
@@ -647,7 +653,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @return the List of converted objects
|
||||
*/
|
||||
<T> List<T> find(Query query, Class<T> entityClass);
|
||||
@@ -663,7 +669,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @return the List of converted objects
|
||||
*/
|
||||
@@ -698,7 +704,7 @@ public interface MongoOperations {
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
|
||||
@@ -710,7 +716,7 @@ public interface MongoOperations {
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @param collectionName the collection to query.
|
||||
* @return
|
||||
*/
|
||||
@@ -725,7 +731,7 @@ public interface MongoOperations {
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @return
|
||||
*/
|
||||
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
|
||||
@@ -739,7 +745,7 @@ public interface MongoOperations {
|
||||
* fields specification.
|
||||
* @param update the {@link Update} to apply on matching documents.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information.
|
||||
* @param entityClass the parameterized type.
|
||||
* @param entityClass the parametrized type.
|
||||
* @param collectionName the collection to query.
|
||||
* @return
|
||||
*/
|
||||
@@ -758,7 +764,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @return the converted object
|
||||
*/
|
||||
<T> T findAndRemove(Query query, Class<T> entityClass);
|
||||
@@ -775,7 +781,7 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param query the query class that specifies the criteria used to find a record and also an optional fields
|
||||
* specification
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @param entityClass the parametrized type of the returned list.
|
||||
* @param collectionName name of the collection to retrieve the objects from
|
||||
* @return the converted object
|
||||
*/
|
||||
@@ -793,7 +799,7 @@ public interface MongoOperations {
|
||||
/**
|
||||
* Returns the number of documents for the given {@link Query} querying the given collection. The given {@link Query}
|
||||
* must solely consist of document field references as we lack type information to map potential property references
|
||||
* onto document fields. TO make sure the query gets mapped, use {@link #count(Query, Class, String)}.
|
||||
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
@@ -916,7 +922,9 @@ public interface MongoOperations {
|
||||
|
||||
/**
|
||||
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
|
||||
* combining the query document and the update document.
|
||||
* combining the query document and the update document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #upsert(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -952,7 +960,9 @@ public interface MongoOperations {
|
||||
|
||||
/**
|
||||
* Updates the first object that is found in the specified collection that matches the query document criteria with
|
||||
* the provided updated document.
|
||||
* the provided updated document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #updateFirst(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -989,7 +999,9 @@ public interface MongoOperations {
|
||||
|
||||
/**
|
||||
* Updates all objects that are found in the specified collection that matches the query document criteria with the
|
||||
* provided updated document.
|
||||
* provided updated document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -1048,7 +1060,9 @@ public interface MongoOperations {
|
||||
|
||||
/**
|
||||
* Remove all documents from the specified collection that match the provided query document criteria. There is no
|
||||
* conversion/mapping done for any criteria using the id field.
|
||||
* conversion/mapping done for any criteria using the id field. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping is not available due to the lack of domain type
|
||||
* information. Use {@link #remove(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to remove a record
|
||||
* @param collectionName name of the collection where the objects will removed
|
||||
@@ -1056,10 +1070,12 @@ public interface MongoOperations {
|
||||
DeleteResult remove(Query query, String collectionName);
|
||||
|
||||
/**
|
||||
* Returns and removes all documents form the specified collection that match the provided query.
|
||||
* Returns and removes all documents form the specified collection that match the provided query. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping is not available due to the lack of domain type
|
||||
* information. Use {@link #findAllAndRemove(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query
|
||||
* @param collectionName
|
||||
* @param query must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.5
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,11 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
|
||||
import static org.springframework.data.util.Optionals.*;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@@ -26,7 +30,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -49,13 +52,13 @@ import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||
@@ -67,9 +70,12 @@ import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||
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.IndexOperationsProvider;
|
||||
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
@@ -88,17 +94,22 @@ import org.springframework.data.mongodb.core.mapreduce.GroupBy;
|
||||
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
|
||||
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
||||
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Meta;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
import org.springframework.data.projection.ProjectionInformation;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.data.util.Optionals;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.jca.cci.core.ConnectionCallback;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
@@ -118,6 +129,7 @@ import com.mongodb.client.MapReduceIterable;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoCursor;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.CountOptions;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
import com.mongodb.client.model.DeleteOptions;
|
||||
import com.mongodb.client.model.Filters;
|
||||
@@ -148,6 +160,7 @@ import com.mongodb.util.JSONParseException;
|
||||
* @author Mark Paluch
|
||||
* @author Laszlo Csontos
|
||||
* @author Maninder Singh
|
||||
* @author Borislav Rangelov
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
|
||||
@@ -173,6 +186,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
private final QueryMapper queryMapper;
|
||||
private final UpdateMapper updateMapper;
|
||||
private final SpelAwareProxyProjectionFactory projectionFactory;
|
||||
|
||||
private WriteConcern writeConcern;
|
||||
private WriteConcernResolver writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
|
||||
@@ -216,6 +230,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
|
||||
this.queryMapper = new QueryMapper(this.mongoConverter);
|
||||
this.updateMapper = new UpdateMapper(this.mongoConverter);
|
||||
this.projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
// We always have a mapping context in the converter, whether it's a simple one or not
|
||||
mappingContext = this.mongoConverter.getMappingContext();
|
||||
@@ -278,10 +293,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
prepareIndexCreator(applicationContext);
|
||||
|
||||
eventPublisher = applicationContext;
|
||||
|
||||
if (mappingContext instanceof ApplicationEventPublisherAware) {
|
||||
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
|
||||
}
|
||||
|
||||
resourceLoader = applicationContext;
|
||||
|
||||
projectionFactory.setBeanFactory(applicationContext);
|
||||
projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,10 +353,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(final Query query, final Class<T> entityType, final String collectionName) {
|
||||
return doStream(query, entityType, collectionName, entityType);
|
||||
}
|
||||
|
||||
protected <T> CloseableIterator<T> doStream(final Query query, final Class<?> entityType, final String collectionName,
|
||||
Class<T> returnType) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(entityType, "Entity type must not be null!");
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
Assert.notNull(returnType, "ReturnType must not be null!");
|
||||
|
||||
return execute(collectionName, new CollectionCallback<CloseableIterator<T>>() {
|
||||
|
||||
@@ -346,14 +372,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
MongoPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entityType);
|
||||
|
||||
Document mappedFields = queryMapper.getMappedFields(query.getFieldsObject(), persistentEntity);
|
||||
Document mappedFields = getMappedFieldsObject(query.getFieldsObject(), persistentEntity, returnType);
|
||||
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), persistentEntity);
|
||||
|
||||
FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType)
|
||||
.prepare(collection.find(mappedQuery).projection(mappedFields));
|
||||
|
||||
return new CloseableIterableCursorAdapter<T>(cursor, exceptionTranslator,
|
||||
new ReadDocumentCallback<T>(mongoConverter, entityType, collectionName));
|
||||
new ProjectingReadCallback<>(mongoConverter, entityType, returnType, collectionName));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -523,7 +549,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
public IndexOperations indexOps(Class<?> entityClass) {
|
||||
return new DefaultIndexOperations(getMongoDbFactory(), determineCollectionName(entityClass), queryMapper);
|
||||
return new DefaultIndexOperations(getMongoDbFactory(), determineCollectionName(entityClass), queryMapper,
|
||||
entityClass);
|
||||
}
|
||||
|
||||
public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {
|
||||
@@ -539,10 +566,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.notNull(mode, "BulkMode must not be null!");
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
|
||||
DefaultBulkOperations operations = new DefaultBulkOperations(this, mode, collectionName, entityType);
|
||||
DefaultBulkOperations operations = new DefaultBulkOperations(this, collectionName, new BulkOperationContext(mode,
|
||||
Optional.ofNullable(getPersistentEntity(entityType)), queryMapper, updateMapper));
|
||||
|
||||
operations.setExceptionTranslator(exceptionTranslator);
|
||||
operations.setWriteConcernResolver(writeConcernResolver);
|
||||
operations.setDefaultWriteConcern(writeConcern);
|
||||
|
||||
return operations;
|
||||
@@ -565,7 +592,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
public <T> T findOne(Query query, Class<T> entityClass, String collectionName) {
|
||||
|
||||
if (query.getSortObject() == null) {
|
||||
if (ObjectUtils.isEmpty(query.getSortObject()) && !query.getCollation().isPresent()) {
|
||||
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass);
|
||||
} else {
|
||||
query.limit(1);
|
||||
@@ -589,14 +616,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), getPersistentEntity(entityClass));
|
||||
FindIterable<Document> iterable = execute(collectionName, new FindCallback(mappedQuery));
|
||||
|
||||
if (query.getCollation().isPresent()) {
|
||||
iterable = iterable
|
||||
.collation(query.getCollation().map(org.springframework.data.mongodb.core.Collation::toMongoCollation).get());
|
||||
}
|
||||
|
||||
return iterable.iterator().hasNext();
|
||||
return execute(collectionName,
|
||||
new ExistsCallback(mappedQuery, query.getCollation().map(Collation::toMongoCollation).orElse(null)));
|
||||
}
|
||||
|
||||
// Find methods that take a Query to express the query and that return a List of objects.
|
||||
@@ -621,9 +643,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
|
||||
|
||||
String idKey = mappingContext.getPersistentEntity(entityClass)//
|
||||
.flatMap(it -> it.getIdProperty())//
|
||||
.map(it -> it.getName()).orElse(ID_FIELD);
|
||||
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass);
|
||||
String idKey = ID_FIELD;
|
||||
if (persistentEntity != null) {
|
||||
if (persistentEntity.getIdProperty() != null) {
|
||||
idKey = persistentEntity.getIdProperty().getName();
|
||||
}
|
||||
}
|
||||
|
||||
return doFindOne(collectionName, new Document(idKey, id), null, entityClass);
|
||||
}
|
||||
@@ -633,17 +659,21 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName) {
|
||||
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> domainType, String collectionName) {
|
||||
return geoNear(near, domainType, collectionName, domainType);
|
||||
}
|
||||
|
||||
public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String collectionName, Class<T> returnType) {
|
||||
|
||||
if (near == null) {
|
||||
throw new InvalidDataAccessApiUsageException("NearQuery must not be null!");
|
||||
}
|
||||
|
||||
if (entityClass == null) {
|
||||
if (domainType == null) {
|
||||
throw new InvalidDataAccessApiUsageException("Entity class must not be null!");
|
||||
}
|
||||
|
||||
String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(entityClass);
|
||||
String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(domainType);
|
||||
Document nearDocument = near.toDocument();
|
||||
|
||||
Document command = new Document("geoNear", collection);
|
||||
@@ -651,12 +681,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
if (nearDocument.containsKey("query")) {
|
||||
Document query = (Document) nearDocument.get("query");
|
||||
command.put("query", queryMapper.getMappedObject(query, getPersistentEntity(entityClass)));
|
||||
command.put("query", queryMapper.getMappedObject(query, getPersistentEntity(domainType)));
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Executing geoNear using: {} for class: {} in collection: {}", serializeToJsonSafely(command),
|
||||
entityClass, collectionName);
|
||||
domainType, collectionName);
|
||||
}
|
||||
|
||||
Document commandResult = executeCommand(command, this.readPreference);
|
||||
@@ -664,7 +694,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
results = results == null ? Collections.emptyList() : results;
|
||||
|
||||
DocumentCallback<GeoResult<T>> callback = new GeoNearResultDocumentCallback<T>(
|
||||
new ReadDocumentCallback<T>(mongoConverter, entityClass, collectionName), near.getMetric());
|
||||
new ProjectingReadCallback<>(mongoConverter, domainType, returnType, collectionName), near.getMetric());
|
||||
List<GeoResult<T>> result = new ArrayList<GeoResult<T>>(results.size());
|
||||
|
||||
int index = 0;
|
||||
@@ -752,11 +782,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
|
||||
final Document document = query == null ? null
|
||||
Document document = query == null ? null
|
||||
: queryMapper.getMappedObject(query.getQueryObject(),
|
||||
Optional.ofNullable(entityClass).flatMap(it -> mappingContext.getPersistentEntity(entityClass)));
|
||||
Optional.ofNullable(entityClass).map(it -> mappingContext.getPersistentEntity(entityClass)));
|
||||
|
||||
return execute(collectionName, (CollectionCallback<Long>) collection -> collection.count(document));
|
||||
return execute(collectionName, collection -> collection.count(document));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -873,13 +903,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
private void initializeVersionProperty(Object entity) {
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> persistentEntity = getPersistentEntity(entity.getClass());
|
||||
MongoPersistentEntity<?> persistentEntity = getPersistentEntity(entity.getClass());
|
||||
|
||||
ifAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty), (l, r) -> {
|
||||
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(l.getPropertyAccessor(entity),
|
||||
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
|
||||
|
||||
MongoPersistentProperty versionProperty = persistentEntity.getRequiredVersionProperty();
|
||||
|
||||
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(persistentEntity.getPropertyAccessor(entity),
|
||||
mongoConverter.getConversionService());
|
||||
accessor.setProperty(r, Optional.of(0));
|
||||
});
|
||||
accessor.setProperty(versionProperty, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void insert(Collection<? extends Object> batchToSave, Class<?> entityClass) {
|
||||
@@ -938,7 +971,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
documentList.add(document);
|
||||
}
|
||||
|
||||
List<Object> ids = consolidateIdentifiers(insertDocumentList(collectionName, documentList), documentList);
|
||||
List<Object> ids = insertDocumentList(collectionName, documentList);
|
||||
|
||||
int i = 0;
|
||||
for (T obj : batchToSave) {
|
||||
@@ -961,12 +994,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.notNull(objectToSave, "Object to save must not be null!");
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = getPersistentEntity(objectToSave.getClass());
|
||||
Optional<MongoPersistentProperty> versionProperty = entity.flatMap(PersistentEntity::getVersionProperty);
|
||||
MongoPersistentEntity<?> entity = getPersistentEntity(objectToSave.getClass());
|
||||
|
||||
mapIfAllPresent(entity, versionProperty, //
|
||||
(l, r) -> doSaveVersioned(objectToSave, l, collectionName))//
|
||||
.orElseGet(() -> doSave(collectionName, objectToSave, this.mongoConverter));
|
||||
if (entity != null && entity.hasVersionProperty()) {
|
||||
doSaveVersioned(objectToSave, entity, collectionName);
|
||||
return;
|
||||
}
|
||||
|
||||
doSave(collectionName, objectToSave, this.mongoConverter);
|
||||
}
|
||||
|
||||
private <T> T doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {
|
||||
@@ -974,13 +1009,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor(
|
||||
entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService());
|
||||
|
||||
Optional<MongoPersistentProperty> versionProperty = entity.getVersionProperty();
|
||||
Optional<Number> versionNumber = versionProperty.flatMap(it -> convertingAccessor.getProperty(it, Number.class));
|
||||
MongoPersistentProperty property = entity.getRequiredVersionProperty();
|
||||
Number number = convertingAccessor.getProperty(property, Number.class);
|
||||
|
||||
return mapIfAllPresent(versionProperty, versionNumber, (property, number) -> {
|
||||
if (number != null) {
|
||||
|
||||
// Bump version number
|
||||
convertingAccessor.setProperty(property, Optional.of(number.longValue() + 1));
|
||||
convertingAccessor.setProperty(property, number.longValue() + 1);
|
||||
|
||||
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
|
||||
assertUpdateableIdIfNotSet(objectToSave);
|
||||
@@ -1002,16 +1037,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
if (result.getModifiedCount() == 0) {
|
||||
throw new OptimisticLockingFailureException(
|
||||
String.format("Cannot save entity %s with version %s to collection %s. Has it been modified meanwhile?", id,
|
||||
versionNumber, collectionName));
|
||||
number, collectionName));
|
||||
}
|
||||
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, document, collectionName));
|
||||
|
||||
return objectToSave;
|
||||
|
||||
}).orElseGet(() -> {
|
||||
doInsert(collectionName, objectToSave, this.mongoConverter);
|
||||
return objectToSave;
|
||||
});
|
||||
}
|
||||
|
||||
doInsert(collectionName, objectToSave, this.mongoConverter);
|
||||
return objectToSave;
|
||||
}
|
||||
|
||||
protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
|
||||
@@ -1051,9 +1086,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: 2.0 - Change method signature to return List<Object> and return all identifiers (DATAMONGO-1513,
|
||||
// DATAMONGO-1519)
|
||||
protected List<ObjectId> insertDocumentList(final String collectionName, final List<Document> documents) {
|
||||
protected List<Object> insertDocumentList(final String collectionName, final List<Document> documents) {
|
||||
|
||||
if (documents.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -1062,33 +1096,24 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
LOGGER.debug("Inserting list of Documents containing {} items", documents.size());
|
||||
}
|
||||
|
||||
execute(collectionName, new CollectionCallback<Void>() {
|
||||
public Void doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
|
||||
null, null);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
execute(collectionName, collection -> {
|
||||
|
||||
if (writeConcernToUse == null) {
|
||||
collection.insertMany(documents);
|
||||
} else {
|
||||
collection.withWriteConcern(writeConcernToUse).insertMany(documents);
|
||||
}
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
|
||||
null, null);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
|
||||
return null;
|
||||
if (writeConcernToUse == null) {
|
||||
collection.insertMany(documents);
|
||||
} else {
|
||||
collection.withWriteConcern(writeConcernToUse).insertMany(documents);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
List<ObjectId> ids = new ArrayList<ObjectId>();
|
||||
for (Document dbo : documents) {
|
||||
Object id = dbo.get(ID_FIELD);
|
||||
if (id instanceof ObjectId) {
|
||||
ids.add((ObjectId) id);
|
||||
} else {
|
||||
// no id was generated
|
||||
ids.add(null);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
return documents.stream()//
|
||||
.map(it -> it.get(ID_FIELD))//
|
||||
.collect(StreamUtils.toUnmodifiableList());
|
||||
}
|
||||
|
||||
protected Object saveDocument(final String collectionName, final Document dbDoc, final Class<?> entityClass) {
|
||||
@@ -1165,8 +1190,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
public UpdateResult doInCollection(MongoCollection<Document> collection)
|
||||
throws MongoException, DataAccessException {
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = entityClass == null ? Optional.empty()
|
||||
: getPersistentEntity(entityClass);
|
||||
MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass);
|
||||
|
||||
increaseVersionForUpdateIfNecessary(entity, update);
|
||||
|
||||
@@ -1212,24 +1236,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
});
|
||||
}
|
||||
|
||||
private void increaseVersionForUpdateIfNecessary(Optional<? extends MongoPersistentEntity<?>> persistentEntity,
|
||||
Update update) {
|
||||
private void increaseVersionForUpdateIfNecessary(MongoPersistentEntity<?> persistentEntity, Update update) {
|
||||
|
||||
ifAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty),
|
||||
(entity, property) -> {
|
||||
String versionFieldName = property.getFieldName();
|
||||
if (!update.modifies(versionFieldName)) {
|
||||
update.inc(versionFieldName, 1L);
|
||||
}
|
||||
});
|
||||
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
|
||||
String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName();
|
||||
if (!update.modifies(versionFieldName)) {
|
||||
update.inc(versionFieldName, 1L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean documentContainsVersionProperty(Document document,
|
||||
Optional<? extends MongoPersistentEntity<?>> persistentEntity) {
|
||||
private boolean documentContainsVersionProperty(Document document, MongoPersistentEntity<?> persistentEntity) {
|
||||
|
||||
return mapIfAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty), //
|
||||
(entity, property) -> document.containsKey(property.getFieldName()))//
|
||||
.orElse(false);
|
||||
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
|
||||
|
||||
MongoPersistentProperty property = persistentEntity.getRequiredVersionProperty();
|
||||
|
||||
return document.containsKey(property.getFieldName());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public DeleteResult remove(Object object) {
|
||||
@@ -1259,21 +1285,25 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private Pair<String, Optional<Object>> extractIdPropertyAndValue(Object object) {
|
||||
private Pair<String, Object> extractIdPropertyAndValue(Object object) {
|
||||
|
||||
Assert.notNull(object, "Id cannot be extracted from 'null'.");
|
||||
|
||||
Class<?> objectType = object.getClass();
|
||||
|
||||
if (object instanceof Document) {
|
||||
return Pair.of(ID_FIELD, Optional.ofNullable(((Document) object).get(ID_FIELD)));
|
||||
return Pair.of(ID_FIELD, ((Document) object).get(ID_FIELD));
|
||||
}
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(objectType);
|
||||
return mapIfAllPresent(entity, entity.flatMap(it -> it.getIdProperty()), //
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(objectType);
|
||||
|
||||
(l, r) -> Pair.of(r.getFieldName(), l.getPropertyAccessor(object).getProperty(r)))//
|
||||
.orElseThrow(() -> new MappingException("No id property found for object of type " + objectType));
|
||||
if (entity != null && entity.hasIdProperty()) {
|
||||
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
return Pair.of(idProperty.getFieldName(), entity.getPropertyAccessor(object).getProperty(idProperty));
|
||||
}
|
||||
|
||||
throw new MappingException("No id property found for object of type " + objectType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1284,8 +1314,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private Query getIdQueryFor(Object object) {
|
||||
|
||||
Pair<String, Optional<Object>> id = extractIdPropertyAndValue(object);
|
||||
return new Query(where(id.getFirst()).is(id.getSecond().orElse(null)));
|
||||
Pair<String, Object> id = extractIdPropertyAndValue(object);
|
||||
return new Query(where(id.getFirst()).is(id.getSecond()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1299,13 +1329,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.notEmpty(objects, "Cannot create Query for empty collection.");
|
||||
|
||||
Iterator<?> it = objects.iterator();
|
||||
Pair<String, Optional<Object>> pair = extractIdPropertyAndValue(it.next());
|
||||
Pair<String, Object> pair = extractIdPropertyAndValue(it.next());
|
||||
|
||||
ArrayList<Object> ids = new ArrayList<Object>(objects.size());
|
||||
ids.add(pair.getSecond().orElse(null));
|
||||
ids.add(pair.getSecond());
|
||||
|
||||
while (it.hasNext()) {
|
||||
ids.add(extractIdPropertyAndValue(it.next()).getSecond().orElse(null));
|
||||
ids.add(extractIdPropertyAndValue(it.next()).getSecond());
|
||||
}
|
||||
|
||||
return new Query(where(pair.getFirst()).in(ids));
|
||||
@@ -1313,15 +1343,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
private void assertUpdateableIdIfNotSet(Object value) {
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> persistentEntity = mappingContext
|
||||
.getPersistentEntity(value.getClass());
|
||||
Optional<MongoPersistentProperty> idProperty = persistentEntity.flatMap(it -> it.getIdProperty());
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(value.getClass());
|
||||
|
||||
Optionals.ifAllPresent(persistentEntity, idProperty, (entity, property) -> {
|
||||
if (entity != null && entity.hasIdProperty()) {
|
||||
|
||||
Optional<Object> propertyValue = entity.getPropertyAccessor(value).getProperty(property);
|
||||
MongoPersistentProperty property = entity.getRequiredIdProperty();
|
||||
Object propertyValue = entity.getPropertyAccessor(value).getProperty(property);
|
||||
|
||||
if (propertyValue.isPresent()) {
|
||||
if (propertyValue != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1330,7 +1359,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
String.format("Cannot autogenerate id of type %s for entity of type %s!", property.getType().getName(),
|
||||
value.getClass().getName()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public DeleteResult remove(Query query, String collectionName) {
|
||||
@@ -1354,7 +1383,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
|
||||
final Document queryObject = query.getQueryObject();
|
||||
final Optional<? extends MongoPersistentEntity<?>> entity = getPersistentEntity(entityClass);
|
||||
final MongoPersistentEntity<?> entity = getPersistentEntity(entityClass);
|
||||
|
||||
return execute(collectionName, new CollectionCallback<DeleteResult>() {
|
||||
|
||||
@@ -1436,9 +1465,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
if (query.getMeta() != null && query.getMeta().getMaxTimeMsec() != null) {
|
||||
result = result.maxTime(query.getMeta().getMaxTimeMsec(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
result = result.sort(query.getSortObject());
|
||||
}
|
||||
result = result.sort(query.getSortObject());
|
||||
|
||||
result = result.filter(queryMapper.getMappedObject(query.getQueryObject(), Optional.empty()));
|
||||
}
|
||||
@@ -1625,17 +1652,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return aggregateStream(aggregation, collectionName, outputType, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoOperations#findAllAndRemove(org.springframework.data.mongodb.core.query.Query, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> findAllAndRemove(Query query, String collectionName) {
|
||||
return findAndRemove(query, null, collectionName);
|
||||
return (List<T>) findAllAndRemove(query, Object.class, collectionName);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoOperations#findAllAndRemove(org.springframework.data.mongodb.core.query.Query, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@@ -1643,8 +1668,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return findAllAndRemove(query, entityClass, determineCollectionName(entityClass));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoOperations#findAllAndRemove(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
@@ -1765,6 +1789,51 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableFindOperation#query(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableFind<T> query(Class<T> domainType) {
|
||||
return new ExecutableFindOperationSupport(this).query(domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation#update(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableUpdate<T> update(Class<T> domainType) {
|
||||
return new ExecutableUpdateOperationSupport(this).update(domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableRemoveOperation#remove(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableRemove<T> remove(Class<T> domainType) {
|
||||
return new ExecutableRemoveOperationSupport(this).remove(domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableAggregationOperation#aggregateAndReturn(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableAggregation<T> aggregateAndReturn(Class<T> domainType) {
|
||||
return new ExecutableAggregationOperationSupport(this).aggregateAndReturn(domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation#insert(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ExecutableInsert<T> insert(Class<T> domainType) {
|
||||
return new ExecutableInsertOperationSupport(this).insert(domainType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the {@link Document} does not enable Aggregation explain mode.
|
||||
*
|
||||
@@ -1883,7 +1952,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
protected <T> T doFindOne(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
Document mappedQuery = queryMapper.getMappedObject(query, entity);
|
||||
Document mappedFields = fields == null ? null : queryMapper.getMappedObject(fields, entity);
|
||||
|
||||
@@ -1933,7 +2002,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
protected <S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||
CursorPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
Document mappedFields = queryMapper.getMappedFields(fields, entity);
|
||||
Document mappedQuery = queryMapper.getMappedObject(query, entity);
|
||||
@@ -1947,20 +2016,37 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified targetClass while
|
||||
* using sourceClass for mapping the query.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
<S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> sourceClass,
|
||||
Class<T> targetClass, CursorPreparer preparer) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(sourceClass);
|
||||
|
||||
Document mappedFields = getMappedFieldsObject(fields, entity, targetClass);
|
||||
Document mappedQuery = queryMapper.getMappedObject(query, entity);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("find using query: {} fields: {} for class: {} in collection: {}",
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName);
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer,
|
||||
new ProjectingReadCallback<>(mongoConverter, sourceClass, targetClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
protected Document convertToDocument(CollectionOptions collectionOptions) {
|
||||
|
||||
Document document = new Document();
|
||||
if (collectionOptions != null) {
|
||||
if (collectionOptions.getCapped() != null) {
|
||||
document.put("capped", collectionOptions.getCapped().booleanValue());
|
||||
}
|
||||
if (collectionOptions.getSize() != null) {
|
||||
document.put("size", collectionOptions.getSize().intValue());
|
||||
}
|
||||
if (collectionOptions.getMaxDocuments() != null) {
|
||||
document.put("max", collectionOptions.getMaxDocuments().intValue());
|
||||
}
|
||||
|
||||
collectionOptions.getCapped().ifPresent(val -> document.put("capped", val));
|
||||
collectionOptions.getSize().ifPresent(val -> document.put("size", val));
|
||||
collectionOptions.getMaxDocuments().ifPresent(val -> document.put("max", val));
|
||||
collectionOptions.getCollation().ifPresent(val -> document.append("collation", val.toDocument()));
|
||||
}
|
||||
return document;
|
||||
@@ -1987,7 +2073,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(query), fields, sort, entityClass, collectionName);
|
||||
}
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||
@@ -2003,7 +2089,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
options = new FindAndModifyOptions();
|
||||
}
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
increaseVersionForUpdateIfNecessary(entity, update);
|
||||
|
||||
@@ -2039,17 +2125,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return;
|
||||
}
|
||||
|
||||
getIdPropertyFor(savedObject.getClass()).ifPresent(idProp -> {
|
||||
MongoPersistentProperty idProperty = getIdPropertyFor(savedObject.getClass());
|
||||
|
||||
if (idProperty != null) {
|
||||
|
||||
ConversionService conversionService = mongoConverter.getConversionService();
|
||||
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(savedObject.getClass());
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
|
||||
|
||||
Optional<Object> value = accessor.getProperty(idProp);
|
||||
if (!value.isPresent()) {
|
||||
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, Optional.of(id));
|
||||
Object value = accessor.getProperty(idProperty);
|
||||
if (value == null) {
|
||||
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProperty, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private MongoCollection<Document> getAndPrepareCollection(MongoDatabase db, String collectionName) {
|
||||
@@ -2180,12 +2268,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return exceptionTranslator;
|
||||
}
|
||||
|
||||
private Optional<? extends MongoPersistentEntity<?>> getPersistentEntity(Class<?> type) {
|
||||
return Optional.ofNullable(type).flatMap(it -> mappingContext.getPersistentEntity(it));
|
||||
private MongoPersistentEntity<?> getPersistentEntity(Class<?> type) {
|
||||
return type != null ? mappingContext.getPersistentEntity(type) : null;
|
||||
}
|
||||
|
||||
private Optional<MongoPersistentProperty> getIdPropertyFor(Class<?> type) {
|
||||
return mappingContext.getPersistentEntity(type).flatMap(it -> it.getIdProperty());
|
||||
private MongoPersistentProperty getIdPropertyFor(Class<?> type) {
|
||||
|
||||
MongoPersistentEntity<?> persistentEntity = getPersistentEntity(type);
|
||||
return persistentEntity != null ? persistentEntity.getIdProperty() : null;
|
||||
}
|
||||
|
||||
private <T> String determineEntityCollectionName(T obj) {
|
||||
@@ -2209,20 +2299,57 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
private static final MongoConverter getDefaultMongoConverter(MongoDbFactory factory) {
|
||||
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
|
||||
MongoCustomConversions conversions = new MongoCustomConversions(Collections.emptyList());
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
|
||||
mappingContext.afterPropertiesSet();
|
||||
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
converter.setCustomConversions(conversions);
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
private Document getMappedSortObject(Query query, Class<?> type) {
|
||||
|
||||
if (query == null || query.getSortObject() == null) {
|
||||
if (query == null || ObjectUtils.isEmpty(query.getSortObject())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return queryMapper.getMappedSort(query.getSortObject(), mappingContext.getPersistentEntity(type));
|
||||
}
|
||||
|
||||
private Document getMappedFieldsObject(Document fields, MongoPersistentEntity<?> entity, Class<?> targetType) {
|
||||
return queryMapper.getMappedFields(addFieldsForProjection(fields, entity.getType(), targetType), entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* For cases where {@code fields} is {@literal null} or {@literal empty} add fields required for creating the
|
||||
* projection (target) type if the {@code targetType} is a {@literal closed interface projection}.
|
||||
*
|
||||
* @param fields can be {@literal null}.
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @param targetType must not be {@literal null}.
|
||||
* @return {@link Document} with fields to be included.
|
||||
*/
|
||||
private Document addFieldsForProjection(Document fields, Class<?> domainType, Class<?> targetType) {
|
||||
|
||||
if ((fields != null && !fields.isEmpty()) || !targetType.isInterface()
|
||||
|| ClassUtils.isAssignable(domainType, targetType)) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(targetType);
|
||||
|
||||
if (projectionInformation.isClosed()) {
|
||||
projectionInformation.getInputProperties().forEach(it -> fields.append(it.getName(), 1));
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
|
||||
* exception if the conversation failed. Thus allows safe re-throwing of the return value.
|
||||
@@ -2237,28 +2364,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return resolved == null ? ex : resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all identifiers for the given documents. Will augment the given identifiers and fill in only the ones that
|
||||
* are {@literal null} currently. This would've been better solved in {@link #insertDBObjectList(String, List)}
|
||||
* directly but would require a signature change of that method.
|
||||
*
|
||||
* @param ids
|
||||
* @param documents
|
||||
* @return TODO: Remove for 2.0 and change method signature of {@link #insertDBObjectList(String, List)}.
|
||||
*/
|
||||
private static List<Object> consolidateIdentifiers(List<ObjectId> ids, List<Document> documents) {
|
||||
|
||||
List<Object> result = new ArrayList<Object>(ids.size());
|
||||
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
|
||||
ObjectId objectId = ids.get(i);
|
||||
result.add(objectId == null ? documents.get(i).get(ID_FIELD) : objectId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Callback implementations
|
||||
|
||||
/**
|
||||
@@ -2276,7 +2381,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
public FindOneCallback(Document query, Document fields) {
|
||||
this.query = query;
|
||||
this.fields = Optional.ofNullable(fields);
|
||||
this.fields = Optional.ofNullable(fields).filter(it -> !ObjectUtils.isEmpty(fields));
|
||||
}
|
||||
|
||||
public Document doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
@@ -2317,7 +2422,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
public FindCallback(Document query, Document fields) {
|
||||
|
||||
this.query = query != null ? query : new Document();
|
||||
this.fields = Optional.ofNullable(fields);
|
||||
this.fields = Optional.ofNullable(fields).filter(it -> !ObjectUtils.isEmpty(fields));
|
||||
}
|
||||
|
||||
public FindIterable<Document> doInCollection(MongoCollection<Document> collection)
|
||||
@@ -2329,6 +2434,25 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized {@link CollectionCallback} that takes an already mappend query and a nullable
|
||||
* {@link com.mongodb.client.model.Collation} to execute a count query limited to one element.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
private static class ExistsCallback implements CollectionCallback<Boolean> {
|
||||
|
||||
private final Document mappedQuery;
|
||||
private final com.mongodb.client.model.Collation collation;
|
||||
|
||||
@Override
|
||||
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
return collection.count(mappedQuery, new CountOptions().limit(1).collation(collation)) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification
|
||||
* {@link Document} and executes that against the {@link DBCollection}.
|
||||
@@ -2441,6 +2565,46 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DocumentCallback} transforming {@link Document} into the given {@code targetType} or decorating the
|
||||
* {@code sourceType} with a {@literal projection} in case the {@code targetType} is an {@litera interface}.
|
||||
*
|
||||
* @param <S>
|
||||
* @param <T>
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
|
||||
|
||||
private final @NonNull EntityReader<Object, Bson> reader;
|
||||
private final @NonNull Class<S> entityType;
|
||||
private final @NonNull Class<T> targetType;
|
||||
private final @NonNull String collectionName;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.MongoTemplate.DocumentCallback#doWith(org.bson.Document)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T doWith(Document object) {
|
||||
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
|
||||
: targetType;
|
||||
Object source = reader.read(typeToRead, object);
|
||||
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
|
||||
|
||||
if (result == null) {
|
||||
maybeEmitEvent(new AfterConvertEvent<>(object, result, collectionName));
|
||||
}
|
||||
|
||||
return (T) result;
|
||||
}
|
||||
}
|
||||
|
||||
class UnwrapAndReadDocumentCallback<T> extends ReadDocumentCallback<T> {
|
||||
|
||||
public UnwrapAndReadDocumentCallback(EntityReader<? super T, Bson> reader, Class<T> type, String collectionName) {
|
||||
@@ -2491,9 +2655,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return cursor;
|
||||
}
|
||||
|
||||
if (query.getSkip() <= 0 && query.getLimit() <= 0
|
||||
&& (query.getSortObject() == null || query.getSortObject().isEmpty()) && !StringUtils.hasText(query.getHint())
|
||||
&& !query.getMeta().hasValues() && !query.getCollation().isPresent()) {
|
||||
if (query.getSkip() <= 0 && query.getLimit() <= 0 && ObjectUtils.isEmpty(query.getSortObject())
|
||||
&& !StringUtils.hasText(query.getHint()) && !query.getMeta().hasValues()
|
||||
&& !query.getCollation().isPresent()) {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@@ -2508,7 +2672,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
if (query.getLimit() > 0) {
|
||||
cursorToUse = cursorToUse.limit(query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null && !query.getSortObject().isEmpty()) {
|
||||
if (!ObjectUtils.isEmpty(query.getSortObject())) {
|
||||
Document sort = type != null ? getMappedSortObject(query, type) : query.getSortObject();
|
||||
cursorToUse = cursorToUse.sort(sort);
|
||||
}
|
||||
@@ -2592,19 +2756,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @since 1.7
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
static class CloseableIterableCursorAdapter<T> implements CloseableIterator<T> {
|
||||
|
||||
private volatile MongoCursor<Document> cursor;
|
||||
private PersistenceExceptionTranslator exceptionTranslator;
|
||||
private DocumentCallback<T> objectReadCallback;
|
||||
|
||||
CloseableIterableCursorAdapter(MongoCursor<Document> cursor, PersistenceExceptionTranslator exceptionTranslator,
|
||||
DocumentCallback<T> objectReadCallback) {
|
||||
this.cursor = cursor;
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
this.objectReadCallback = objectReadCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link Cursor}.
|
||||
*
|
||||
@@ -2654,8 +2812,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
public void close() {
|
||||
|
||||
MongoCursor<Document> c = cursor;
|
||||
|
||||
try {
|
||||
c.close();
|
||||
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
throw potentiallyConvertRuntimeException(ex, exceptionTranslator);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
|
||||
/**
|
||||
* {@link ReactiveAggregationOperation} allows creation and execution of reactive MongoDB aggregation operations in a
|
||||
* fluent API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Aggregation} provided via {@code by} into the
|
||||
* MongoDB specific representation, as well as mapping back the resulting {@link org.bson.Document}. An alternative
|
||||
* input type for mapping the {@link Aggregation} can be provided by using
|
||||
* {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation}.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* aggregateAndReturn(Jedi.class)
|
||||
* .by(newAggregation(Human.class, project("These are not the droids you are looking for")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveAggregationOperation {
|
||||
|
||||
/**
|
||||
* Start creating an aggregation operation that returns results mapped to the given domain type. <br />
|
||||
* Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to specify a potentially different
|
||||
* input type for he aggregation.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ReactiveAggregation}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ReactiveAggregation<T> aggregateAndReturn(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*/
|
||||
interface AggregationOperationWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link AggregationOperationWithAggregation}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null} or empty.
|
||||
*/
|
||||
AggregationOperationWithAggregation<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingAggregationOperation<T> {
|
||||
|
||||
/**
|
||||
* Apply pipeline operations as specified and stream all matching elements. <br />
|
||||
*
|
||||
* @return a {@link Flux} streaming all matching elements. Never {@literal null}.
|
||||
*/
|
||||
Flux<T> all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the aggregation with pipeline stages.
|
||||
*/
|
||||
interface AggregationOperationWithAggregation<T> {
|
||||
|
||||
/**
|
||||
* Set the aggregation to be used.
|
||||
*
|
||||
* @param aggregation must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingAggregationOperation}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if aggregation is {@literal null}.
|
||||
*/
|
||||
TerminatingAggregationOperation<T> by(Aggregation aggregation);
|
||||
}
|
||||
|
||||
interface ReactiveAggregation<T>
|
||||
extends AggregationOperationWithCollection<T>, AggregationOperationWithAggregation<T> {}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveAggregationOperation} operating directly on {@link ReactiveMongoTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @autor Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
class ReactiveAggregationOperationSupport implements ReactiveAggregationOperation {
|
||||
|
||||
private final ReactiveMongoTemplate template;
|
||||
|
||||
/**
|
||||
* Create new instance of {@link ReactiveAggregationOperationSupport}.
|
||||
*
|
||||
* @param template must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if template is {@literal null}.
|
||||
*/
|
||||
ReactiveAggregationOperationSupport(ReactiveMongoTemplate template) {
|
||||
|
||||
Assert.notNull(template, "Template must not be null!");
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveAggregationOperation#aggregateAndReturn(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ReactiveAggregation<T> aggregateAndReturn(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ReactiveAggregationSupport<>(template, domainType, null, null);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ReactiveAggregationSupport<T>
|
||||
implements AggregationOperationWithAggregation<T>, ReactiveAggregation<T>, TerminatingAggregationOperation<T> {
|
||||
|
||||
@NonNull ReactiveMongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Aggregation aggregation;
|
||||
String collection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveAggregationOperation.AggregationOperationWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public AggregationOperationWithAggregation<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ReactiveAggregationSupport<>(template, domainType, aggregation, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveAggregationOperation.AggregationOperationWithAggregation#by(org.springframework.data.mongodb.core.Aggregation)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingAggregationOperation<T> by(Aggregation aggregation) {
|
||||
|
||||
Assert.notNull(aggregation, "Aggregation must not be null!");
|
||||
|
||||
return new ReactiveAggregationSupport<>(template, domainType, aggregation, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveAggregationOperation.TerminatingAggregationOperation#all()
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return template.aggregate(aggregation, getCollectionName(aggregation), domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName(Aggregation aggregation) {
|
||||
|
||||
if (StringUtils.hasText(collection)) {
|
||||
return collection;
|
||||
}
|
||||
|
||||
if (aggregation instanceof TypedAggregation) {
|
||||
|
||||
TypedAggregation<?> typedAggregation = (TypedAggregation<?>) aggregation;
|
||||
|
||||
if (typedAggregation.getInputType() != null) {
|
||||
return template.determineCollectionName(typedAggregation.getInputType());
|
||||
}
|
||||
}
|
||||
|
||||
return template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
/**
|
||||
* {@link ReactiveFindOperation} allows creation and execution of reactive MongoDB find operations in a fluent API
|
||||
* style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
|
||||
* MongoDB specific representation. By default, the originating {@literal domainType} is also used for mapping back the
|
||||
* result from the {@link org.bson.Document}. However, it is possible to define an different {@literal returnType} via
|
||||
* {@code as} to mapping the result.<br />
|
||||
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
|
||||
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
|
||||
* collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* query(Human.class)
|
||||
* .inCollection("star-wars")
|
||||
* .as(Jedi.class)
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveFindOperation {
|
||||
|
||||
/**
|
||||
* Start creating a find operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ReactiveFind}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ReactiveFind<T> query(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose find execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingFind<T> {
|
||||
|
||||
/**
|
||||
* Get exactly zero or one result.
|
||||
*
|
||||
* @return {@link Mono#empty()} if no match found. Never {@literal null}.
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
Mono<T> one();
|
||||
|
||||
/**
|
||||
* Get the first or no result.
|
||||
*
|
||||
* @return {@link Mono#empty()} if no match found. Never {@literal null}.
|
||||
*/
|
||||
Mono<T> first();
|
||||
|
||||
/**
|
||||
* Get all matching elements.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Flux<T> all();
|
||||
|
||||
/**
|
||||
* Get the number of matching elements.
|
||||
*
|
||||
* @return {@link Mono} emitting total number of matching elements. Never {@literal null}.
|
||||
*/
|
||||
Mono<Long> count();
|
||||
|
||||
/**
|
||||
* Check for the presence of matching elements.
|
||||
*
|
||||
* @return {@link Mono} emitting {@literal true} if at least one matching element exists. Never {@literal null}.
|
||||
*/
|
||||
Mono<Boolean> exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose geonear execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingFindNear<T> {
|
||||
|
||||
/**
|
||||
* Find all matching elements and return them as {@link org.springframework.data.geo.GeoResult}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Flux<GeoResult<T>> all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a {@link Query} override (optional).
|
||||
*/
|
||||
interface FindWithQuery<T> extends TerminatingFind<T> {
|
||||
|
||||
/**
|
||||
* Set the filter query to be used.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFind}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
TerminatingFind<T> matching(Query query);
|
||||
|
||||
/**
|
||||
* Set the filter query for the geoNear execution.
|
||||
*
|
||||
* @param nearQuery must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFindNear}.
|
||||
* @throws IllegalArgumentException if nearQuery is {@literal null}.
|
||||
*/
|
||||
TerminatingFindNear<T> near(NearQuery nearQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*/
|
||||
interface FindWithCollection<T> extends FindWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link FindWithProjection}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
FindWithProjection<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type override (optional).
|
||||
*/
|
||||
interface FindWithProjection<T> extends FindWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param resultType must not be {@literal null}.
|
||||
* @param <R> result type.
|
||||
* @return new instance of {@link FindWithProjection}.
|
||||
* @throws IllegalArgumentException if resultType is {@literal null}.
|
||||
*/
|
||||
<R> FindWithQuery<R> as(Class<R> resultType);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReactiveFind} provides methods for constructing lookup operations in a fluent way.
|
||||
*/
|
||||
interface ReactiveFind<T> extends FindWithCollection<T>, FindWithProjection<T> {}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.reactivestreams.client.FindPublisher;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveFindOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class ReactiveFindOperationSupport implements ReactiveFindOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final @NonNull ReactiveMongoTemplate template;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation#query(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ReactiveFind<T> query(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ReactiveFindSupport<>(template, domainType, domainType, null, ALL_QUERY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ReactiveFindSupport<T>
|
||||
implements ReactiveFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
|
||||
|
||||
@NonNull ReactiveMongoTemplate template;
|
||||
@NonNull Class<?> domainType;
|
||||
Class<T> returnType;
|
||||
String collection;
|
||||
Query query;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public FindWithProjection<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection name must not be null nor empty!");
|
||||
|
||||
return new ReactiveFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithProjection#as(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T1> FindWithQuery<T1> as(Class<T1> returnType) {
|
||||
|
||||
Assert.notNull(returnType, "ReturnType must not be null!");
|
||||
|
||||
return new ReactiveFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery#matching(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFind<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ReactiveFindSupport<>(template, domainType, returnType, collection, query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind#first()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> first() {
|
||||
|
||||
FindPublisherPreparer preparer = getCursorPreparer(query);
|
||||
Flux<T> result = doFind(new FindPublisherPreparer() {
|
||||
@Override
|
||||
public <D> FindPublisher<D> prepare(FindPublisher<D> publisher) {
|
||||
return preparer.prepare(publisher).limit(1);
|
||||
}
|
||||
});
|
||||
|
||||
return result.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind#one()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> one() {
|
||||
|
||||
FindPublisherPreparer preparer = getCursorPreparer(query);
|
||||
Flux<T> result = doFind(new FindPublisherPreparer() {
|
||||
@Override
|
||||
public <D> FindPublisher<D> prepare(FindPublisher<D> publisher) {
|
||||
return preparer.prepare(publisher).limit(2);
|
||||
}
|
||||
});
|
||||
|
||||
return result.collectList().flatMap(it -> {
|
||||
|
||||
if (it.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
if (it.size() > 1) {
|
||||
return Mono.error(
|
||||
new IncorrectResultSizeDataAccessException("Query " + asString() + " returned non unique result.", 1));
|
||||
}
|
||||
|
||||
return Mono.just(it.get(0));
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind#all()
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return doFind(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery#near(org.springframework.data.mongodb.core.query.NearQuery)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFindNear<T> near(NearQuery nearQuery) {
|
||||
return () -> template.geoNear(nearQuery, domainType, getCollectionName(), returnType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind#count()
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return template.count(query, domainType, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind#exists()
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> exists() {
|
||||
return template.exists(query, domainType, getCollectionName());
|
||||
}
|
||||
|
||||
private Flux<T> doFind(FindPublisherPreparer preparer) {
|
||||
|
||||
Document queryObject = query.getQueryObject();
|
||||
Document fieldsObject = query.getFieldsObject();
|
||||
|
||||
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||
preparer != null ? preparer : getCursorPreparer(query));
|
||||
}
|
||||
|
||||
private FindPublisherPreparer getCursorPreparer(Query query) {
|
||||
return template.new QueryFindPublisherPreparer(query, domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
|
||||
private String asString() {
|
||||
return SerializationUtils.serializeToJsonSafely(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
/**
|
||||
* Stripped down interface providing access to a fluent API that specifies a basic set of reactive MongoDB operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveFluentMongoOperations extends ReactiveFindOperation, ReactiveInsertOperation,
|
||||
ReactiveUpdateOperation, ReactiveRemoveOperation, ReactiveAggregationOperation {}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* {@link ReactiveInsertOperation} allows creation and execution of reactive MongoDB insert and bulk insert operations
|
||||
* in a fluent API style. <br />
|
||||
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
|
||||
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
|
||||
* collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* insert(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .one(luke);
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveInsertOperation {
|
||||
|
||||
/**
|
||||
* Start creating an insert operation for given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ReactiveInsert}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ReactiveInsert<T> insert(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose insert execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingInsert<T> {
|
||||
|
||||
/**
|
||||
* Insert exactly one object.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @return {@link Mono} emitting the inserted {@code object} when operation has completed. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if object is {@literal null}.
|
||||
*/
|
||||
Mono<T> one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of objects.
|
||||
*
|
||||
* @param objects must not be {@literal null}.
|
||||
* @return {@literal Flux} emitting the inserted {@code objects} ony by one. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if objects is {@literal null}.
|
||||
*/
|
||||
Flux<T> all(Collection<? extends T> objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*/
|
||||
interface InsertWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link TerminatingInsert}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
*/
|
||||
TerminatingInsert<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
interface ReactiveInsert<T> extends TerminatingInsert<T>, InsertWithCollection<T> {}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveInsertOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
|
||||
|
||||
private final @NonNull ReactiveMongoTemplate template;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveInsertOperation#insert(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ReactiveInsert<T> insert(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ReactiveInsertSupport<>(template, domainType, null);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ReactiveInsertSupport<T> implements ReactiveInsert<T> {
|
||||
|
||||
@NonNull ReactiveMongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
String collection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveInsertOperation.TerminatingInsert#one(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> one(T object) {
|
||||
|
||||
Assert.notNull(object, "Object must not be null!");
|
||||
|
||||
return template.insert(object, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveInsertOperation.TerminatingInsert#all(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> all(Collection<? extends T> objects) {
|
||||
|
||||
Assert.notNull(objects, "Objects must not be null!");
|
||||
|
||||
return template.insert(objects, getCollectionName());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveInsertOperation.InsertWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ReactiveInsert<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
|
||||
return new ReactiveInsertSupport<>(template, domainType, collection);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.reactivestreams.client.MongoClient;
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase;
|
||||
|
||||
/**
|
||||
* Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the
|
||||
* framework.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class ReactiveMongoDbUtils {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private ReactiveMongoDbUtils() {}
|
||||
|
||||
/**
|
||||
* Obtains a {@link MongoDatabase} connection for the given {@link MongoClient} instance and database name
|
||||
*
|
||||
* @param mongo the {@link MongoClient} instance, must not be {@literal null}.
|
||||
* @param databaseName the database name, must not be {@literal null} or empty.
|
||||
* @return the {@link MongoDatabase} connection
|
||||
*/
|
||||
public static MongoDatabase getMongoDatabase(MongoClient mongo, String databaseName) {
|
||||
return doGetMongoDatabase(mongo, databaseName, true);
|
||||
}
|
||||
|
||||
private static MongoDatabase doGetMongoDatabase(MongoClient mongo, String databaseName, boolean allowCreate) {
|
||||
return mongo.getDatabase(databaseName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,21 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.index.ReactiveIndexOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
@@ -34,9 +41,6 @@ import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
import com.mongodb.reactivestreams.client.MongoCollection;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Interface that specifies a basic set of MongoDB operations executed in a reactive way.
|
||||
* <p>
|
||||
@@ -51,7 +55,7 @@ import reactor.core.publisher.Mono;
|
||||
* @see Mono
|
||||
* @see <a href="http://projectreactor.io/docs/">Project Reactor</a>
|
||||
*/
|
||||
public interface ReactiveMongoOperations {
|
||||
public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
|
||||
/**
|
||||
* Returns the reactive operations that can be performed on indexes
|
||||
@@ -228,8 +232,8 @@ public interface ReactiveMongoOperations {
|
||||
* <p/>
|
||||
* If your collection does not contain a homogeneous collection of types, this operation will not be an efficient way
|
||||
* to map objects since the test for class type is done in the client and not on the server.
|
||||
* @param entityClass the parametrized type of the returned {@link Flux}.
|
||||
*
|
||||
* @param entityClass the parametrized type of the returned {@link Flux}.
|
||||
* @return the converted collection
|
||||
*/
|
||||
<T> Flux<T> findAll(Class<T> entityClass);
|
||||
@@ -285,7 +289,9 @@ public interface ReactiveMongoOperations {
|
||||
<T> Mono<T> findOne(Query query, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Determine result of given {@link Query} contains at least one element.
|
||||
* Determine result of given {@link Query} contains at least one element. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for query/field mapping, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #exists(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the criteria used to find a record.
|
||||
* @param collectionName name of the collection to check for objects.
|
||||
@@ -368,10 +374,87 @@ public interface ReactiveMongoOperations {
|
||||
<T> Mono<T> findById(Object id, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Returns {@link Flux} of {@link GeoResult} for all entities matching the given {@link NearQuery}. Will consider entity mapping
|
||||
* information to determine the collection the query is ran against. Note, that MongoDB limits the number of results
|
||||
* by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a particular number of
|
||||
* results.
|
||||
* Execute an aggregation operation.
|
||||
* <p>
|
||||
* The raw results will be mapped to the given entity class.
|
||||
* <p>
|
||||
* Aggregation streaming cannot be used with {@link AggregationOptions#isExplain() aggregation explain} nor with
|
||||
* {@link AggregationOptions#getCursorBatchSize()}. Enabling explanation mode or setting batch size cause
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations. Must not be
|
||||
* {@literal null}.
|
||||
* @param collectionName The name of the input collection to use for the aggregation. Must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned {@link Flux}. Must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @throws IllegalArgumentException if {@code aggregation}, {@code collectionName} or {@code outputType} is
|
||||
* {@literal null}.
|
||||
*/
|
||||
<O> Flux<O> aggregate(TypedAggregation<?> aggregation, String collectionName, Class<O> outputType);
|
||||
|
||||
/**
|
||||
* Execute an aggregation operation.
|
||||
* <p/>
|
||||
* The raw results will be mapped to the given entity class and are returned as stream. The name of the
|
||||
* inputCollection is derived from the {@link TypedAggregation#getInputType() aggregation input type}.
|
||||
* <p/>
|
||||
* Aggregation streaming cannot be used with {@link AggregationOptions#isExplain() aggregation explain} nor with
|
||||
* {@link AggregationOptions#getCursorBatchSize()}. Enabling explanation mode or setting batch size cause
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param aggregation The {@link TypedAggregation} specification holding the aggregation operations. Must not be
|
||||
* {@literal null}.
|
||||
* @param outputType The parametrized type of the returned {@link Flux}. Must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @throws IllegalArgumentException if {@code aggregation} or {@code outputType} is {@literal null}.
|
||||
*/
|
||||
<O> Flux<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType);
|
||||
|
||||
/**
|
||||
* Execute an aggregation operation.
|
||||
* <p/>
|
||||
* The raw results will be mapped to the given {@code ouputType}. The name of the inputCollection is derived from the
|
||||
* {@code inputType}.
|
||||
* <p/>
|
||||
* Aggregation streaming cannot be used with {@link AggregationOptions#isExplain() aggregation explain} nor with
|
||||
* {@link AggregationOptions#getCursorBatchSize()}. Enabling explanation mode or setting batch size cause
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param aggregation The {@link Aggregation} specification holding the aggregation operations. Must not be
|
||||
* {@literal null}.
|
||||
* @param inputType the inputType where the aggregation operation will read from. Must not be {@literal null}.
|
||||
* @param outputType The parametrized type of the returned {@link Flux}. Must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @throws IllegalArgumentException if {@code aggregation}, {@code inputType} or {@code outputType} is
|
||||
* {@literal null}.
|
||||
*/
|
||||
<O> Flux<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType);
|
||||
|
||||
/**
|
||||
* Execute an aggregation operation.
|
||||
* <p/>
|
||||
* The raw results will be mapped to the given entity class.
|
||||
* <p/>
|
||||
* Aggregation streaming cannot be used with {@link AggregationOptions#isExplain() aggregation explain} nor with
|
||||
* {@link AggregationOptions#getCursorBatchSize()}. Enabling explanation mode or setting batch size cause
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param aggregation The {@link Aggregation} specification holding the aggregation operations. Must not be
|
||||
* {@literal null}.
|
||||
* @param collectionName the collection where the aggregation operation will read from. Must not be {@literal null} or
|
||||
* empty.
|
||||
* @param outputType The parametrized type of the returned {@link Flux}. Must not be {@literal null}.
|
||||
* @return The results of the aggregation operation.
|
||||
* @throws IllegalArgumentException if {@code aggregation}, {@code collectionName} or {@code outputType} is
|
||||
* {@literal null}.
|
||||
*/
|
||||
<O> Flux<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType);
|
||||
|
||||
/**
|
||||
* Returns {@link Flux} of {@link GeoResult} for all entities matching the given {@link NearQuery}. Will consider
|
||||
* entity mapping information to determine the collection the query is ran against. Note, that MongoDB limits the
|
||||
* number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a
|
||||
* particular number of results.
|
||||
*
|
||||
* @param near must not be {@literal null}.
|
||||
* @param entityClass must not be {@literal null}.
|
||||
@@ -380,9 +463,9 @@ public interface ReactiveMongoOperations {
|
||||
<T> Flux<GeoResult<T>> geoNear(NearQuery near, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Returns {@link Flux} of {@link GeoResult} for all entities matching the given {@link NearQuery}. Note, that MongoDB limits the
|
||||
* number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a
|
||||
* particular number of results.
|
||||
* Returns {@link Flux} of {@link GeoResult} for all entities matching the given {@link NearQuery}. Note, that MongoDB
|
||||
* limits the number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect
|
||||
* a particular number of results.
|
||||
*
|
||||
* @param near must not be {@literal null}.
|
||||
* @param entityClass must not be {@literal null}.
|
||||
@@ -494,7 +577,7 @@ public interface ReactiveMongoOperations {
|
||||
/**
|
||||
* Returns the number of documents for the given {@link Query} querying the given collection. The given {@link Query}
|
||||
* must solely consist of document field references as we lack type information to map potential property references
|
||||
* onto document fields. TO make sure the query gets mapped, use {@link #count(Query, Class, String)}.
|
||||
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
@@ -707,7 +790,9 @@ public interface ReactiveMongoOperations {
|
||||
|
||||
/**
|
||||
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
|
||||
* combining the query document and the update document.
|
||||
* combining the query document and the update document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #upsert(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -743,7 +828,9 @@ public interface ReactiveMongoOperations {
|
||||
|
||||
/**
|
||||
* Updates the first object that is found in the specified collection that matches the query document criteria with
|
||||
* the provided updated document.
|
||||
* the provided updated document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #updateFirst(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -755,7 +842,9 @@ public interface ReactiveMongoOperations {
|
||||
|
||||
/**
|
||||
* Updates the first object that is found in the specified collection that matches the query document criteria with
|
||||
* the provided updated document.
|
||||
* the provided updated document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #updateFirst(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -780,7 +869,9 @@ public interface ReactiveMongoOperations {
|
||||
|
||||
/**
|
||||
* Updates all objects that are found in the specified collection that matches the query document criteria with the
|
||||
* provided updated document.
|
||||
* provided updated document. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
|
||||
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to select a record to be updated
|
||||
* @param update the update document that contains the updated object or $ operators to manipulate the existing
|
||||
@@ -859,7 +950,9 @@ public interface ReactiveMongoOperations {
|
||||
|
||||
/**
|
||||
* Remove all documents from the specified collection that match the provided query document criteria. There is no
|
||||
* conversion/mapping done for any criteria using the id field.
|
||||
* conversion/mapping done for any criteria using the id field. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping is not available due to the lack of domain type
|
||||
* information. Use {@link #remove(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query the query document that specifies the criteria used to remove a record
|
||||
* @param collectionName name of the collection where the objects will removed
|
||||
@@ -867,7 +960,9 @@ public interface ReactiveMongoOperations {
|
||||
Mono<DeleteResult> remove(Query query, String collectionName);
|
||||
|
||||
/**
|
||||
* Returns and removes all documents form the specified collection that match the provided query.
|
||||
* Returns and removes all documents form the specified collection that match the provided query. <br />
|
||||
* <strong>NOTE:</strong> Any additional support for field mapping is not available due to the lack of domain type
|
||||
* information. Use {@link #findAllAndRemove(Query, Class, String)} to get full type specific support.
|
||||
*
|
||||
* @param query
|
||||
* @param collectionName
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
|
||||
/**
|
||||
* {@link ReactiveRemoveOperation} allows creation and execution of reactive MongoDB remove / findAndRemove operations
|
||||
* in a fluent API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
|
||||
* MongoDB specific representation. The collection to operate on is by default derived from the initial
|
||||
* {@literal domainType} and can be defined there via {@link org.springframework.data.mongodb.core.mapping.Document}.
|
||||
* Using {@code inCollection} allows to override the collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* remove(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .all();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRemoveOperation {
|
||||
|
||||
/**
|
||||
* Start creating a remove operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ReactiveRemove}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ReactiveRemove<T> remove(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose remove execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingRemove<T> {
|
||||
|
||||
/**
|
||||
* Remove all documents matching.
|
||||
*
|
||||
* @return {@link Mono} emitting the {@link DeleteResult}. Never {@literal null}.
|
||||
*/
|
||||
Mono<DeleteResult> all();
|
||||
|
||||
/**
|
||||
* Remove and return all matching documents. <br/>
|
||||
* <strong>NOTE</strong> The entire list of documents will be fetched before sending the actual delete commands.
|
||||
* Also, {@link org.springframework.context.ApplicationEvent}s will be published for each and every delete
|
||||
* operation.
|
||||
*
|
||||
* @return empty {@link Flux} if no match found. Never {@literal null}.
|
||||
*/
|
||||
Flux<T> findAndRemove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*/
|
||||
interface RemoveWithCollection<T> extends RemoveWithQuery<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link RemoveWithCollection}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null} or empty.
|
||||
*/
|
||||
RemoveWithQuery<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a {@link Query} override (optional).
|
||||
*/
|
||||
interface RemoveWithQuery<T> extends TerminatingRemove<T> {
|
||||
|
||||
/**
|
||||
* Define the query filtering elements.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingRemove}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
TerminatingRemove<T> matching(Query query);
|
||||
}
|
||||
|
||||
interface ReactiveRemove<T> extends RemoveWithCollection<T> {}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveRemoveOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final @NonNull ReactiveMongoTemplate tempate;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveRemoveOperation#remove(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ReactiveRemove<T> remove(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ReactiveRemoveSupport<>(tempate, domainType, ALL_QUERY, null);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCollection<T> {
|
||||
|
||||
@NonNull ReactiveMongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Query query;
|
||||
String collection;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveRemoveOperation.RemoveWithCollection#inCollection(String)
|
||||
*/
|
||||
@Override
|
||||
public RemoveWithQuery<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveRemoveOperation.RemoveWithQuery#matching(org.springframework.data.mongodb.core.Query)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingRemove<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveRemoveOperation.TerminatingRemove#all()
|
||||
*/
|
||||
@Override
|
||||
public Mono<DeleteResult> all() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.doRemove(collectionName, query, domainType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveRemoveOperation.TerminatingRemove#findAndRemove()
|
||||
*/
|
||||
@Override
|
||||
public Flux<T> findAndRemove() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.doFindAndDelete(collectionName, query, domainType);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
/**
|
||||
* {@link ReactiveUpdateOperation} allows creation and execution of reactive MongoDB update / findAndModify operations
|
||||
* in a fluent API style. <br />
|
||||
* The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching}, as well as
|
||||
* the {@link org.springframework.data.mongodb.core.query.Update} via {@code apply} into the MongoDB specific
|
||||
* representations. The collection to operate on is by default derived from the initial {@literal domainType} and can be
|
||||
* defined there via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows
|
||||
* to override the collection name for the execution.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* update(Jedi.class)
|
||||
* .inCollection("star-wars")
|
||||
* .matching(query(where("firstname").is("luke")))
|
||||
* .apply(new Update().set("lastname", "skywalker"))
|
||||
* .upsert();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveUpdateOperation {
|
||||
|
||||
/**
|
||||
* Start creating an update operation for the given {@literal domainType}.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return new instance of {@link ReactiveUpdate}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if domainType is {@literal null}.
|
||||
*/
|
||||
<T> ReactiveUpdate<T> update(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose findAndModify execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingFindAndModify<T> {
|
||||
|
||||
/**
|
||||
* Find, modify and return the first matching document.
|
||||
*
|
||||
* @return {@link Mono#empty()} if nothing found. Never {@literal null}.
|
||||
*/
|
||||
Mono<T> findAndModify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose update execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingUpdate<T> extends TerminatingFindAndModify<T>, FindAndModifyWithOptions<T> {
|
||||
|
||||
/**
|
||||
* Update all matching documents in the collection.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Mono<UpdateResult> all();
|
||||
|
||||
/**
|
||||
* Update the first document in the collection.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Mono<UpdateResult> first();
|
||||
|
||||
/**
|
||||
* Creates a new document if no documents match the filter query or updates the matching ones.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Mono<UpdateResult> upsert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare the {@link org.springframework.data.mongodb.core.query.Update} to apply.
|
||||
*/
|
||||
interface UpdateWithUpdate<T> {
|
||||
|
||||
/**
|
||||
* Set the {@link org.springframework.data.mongodb.core.query.Update} to be applied.
|
||||
*
|
||||
* @param update must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingUpdate}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if update is {@literal null}.
|
||||
*/
|
||||
TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly define the name of the collection to perform operation in (optional).
|
||||
*/
|
||||
interface UpdateWithCollection<T> {
|
||||
|
||||
/**
|
||||
* Explicitly set the name of the collection to perform the query on. <br />
|
||||
* Skip this step to use the default collection derived from the domain type.
|
||||
*
|
||||
* @param collection must not be {@literal null} nor {@literal empty}.
|
||||
* @return new instance of {@link UpdateWithCollection}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null} or empty.
|
||||
*/
|
||||
UpdateWithQuery<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a filter query for the {@link org.springframework.data.mongodb.core.query.Update} (optional).
|
||||
*/
|
||||
interface UpdateWithQuery<T> extends UpdateWithUpdate<T> {
|
||||
|
||||
/**
|
||||
* Filter documents by given {@literal query}.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @return new instance of {@link UpdateWithQuery}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
UpdateWithUpdate<T> matching(Query query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define {@link FindAndModifyOptions} (optional).
|
||||
*/
|
||||
interface FindAndModifyWithOptions<T> {
|
||||
|
||||
/**
|
||||
* Explicitly define {@link FindAndModifyOptions} for the
|
||||
* {@link org.springframework.data.mongodb.core.query.Update}.
|
||||
*
|
||||
* @param options must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFindAndModify}. Never {@literal null}.
|
||||
* @throws IllegalArgumentException if options is {@literal null}.
|
||||
*/
|
||||
TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options);
|
||||
}
|
||||
|
||||
interface ReactiveUpdate<T> extends UpdateWithCollection<T>, UpdateWithQuery<T>, UpdateWithUpdate<T> {}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveUpdateOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
|
||||
private static final Query ALL_QUERY = new Query();
|
||||
|
||||
private final @NonNull ReactiveMongoTemplate template;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation#update(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> ReactiveUpdate<T> update(Class<T> domainType) {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
static class ReactiveUpdateSupport<T>
|
||||
implements ReactiveUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T> {
|
||||
|
||||
@NonNull ReactiveMongoTemplate template;
|
||||
@NonNull Class<T> domainType;
|
||||
Query query;
|
||||
org.springframework.data.mongodb.core.query.Update update;
|
||||
String collection;
|
||||
FindAndModifyOptions options;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.Update)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update) {
|
||||
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithCollection#inCollection(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public UpdateWithQuery<T> inCollection(String collection) {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty!");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate#first()
|
||||
*/
|
||||
@Override
|
||||
public Mono<UpdateResult> first() {
|
||||
return doUpdate(false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate#upsert()
|
||||
*/
|
||||
@Override
|
||||
public Mono<UpdateResult> upsert() {
|
||||
return doUpdate(true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingFindAndModify#findAndModify()
|
||||
*/
|
||||
@Override
|
||||
public Mono<T> findAndModify() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.findAndModify(query, update, options, domainType, collectionName);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithQuery#matching(org.springframework.data.mongodb.core.Query)
|
||||
*/
|
||||
@Override
|
||||
public UpdateWithUpdate<T> matching(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate#all()
|
||||
*/
|
||||
@Override
|
||||
public Mono<UpdateResult> all() {
|
||||
return doUpdate(true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.FindAndModifyWithOptions#withOptions(org.springframework.data.mongodb.core.FindAndModifyOptions)
|
||||
*/
|
||||
@Override
|
||||
public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) {
|
||||
|
||||
Assert.notNull(options, "Options must not be null!");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, options);
|
||||
}
|
||||
|
||||
private Mono<UpdateResult> doUpdate(boolean multi, boolean upsert) {
|
||||
return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
|
||||
* Factory to create {@link MongoDatabase} instances from a {@link MongoClient} instance.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, ReactiveMongoDatabaseFactory {
|
||||
@@ -103,14 +104,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
|
||||
|
||||
Assert.hasText(dbName, "Database name must not be empty.");
|
||||
|
||||
MongoDatabase db = ReactiveMongoDbUtils.getMongoDatabase(mongo, dbName);
|
||||
|
||||
if (writeConcern != null) {
|
||||
|
||||
db = db.withWriteConcern(writeConcern);
|
||||
}
|
||||
|
||||
return db;
|
||||
MongoDatabase db = mongo.getDatabase(dbName);
|
||||
return writeConcern != null ? db.withWriteConcern(writeConcern) : db;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +114,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
|
||||
* @see DisposableBean#destroy()
|
||||
*/
|
||||
public void destroy() throws Exception {
|
||||
|
||||
if (mongoInstanceCreated) {
|
||||
mongo.close();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
@@ -110,6 +110,16 @@ public class AggregationOptions {
|
||||
return new AggregationOptions(allowDiskUse, explain, cursor, collation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a new {@link Builder} for constructing {@link AggregationOptions}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables writing to temporary files. When set to true, aggregation stages can write data to the _tmp subdirectory in
|
||||
* the dbPath directory.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,9 +49,10 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Renders the AST of a SpEL expression as a MongoDB Aggregation Framework projection expression.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
@@ -84,7 +85,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
* {@link AggregationOperationContext} {@code context}.
|
||||
* <p>
|
||||
* Exposes the given @{code params} as <code>[0] ... [n]</code>.
|
||||
*
|
||||
*
|
||||
* @param expression must not be {@literal null}
|
||||
* @param context must not be {@literal null}
|
||||
* @param params must not be {@literal null}
|
||||
@@ -114,7 +115,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
/**
|
||||
* Returns an appropriate {@link ExpressionNodeConversion} for the given {@code node}. Throws an
|
||||
* {@link IllegalArgumentException} if no conversion could be found.
|
||||
*
|
||||
*
|
||||
* @param node
|
||||
* @return the appropriate {@link ExpressionNodeConversion} for the given {@link ExpressionNode}.
|
||||
*/
|
||||
@@ -133,7 +134,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link SpelNode} to (Db)-object conversions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -145,7 +146,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionNodeConversion}.
|
||||
*
|
||||
*
|
||||
* @param transformer must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -161,7 +162,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
/**
|
||||
* Returns whether the current conversion supports the given {@link ExpressionNode}. By default we will match the
|
||||
* node type against the genric type the subclass types the type parameter to.
|
||||
*
|
||||
*
|
||||
* @param node will never be {@literal null}.
|
||||
* @return true if {@literal this} conversion can be applied to the given {@code node}.
|
||||
*/
|
||||
@@ -171,7 +172,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* Triggers the transformation for the given {@link ExpressionNode} and the given current context.
|
||||
*
|
||||
*
|
||||
* @param node must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @return
|
||||
@@ -187,7 +188,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
/**
|
||||
* Triggers the transformation with the given new {@link ExpressionNode}, new parent node, the current operation and
|
||||
* the previous context.
|
||||
*
|
||||
*
|
||||
* @param node must not be {@literal null}.
|
||||
* @param parent
|
||||
* @param operation
|
||||
@@ -204,7 +205,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
context.getAggregationContext()));
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#transform(org.springframework.data.mongodb.core.aggregation.AggregationExpressionTransformer.AggregationExpressionTransformationContext)
|
||||
*/
|
||||
@@ -215,7 +216,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* Performs the actual conversion from {@link SpelNode} to the corresponding representation for MongoDB.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
@@ -224,7 +225,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts arithmetic operations.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class OperatorNodeConversion extends ExpressionNodeConversion<OperatorNode> {
|
||||
@@ -233,7 +234,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -258,8 +259,10 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return convertUnaryMinusOp(context, leftResult);
|
||||
}
|
||||
|
||||
// we deliberately ignore the RHS result
|
||||
transform(currentNode.getRight(), currentNode, operationObject, context);
|
||||
if (!currentNode.isUnaryOperator()) {
|
||||
// we deliberately ignore the RHS result
|
||||
transform(currentNode.getRight(), currentNode, operationObject, context);
|
||||
}
|
||||
|
||||
return operationObject;
|
||||
}
|
||||
@@ -299,7 +302,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#supports(java.lang.Class)
|
||||
*/
|
||||
@@ -311,7 +314,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts indexed expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -321,7 +324,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -330,7 +333,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return context.addToPreviousOrReturn(context.getCurrentNode().getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
@@ -342,7 +345,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts in-line list expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class InlineListNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
|
||||
@@ -351,7 +354,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -368,7 +371,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return transform(currentNode.getChild(0), currentNode, null, context);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
@@ -380,7 +383,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts property or field reference expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -401,7 +404,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return context.addToPreviousOrReturn(fieldReference);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
@@ -413,7 +416,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts literal expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -423,7 +426,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -448,7 +451,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#supports(org.springframework.expression.spel.SpelNode)
|
||||
*/
|
||||
@@ -460,7 +463,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts method reference expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -470,7 +473,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -489,7 +492,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
Document dbo = new Document();
|
||||
|
||||
int i = 0;
|
||||
for(ExpressionNode child : node) {
|
||||
for (ExpressionNode child : node) {
|
||||
dbo.put(methodReference.getArgumentMap()[i++], transform(child, context));
|
||||
}
|
||||
args = dbo;
|
||||
@@ -510,7 +513,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
/**
|
||||
* A {@link ExpressionNodeConversion} that converts method compound expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -520,7 +523,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@@ -537,7 +540,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return context.addToPreviousOrReturn(currentNode.getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import org.bson.Document;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
@@ -27,7 +26,7 @@ import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Used to resolve associations annotated with {@link org.springframework.data.mongodb.core.mapping.DBRef}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
@@ -40,19 +39,19 @@ public interface DbRefResolver {
|
||||
* Resolves the given {@link DBRef} into an object of the given {@link MongoPersistentProperty}'s type. The method
|
||||
* might return a proxy object for the {@link DBRef} or resolve it immediately. In both cases the
|
||||
* {@link DbRefResolverCallback} will be used to obtain the actual backing object.
|
||||
*
|
||||
*
|
||||
* @param property will never be {@literal null}.
|
||||
* @param dbref the {@link DBRef} to resolve.
|
||||
* @param callback will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Optional<Object> resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
|
||||
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
|
||||
DbRefProxyHandler proxyHandler);
|
||||
|
||||
/**
|
||||
* Creates a {@link DBRef} instance for the given {@link org.springframework.data.mongodb.core.mapping.DBRef}
|
||||
* annotation, {@link MongoPersistentEntity} and id.
|
||||
*
|
||||
*
|
||||
* @param annotation will never be {@literal null}.
|
||||
* @param entity will never be {@literal null}.
|
||||
* @param id will never be {@literal null}.
|
||||
@@ -63,7 +62,7 @@ public interface DbRefResolver {
|
||||
|
||||
/**
|
||||
* Actually loads the {@link DBRef} from the datasource.
|
||||
*
|
||||
*
|
||||
* @param dbRef must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import com.mongodb.DBRef;
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultDbRefProxyHandler implements DbRefProxyHandler {
|
||||
|
||||
@@ -50,7 +51,7 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.DbRefProxyHandler#populateId(com.mongodb.DBRef, java.lang.Object)
|
||||
*/
|
||||
@@ -62,8 +63,7 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(property);
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty()
|
||||
.orElseThrow(() -> new IllegalStateException("Couldn't find identifier property!"));
|
||||
MongoPersistentProperty idProperty = entity.getRequiredIdProperty();
|
||||
|
||||
if (idProperty.usePropertyAccess()) {
|
||||
return proxy;
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
@@ -55,7 +54,7 @@ import com.mongodb.client.model.Filters;
|
||||
/**
|
||||
* A {@link DbRefResolver} that resolves {@link org.springframework.data.mongodb.core.mapping.DBRef}s by delegating to a
|
||||
* {@link DbRefResolverCallback} than is able to generate lazy loading proxies.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
@@ -70,7 +69,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultDbRefResolver} with the given {@link MongoDbFactory}.
|
||||
*
|
||||
*
|
||||
* @param mongoDbFactory must not be {@literal null}.
|
||||
*/
|
||||
public DefaultDbRefResolver(MongoDbFactory mongoDbFactory) {
|
||||
@@ -87,20 +86,20 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#resolveDbRef(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.convert.DbRefResolverCallback)
|
||||
*/
|
||||
@Override
|
||||
public Optional<Object> resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
|
||||
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
|
||||
DbRefProxyHandler handler) {
|
||||
|
||||
Assert.notNull(property, "Property must not be null!");
|
||||
Assert.notNull(callback, "Callback must not be null!");
|
||||
|
||||
if (isLazyDbRef(property)) {
|
||||
return Optional.of(createLazyLoadingProxy(property, dbref, callback, handler));
|
||||
return createLazyLoadingProxy(property, dbref, callback, handler);
|
||||
}
|
||||
|
||||
return Optional.ofNullable(callback.resolve(property));
|
||||
return callback.resolve(property);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#created(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.Object)
|
||||
*/
|
||||
@@ -165,7 +164,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
/**
|
||||
* Creates a proxy for the given {@link MongoPersistentProperty} using the given {@link DbRefResolverCallback} to
|
||||
* eventually resolve the value of the property.
|
||||
*
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param dbref can be {@literal null}.
|
||||
* @param callback must not be {@literal null}.
|
||||
@@ -200,7 +199,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Returns the CGLib enhanced type for the given source type.
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@@ -216,7 +215,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Returns whether the property shall be resolved lazily.
|
||||
*
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -228,7 +227,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
* A {@link MethodInterceptor} that is used within a lazy loading proxy. The property resolving is delegated to a
|
||||
* {@link DbRefResolverCallback}. The resolving process is triggered by a method invocation on the proxy and is
|
||||
* guaranteed to be performed only once.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
@@ -259,7 +258,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
/**
|
||||
* Creates a new {@link LazyLoadingInterceptor} for the given {@link MongoPersistentProperty},
|
||||
* {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}.
|
||||
*
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param dbref can be {@literal null}.
|
||||
* @param callback must not be {@literal null}.
|
||||
@@ -286,7 +285,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)
|
||||
*/
|
||||
@@ -327,12 +326,14 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a to string representation for the given {@code proxy}.
|
||||
*
|
||||
*
|
||||
* @param proxy
|
||||
* @return
|
||||
*/
|
||||
@@ -353,7 +354,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Returns the hashcode for the given {@code proxy}.
|
||||
*
|
||||
*
|
||||
* @param proxy
|
||||
* @return
|
||||
*/
|
||||
@@ -363,7 +364,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Performs an equality check for the given {@code proxy}.
|
||||
*
|
||||
*
|
||||
* @param proxy
|
||||
* @param that
|
||||
* @return
|
||||
@@ -383,7 +384,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Will trigger the resolution if the proxy is not resolved already or return a previously resolved result.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Object ensureResolved() {
|
||||
@@ -398,7 +399,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Callback method for serialization.
|
||||
*
|
||||
*
|
||||
* @param out
|
||||
* @throws IOException
|
||||
*/
|
||||
@@ -410,7 +411,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Callback method for deserialization.
|
||||
*
|
||||
*
|
||||
* @param in
|
||||
* @throws IOException
|
||||
*/
|
||||
@@ -426,7 +427,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
/**
|
||||
* Resolves the proxy into its backing object.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private synchronized Object resolve() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,9 +22,10 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DbRefResolverCallback}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultDbRefResolverCallback implements DbRefResolverCallback {
|
||||
|
||||
@@ -36,7 +37,7 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback {
|
||||
/**
|
||||
* Creates a new {@link DefaultDbRefResolverCallback} using the given {@link Document}, {@link ObjectPath},
|
||||
* {@link ValueResolver} and {@link SpELExpressionEvaluator}.
|
||||
*
|
||||
*
|
||||
* @param surroundingObject must not be {@literal null}.
|
||||
* @param path must not be {@literal null}.
|
||||
* @param evaluator must not be {@literal null}.
|
||||
@@ -51,12 +52,12 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback {
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.DbRefResolverCallback#resolve(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public Object resolve(MongoPersistentProperty property) {
|
||||
return resolver.getValueInternal(property, surroundingObject, evaluator, path).orElse(null);
|
||||
return resolver.getValueInternal(property, surroundingObject, evaluator, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,17 +32,17 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link MongoTypeMapper} allowing configuration of the key to lookup and store type
|
||||
* information in {@link Document}. The key defaults to {@link #DEFAULT_TYPE_KEY}. Actual type-to-{@link String}
|
||||
* conversion and back is done in {@link #getTypeString(TypeInformation)} or {@link #getTypeInformation(String)}
|
||||
* respectively.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
@@ -94,7 +94,7 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
|
||||
return typeKey == null ? false : typeKey.equals(key);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#writeTypeRestrictions(java.util.Set)
|
||||
*/
|
||||
@@ -111,26 +111,26 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
|
||||
|
||||
Alias typeAlias = getAliasFor(ClassTypeInformation.from(restrictedType));
|
||||
|
||||
if (typeAlias != null && !ObjectUtils.nullSafeEquals(Alias.NONE, typeAlias) && typeAlias.getValue().isPresent()) {
|
||||
restrictedMappedTypes.add(typeAlias.getValue().get());
|
||||
if (typeAlias != null && !ObjectUtils.nullSafeEquals(Alias.NONE, typeAlias) && typeAlias.isPresent()) {
|
||||
restrictedMappedTypes.add(typeAlias.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
accessor.writeTypeTo(result, new Document("$in", restrictedMappedTypes));
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.DefaultTypeMapper#getFallbackTypeFor(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected Optional<TypeInformation<?>> getFallbackTypeFor(Bson source) {
|
||||
return Optional.of(source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO);
|
||||
protected TypeInformation<?> getFallbackTypeFor(Bson source) {
|
||||
return source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TypeAliasAccessor} to store aliases in a {@link Document}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static final class DocumentTypeAliasAccessor implements TypeAliasAccessor<Bson> {
|
||||
@@ -152,9 +152,9 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
|
||||
}
|
||||
|
||||
if (source instanceof Document) {
|
||||
return Alias.ofOptional(Optional.ofNullable(((Document) source).get(typeKey)));
|
||||
return Alias.ofNullable(((Document) source).get(typeKey));
|
||||
} else if (source instanceof DBObject) {
|
||||
return Alias.ofOptional(Optional.ofNullable(((DBObject) source).get(typeKey)));
|
||||
return Alias.ofNullable(((DBObject) source).get(typeKey));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Cannot read alias from " + source.getClass());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,6 @@ package org.springframework.data.mongodb.core.convert;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
@@ -34,9 +32,10 @@ import com.mongodb.DBObject;
|
||||
* Wrapper value object for a {@link Document} to be able to access raw values by {@link MongoPersistentProperty}
|
||||
* references. The accessors will transparently resolve nested document values that a {@link MongoPersistentProperty}
|
||||
* might refer to through a path expression in field names.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DocumentAccessor {
|
||||
|
||||
@@ -44,7 +43,7 @@ class DocumentAccessor {
|
||||
|
||||
/**
|
||||
* Creates a new {@link DocumentAccessor} for the given {@link Document}.
|
||||
*
|
||||
*
|
||||
* @param document must be a {@link Document} effectively, must not be {@literal null}.
|
||||
*/
|
||||
public DocumentAccessor(Bson document) {
|
||||
@@ -62,7 +61,7 @@ class DocumentAccessor {
|
||||
* Puts the given value into the backing {@link Document} based on the coordinates defined through the given
|
||||
* {@link MongoPersistentProperty}. By default this will be the plain field name. But field names might also consist
|
||||
* of path traversals so we might need to create intermediate {@link BasicDocument}s.
|
||||
*
|
||||
*
|
||||
* @param prop must not be {@literal null}.
|
||||
* @param value
|
||||
*/
|
||||
@@ -91,20 +90,11 @@ class DocumentAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
public void computeIfAbsent(MongoPersistentProperty prop, Supplier<Optional<Object>> supplier) {
|
||||
|
||||
if (hasValue(prop)) {
|
||||
return;
|
||||
}
|
||||
|
||||
supplier.get().ifPresent(it -> put(prop, it));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value the given {@link MongoPersistentProperty} refers to. By default this will be a direct field but
|
||||
* the method will also transparently resolve nested values the {@link MongoPersistentProperty} might refer to through
|
||||
* a path expression in the field name metadata.
|
||||
*
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -161,7 +151,7 @@ class DocumentAccessor {
|
||||
|
||||
if (this.document instanceof Document) {
|
||||
source = ((Document) this.document);
|
||||
}else {
|
||||
} else {
|
||||
source = ((DBObject) this.document).toMap();
|
||||
}
|
||||
|
||||
@@ -182,7 +172,7 @@ class DocumentAccessor {
|
||||
|
||||
/**
|
||||
* Returns the given source object as map, i.e. {@link Document}s and maps as is or {@literal null} otherwise.
|
||||
*
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -207,7 +197,7 @@ class DocumentAccessor {
|
||||
/**
|
||||
* Returns the {@link Document} which either already exists in the given source under the given key, or creates a new
|
||||
* nested one, registers it with the source and returns it.
|
||||
*
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
|
||||
@@ -15,8 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
@@ -30,14 +39,14 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PropertyValueProvider;
|
||||
@@ -52,7 +61,6 @@ import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
|
||||
import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -82,9 +90,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
protected static final Logger LOGGER = LoggerFactory.getLogger(MappingMongoConverter.class);
|
||||
|
||||
protected final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
protected final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
protected final QueryMapper idMapper;
|
||||
protected final DbRefResolver dbRefResolver;
|
||||
protected final DefaultDbRefProxyHandler dbRefProxyHandler;
|
||||
|
||||
protected ApplicationContext applicationContext;
|
||||
protected MongoTypeMapper typeMapper;
|
||||
@@ -112,6 +120,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
this.idMapper = new QueryMapper(this);
|
||||
|
||||
this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE);
|
||||
this.dbRefProxyHandler = new DefaultDbRefProxyHandler(spELContext, mappingContext, MappingMongoConverter.this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,66 +257,89 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final Document bson, final ObjectPath path) {
|
||||
|
||||
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
|
||||
DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
|
||||
|
||||
ParameterValueProvider<MongoPersistentProperty> provider = getParameterProvider(entity, bson, evaluator, path);
|
||||
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
|
||||
S instance = instantiator.createInstance(entity, provider);
|
||||
|
||||
final PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance),
|
||||
PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance),
|
||||
conversionService);
|
||||
|
||||
final Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
|
||||
final S result = instance;
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
DocumentAccessor documentAccessor = new DocumentAccessor(bson);
|
||||
|
||||
// make sure id property is set before all other properties
|
||||
Optional<Object> idValue = idProperty.filter(documentAccessor::hasValue).map(it -> {
|
||||
Object idValue = null;
|
||||
|
||||
Optional<Object> value = getValueInternal(it, bson, evaluator, path);
|
||||
accessor.setProperty(it, value);
|
||||
if (idProperty != null && documentAccessor.hasValue(idProperty)) {
|
||||
|
||||
return value;
|
||||
});
|
||||
idValue = readIdValue(path, evaluator, idProperty, documentAccessor);
|
||||
accessor.setProperty(idProperty, idValue);
|
||||
}
|
||||
|
||||
final ObjectPath currentPath = path.push(result, entity,
|
||||
idValue.isPresent() ? idProperty.map(it -> bson.get(it.getFieldName())).orElse(null) : null);
|
||||
ObjectPath currentPath = path.push(instance, entity, idValue != null ? bson.get(idProperty.getFieldName()) : null);
|
||||
|
||||
// Set properties not already set in the constructor
|
||||
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) prop -> {
|
||||
MongoDbPropertyValueProvider valueProvider = new MongoDbPropertyValueProvider(documentAccessor, evaluator,
|
||||
currentPath);
|
||||
|
||||
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(bson, currentPath, evaluator,
|
||||
MappingMongoConverter.this);
|
||||
readProperties(entity, accessor, idProperty, documentAccessor, valueProvider, callback);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Object readIdValue(ObjectPath path, DefaultSpELExpressionEvaluator evaluator,
|
||||
MongoPersistentProperty idProperty, DocumentAccessor documentAccessor) {
|
||||
|
||||
String expression = idProperty.getSpelExpression();
|
||||
Object resolvedValue = expression != null ? evaluator.evaluate(expression) : documentAccessor.get(idProperty);
|
||||
|
||||
return resolvedValue != null ? readValue(resolvedValue, idProperty.getTypeInformation(), path) : null;
|
||||
}
|
||||
|
||||
private void readProperties(MongoPersistentEntity<?> entity, PersistentPropertyAccessor accessor,
|
||||
MongoPersistentProperty idProperty, DocumentAccessor documentAccessor,
|
||||
MongoDbPropertyValueProvider valueProvider, DbRefResolverCallback callback) {
|
||||
|
||||
|
||||
for (MongoPersistentProperty prop : entity) {
|
||||
|
||||
if(prop.isAssociation() && !entity.isConstructorArgument(prop)) {
|
||||
readAssociation(prop.getAssociation(), accessor, documentAccessor, dbRefProxyHandler, callback );
|
||||
continue;
|
||||
}
|
||||
// we skip the id property since it was already set
|
||||
if (idProperty != null && idProperty.equals(prop)) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity.isConstructorArgument(prop) || !documentAccessor.hasValue(prop)) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
accessor.setProperty(prop, getValueInternal(prop, bson, evaluator, currentPath));
|
||||
});
|
||||
if(prop.isAssociation()) {
|
||||
readAssociation(prop.getAssociation(), accessor, documentAccessor, dbRefProxyHandler, callback );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle associations
|
||||
entity.doWithAssociations((AssociationHandler<MongoPersistentProperty>) association -> {
|
||||
accessor.setProperty(prop, valueProvider.getPropertyValue(prop));
|
||||
}
|
||||
}
|
||||
|
||||
final MongoPersistentProperty property = association.getInverse();
|
||||
private void readAssociation(Association<MongoPersistentProperty> association, PersistentPropertyAccessor accessor,
|
||||
DocumentAccessor documentAccessor, DbRefProxyHandler handler, DbRefResolverCallback callback) {
|
||||
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
Object value = documentAccessor.get(property);
|
||||
|
||||
if (value == null || entity.isConstructorArgument(property)) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
|
||||
|
||||
DbRefProxyHandler handler = new DefaultDbRefProxyHandler(spELContext, mappingContext, MappingMongoConverter.this);
|
||||
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(bson, currentPath, evaluator,
|
||||
MappingMongoConverter.this);
|
||||
|
||||
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -348,8 +380,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj;
|
||||
|
||||
writeInternal(target, bson, Optional.of(type));
|
||||
if (asMap(bson).containsKey("_is") && asMap(bson).get("_id") == null) {
|
||||
writeInternal(target, bson, type);
|
||||
if (asMap(bson).containsKey("_id") && asMap(bson).get("_id") == null) {
|
||||
removeFromMap(bson, "_id");
|
||||
}
|
||||
|
||||
@@ -366,7 +398,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param bson
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void writeInternal(final Object obj, final Bson bson, final Optional<TypeInformation<?>> typeHint) {
|
||||
protected void writeInternal(final Object obj, final Bson bson, final TypeInformation<?> typeHint) {
|
||||
|
||||
if (null == obj) {
|
||||
return;
|
||||
@@ -387,7 +419,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
if (Collection.class.isAssignableFrom(entityType)) {
|
||||
writeCollectionInternal((Collection<?>) obj, Optional.of(ClassTypeInformation.LIST), (BasicDBList) bson);
|
||||
writeCollectionInternal((Collection<?>) obj, ClassTypeInformation.LIST, (BasicDBList) bson);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -406,46 +438,65 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
throw new MappingException("No mapping metadata found for entity of type " + obj.getClass().getName());
|
||||
}
|
||||
|
||||
final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(obj);
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(obj);
|
||||
DocumentAccessor dbObjectAccessor = new DocumentAccessor(bson);
|
||||
|
||||
Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
|
||||
idProperty.ifPresent(
|
||||
prop -> dbObjectAccessor.computeIfAbsent(prop, () -> idMapper.convertId(accessor.getProperty(prop))));
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
if (idProperty != null && !dbObjectAccessor.hasValue(idProperty)) {
|
||||
|
||||
Object value = idMapper.convertId(accessor.getProperty(idProperty));
|
||||
|
||||
if (value != null) {
|
||||
dbObjectAccessor.put(idProperty, value);
|
||||
}
|
||||
}
|
||||
writeProperties(bson, entity, accessor, dbObjectAccessor, idProperty);
|
||||
}
|
||||
|
||||
private void writeProperties(Bson bson, MongoPersistentEntity<?> entity, PersistentPropertyAccessor accessor,
|
||||
DocumentAccessor dbObjectAccessor, MongoPersistentProperty idProperty) {
|
||||
|
||||
// Write the properties
|
||||
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) prop -> {
|
||||
for (MongoPersistentProperty prop : entity) {
|
||||
|
||||
if (idProperty.map(it -> it.equals(prop)).orElse(false) || !prop.isWritable()) {
|
||||
return;
|
||||
|
||||
if (prop.equals(idProperty) || !prop.isWritable()) {
|
||||
continue;
|
||||
}
|
||||
if(prop.isAssociation()) {
|
||||
writeAssociation(prop.getAssociation(), accessor, dbObjectAccessor);
|
||||
continue;
|
||||
}
|
||||
|
||||
accessor.getProperty(prop).ifPresent(it -> {
|
||||
if (!conversions.isSimpleType(it.getClass())) {
|
||||
Object value = accessor.getProperty(prop);
|
||||
|
||||
writePropertyInternal(it, bson, prop);
|
||||
} else {
|
||||
writeSimpleInternal(it, bson, prop);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entity.doWithAssociations((AssociationHandler<MongoPersistentProperty>) association -> {
|
||||
if (!conversions.isSimpleType(value.getClass())) {
|
||||
writePropertyInternal(value, dbObjectAccessor, prop);
|
||||
} else {
|
||||
writeSimpleInternal(value, bson, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAssociation(Association<MongoPersistentProperty> association, PersistentPropertyAccessor accessor,
|
||||
DocumentAccessor dbObjectAccessor) {
|
||||
|
||||
MongoPersistentProperty inverseProp = association.getInverse();
|
||||
accessor.getProperty(inverseProp).ifPresent(it -> writePropertyInternal(it, bson, inverseProp));
|
||||
});
|
||||
|
||||
writePropertyInternal(accessor.getProperty(inverseProp), dbObjectAccessor, inverseProp);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
protected void writePropertyInternal(Object obj, Bson bson, MongoPersistentProperty prop) {
|
||||
protected void writePropertyInternal(Object obj, DocumentAccessor accessor, MongoPersistentProperty prop) {
|
||||
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DocumentAccessor accessor = new DocumentAccessor(bson);
|
||||
|
||||
TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass());
|
||||
TypeInformation<?> type = prop.getTypeInformation();
|
||||
|
||||
@@ -497,14 +548,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
Object existingValue = accessor.get(prop);
|
||||
Document document = existingValue instanceof Document ? (Document) existingValue : new Document();
|
||||
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
|
||||
? mappingContext.getRequiredPersistentEntity(obj.getClass()) : mappingContext.getRequiredPersistentEntity(type);
|
||||
|
||||
Object existingValue = accessor.get(prop);
|
||||
Document document = existingValue instanceof Document ? (Document) existingValue : new Document();
|
||||
|
||||
writeInternal(obj, document, entity);
|
||||
addCustomTypeKeyIfNecessary(Optional.of(ClassTypeInformation.from(prop.getRawType())), obj, document);
|
||||
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
|
||||
accessor.put(prop, document);
|
||||
}
|
||||
|
||||
@@ -539,7 +590,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
protected List<Object> createCollection(Collection<?> collection, MongoPersistentProperty property) {
|
||||
|
||||
if (!property.isDbReference()) {
|
||||
return writeCollectionInternal(collection, Optional.of(property.getTypeInformation()), new BasicDBList());
|
||||
return writeCollectionInternal(collection, property.getTypeInformation(), new BasicDBList());
|
||||
}
|
||||
|
||||
List<Object> dbList = new ArrayList<>(collection.size());
|
||||
@@ -601,10 +652,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param sink the {@link BasicDBList} to write to.
|
||||
* @return
|
||||
*/
|
||||
private BasicDBList writeCollectionInternal(Collection<?> source, Optional<TypeInformation<?>> type,
|
||||
BasicDBList sink) {
|
||||
private BasicDBList writeCollectionInternal(Collection<?> source, TypeInformation<?> type, BasicDBList sink) {
|
||||
|
||||
Optional<TypeInformation<?>> componentType = type.flatMap(TypeInformation::getComponentType);
|
||||
TypeInformation<?> componentType = null;
|
||||
|
||||
if (type != null) {
|
||||
componentType = type.getComponentType();
|
||||
}
|
||||
|
||||
for (Object element : source) {
|
||||
|
||||
@@ -649,8 +703,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
writeCollectionInternal(asCollection(val), propertyType.getMapValueType(), new BasicDBList()));
|
||||
} else {
|
||||
Document document = new Document();
|
||||
Optional<TypeInformation<?>> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
|
||||
: Optional.of(ClassTypeInformation.OBJECT);
|
||||
TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
|
||||
: ClassTypeInformation.OBJECT;
|
||||
writeInternal(val, document, valueTypeInfo);
|
||||
addToMap(bson, simpleKey, document);
|
||||
}
|
||||
@@ -736,10 +790,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param value must not be {@literal null}.
|
||||
* @param bson must not be {@literal null}.
|
||||
*/
|
||||
protected void addCustomTypeKeyIfNecessary(Optional<TypeInformation<?>> type, Object value, Bson bson) {
|
||||
protected void addCustomTypeKeyIfNecessary(TypeInformation<?> type, Object value, Bson bson) {
|
||||
|
||||
Optional<Class<?>> actualType = type.map(TypeInformation::getActualType).map(TypeInformation::getType);
|
||||
Class<?> reference = actualType.orElse(Object.class);
|
||||
Class<?> reference = type != null ? type.getActualType().getType() : Object.class;
|
||||
Class<?> valueType = ClassUtils.getUserClass(value.getClass());
|
||||
|
||||
boolean notTheSameClass = !valueType.equals(reference);
|
||||
@@ -779,18 +832,19 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(value.getClass());
|
||||
|
||||
return customTarget.map(it -> (Object) conversionService.convert(value, it)).orElseGet(() -> {
|
||||
if (customTarget.isPresent()) {
|
||||
return conversionService.convert(value, customTarget.get());
|
||||
}
|
||||
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
|
||||
if (value instanceof byte[]) {
|
||||
return value;
|
||||
}
|
||||
return asCollection(value);
|
||||
if (value instanceof byte[]) {
|
||||
return value;
|
||||
}
|
||||
return asCollection(value);
|
||||
}
|
||||
|
||||
return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value;
|
||||
});
|
||||
return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -827,30 +881,30 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return (DBRef) target;
|
||||
}
|
||||
|
||||
Optional<? extends MongoPersistentEntity<?>> targetEntity = mappingContext.getPersistentEntity(target.getClass());
|
||||
targetEntity = targetEntity.isPresent() ? targetEntity : mappingContext.getPersistentEntity(property);
|
||||
MongoPersistentEntity<?> targetEntity = mappingContext.getPersistentEntity(target.getClass());
|
||||
targetEntity = targetEntity != null ? targetEntity : mappingContext.getPersistentEntity(property);
|
||||
|
||||
if (null == targetEntity) {
|
||||
throw new MappingException("No mapping metadata found for " + target.getClass());
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = targetEntity
|
||||
.orElseThrow(() -> new MappingException("No mapping metadata found for " + target.getClass()));
|
||||
MongoPersistentEntity<?> entity = targetEntity;
|
||||
|
||||
Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
|
||||
return idProperty.map(it -> {
|
||||
if (idProperty != null) {
|
||||
|
||||
Object id = target.getClass().equals(it.getType()) ? target : entity.getPropertyAccessor(target).getProperty(it);
|
||||
Object id = target.getClass().equals(idProperty.getType()) ? target
|
||||
: entity.getPropertyAccessor(target).getProperty(idProperty);
|
||||
|
||||
if (null == id) {
|
||||
throw new MappingException("Cannot create a reference to an object with a NULL id.");
|
||||
}
|
||||
|
||||
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), entity,
|
||||
idMapper.convertId(id instanceof Optional ? (Optional) id : Optional.ofNullable(id)).orElse(null));
|
||||
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), entity, idMapper.convertId(id));
|
||||
}
|
||||
|
||||
}).orElseThrow(() -> new MappingException("No id property found on class " + entity.getType()));
|
||||
throw new MappingException("No id property found on class " + entity.getType());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -858,7 +912,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @see org.springframework.data.mongodb.core.convert.ValueResolver#getValueInternal(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, com.mongodb.Document, org.springframework.data.mapping.model.SpELExpressionEvaluator, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Optional<Object> getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
|
||||
public Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
|
||||
ObjectPath path) {
|
||||
return new MongoDbPropertyValueProvider(bson, evaluator, path).getPropertyValue(prop);
|
||||
}
|
||||
@@ -879,7 +933,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Class<?> collectionType = targetType.getType();
|
||||
|
||||
TypeInformation<?> componentType = targetType.getComponentType().orElse(ClassTypeInformation.OBJECT);
|
||||
TypeInformation<?> componentType = targetType.getComponentType() != null ? targetType.getComponentType()
|
||||
: ClassTypeInformation.OBJECT;
|
||||
Class<?> rawComponentType = componentType.getType();
|
||||
|
||||
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
|
||||
@@ -941,15 +996,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Class<?> mapType = typeMapper.readType(bson, type).getType();
|
||||
|
||||
Optional<TypeInformation<?>> valueType = type.getMapValueType();
|
||||
Class<?> rawKeyType = type.getComponentType().map(TypeInformation::getType).orElse(null);
|
||||
Class<?> rawValueType = type.getMapValueType().map(TypeInformation::getType).orElse(null);
|
||||
TypeInformation<?> keyType = type.getComponentType();
|
||||
TypeInformation<?> valueType = type.getMapValueType();
|
||||
|
||||
Class<?> rawKeyType = keyType != null ? keyType.getType() : null;
|
||||
Class<?> rawValueType = valueType != null ? valueType.getType() : null;
|
||||
|
||||
Map<String, Object> sourceMap = asMap(bson);
|
||||
Map<Object, Object> map = CollectionFactory.createMap(mapType, rawKeyType, sourceMap.keySet().size());
|
||||
|
||||
if (!DBRef.class.equals(rawValueType) && isCollectionOfDbRefWhereBulkFetchIsPossible(sourceMap.values())) {
|
||||
bulkReadAndConvertDBRefMapIntoTarget(valueType.orElse(null), rawValueType, sourceMap, map);
|
||||
bulkReadAndConvertDBRefMapIntoTarget(valueType, rawValueType, sourceMap, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -961,12 +1018,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Object key = potentiallyUnescapeMapKey(entry.getKey());
|
||||
|
||||
if (rawKeyType != null) {
|
||||
if (rawKeyType != null && !rawKeyType.isAssignableFrom(key.getClass())) {
|
||||
key = conversionService.convert(key, rawKeyType);
|
||||
}
|
||||
|
||||
Object value = entry.getValue();
|
||||
TypeInformation<?> defaultedValueType = valueType.orElse(ClassTypeInformation.OBJECT);
|
||||
TypeInformation<?> defaultedValueType = valueType != null ? valueType : ClassTypeInformation.OBJECT;
|
||||
|
||||
if (value instanceof Document) {
|
||||
map.put(key, read(defaultedValueType, (Document) value, path));
|
||||
@@ -976,7 +1033,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
map.put(key, DBRef.class.equals(rawValueType) ? value
|
||||
: readAndConvertDBRef((DBRef) value, defaultedValueType, ObjectPath.ROOT, rawValueType));
|
||||
} else if (value instanceof List) {
|
||||
map.put(key, readCollectionOrArray(valueType.orElse(ClassTypeInformation.LIST), (List) value, path));
|
||||
map.put(key,
|
||||
readCollectionOrArray(valueType != null ? valueType : ClassTypeInformation.LIST, (List) value, path));
|
||||
} else {
|
||||
map.put(key, getPotentiallyConvertedSimpleRead(value, rawValueType));
|
||||
}
|
||||
@@ -1098,7 +1156,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
if (obj instanceof Map) {
|
||||
|
||||
Map<Object, Object> converted = new LinkedHashMap<>(((Map) obj).size(), 1);
|
||||
Document result = new Document();
|
||||
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
|
||||
@@ -1199,7 +1256,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private class MongoDbPropertyValueProvider implements PropertyValueProvider<MongoPersistentProperty> {
|
||||
class MongoDbPropertyValueProvider implements PropertyValueProvider<MongoPersistentProperty> {
|
||||
|
||||
private final DocumentAccessor source;
|
||||
private final SpELExpressionEvaluator evaluator;
|
||||
@@ -1224,17 +1281,39 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
|
||||
* {@link ObjectPath}.
|
||||
*
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @param evaluator must not be {@literal null}.
|
||||
* @param path can be {@literal null}.
|
||||
*/
|
||||
public MongoDbPropertyValueProvider(DocumentAccessor accessor, SpELExpressionEvaluator evaluator, ObjectPath path) {
|
||||
|
||||
Assert.notNull(accessor, "DocumentAccessor must no be null!");
|
||||
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!");
|
||||
Assert.notNull(path, "ObjectPath must not be null!");
|
||||
|
||||
this.source = accessor;
|
||||
this.evaluator = evaluator;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
public <T> Optional<T> getPropertyValue(MongoPersistentProperty property) {
|
||||
return Optional
|
||||
public <T> T getPropertyValue(MongoPersistentProperty property) {
|
||||
|
||||
.ofNullable(property.getSpelExpression()//
|
||||
.map(evaluator::evaluate)//
|
||||
.orElseGet(() -> source.get(property)))//
|
||||
.map(it -> readValue(it, property.getTypeInformation(), path));
|
||||
String expression = property.getSpelExpression();
|
||||
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return readValue(value, property.getTypeInformation(), path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1275,7 +1354,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T readValue(Object value, TypeInformation<?> type, ObjectPath path) {
|
||||
<T> T readValue(Object value, TypeInformation<?> type, ObjectPath path) {
|
||||
|
||||
Class<?> rawType = type.getType();
|
||||
|
||||
@@ -1301,8 +1380,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return (T) dbref;
|
||||
}
|
||||
|
||||
Object object = dbref == null ? null : path.getPathItem(dbref.getId(), dbref.getCollectionName());
|
||||
return (T) (object != null ? object : readAndConvertDBRef(dbref, type, path, rawType));
|
||||
T object = dbref == null ? null : path.getPathItem(dbref.getId(), dbref.getCollectionName(), (Class<T>) rawType);
|
||||
return object != null ? object : readAndConvertDBRef(dbref, type, path, rawType);
|
||||
}
|
||||
|
||||
private <T> T readAndConvertDBRef(DBRef dbref, TypeInformation<?> type, ObjectPath path, final Class<?> rawType) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -38,10 +37,9 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.MongoRegexCreator;
|
||||
import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
import org.springframework.data.repository.core.support.ExampleMatcherAccessor;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.data.util.Optionals;
|
||||
import org.springframework.data.support.ExampleMatcherAccessor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -50,24 +48,18 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @since 1.8
|
||||
*/
|
||||
public class MongoExampleMapper {
|
||||
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final MongoConverter converter;
|
||||
private final Map<StringMatcher, Type> stringMatcherPartMapping = new HashMap<StringMatcher, Type>();
|
||||
|
||||
public MongoExampleMapper(MongoConverter converter) {
|
||||
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
|
||||
stringMatcherPartMapping.put(StringMatcher.EXACT, Type.SIMPLE_PROPERTY);
|
||||
stringMatcherPartMapping.put(StringMatcher.CONTAINING, Type.CONTAINING);
|
||||
stringMatcherPartMapping.put(StringMatcher.STARTING, Type.STARTING_WITH);
|
||||
stringMatcherPartMapping.put(StringMatcher.ENDING, Type.ENDING_WITH);
|
||||
stringMatcherPartMapping.put(StringMatcher.REGEX, Type.REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,8 +91,12 @@ public class MongoExampleMapper {
|
||||
|
||||
Document reference = (Document) converter.convertToMongoType(example.getProbe());
|
||||
|
||||
if(entity.getIdProperty().isPresent() && !entity.getIdentifierAccessor(example.getProbe()).getIdentifier().isPresent()) {
|
||||
reference.remove(entity.getIdProperty().get().getFieldName());
|
||||
if (entity.getIdProperty() != null) {
|
||||
|
||||
Object identifier = entity.getIdentifierAccessor(example.getProbe()).getIdentifier();
|
||||
if (identifier == null) {
|
||||
reference.remove(entity.getIdProperty().getFieldName());
|
||||
}
|
||||
}
|
||||
|
||||
ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher());
|
||||
@@ -153,7 +149,7 @@ public class MongoExampleMapper {
|
||||
while (parts.hasNext()) {
|
||||
|
||||
String part = parts.next();
|
||||
MongoPersistentProperty prop = entity.getPersistentProperty(part).orElse(null);
|
||||
MongoPersistentProperty prop = entity.getPersistentProperty(part);
|
||||
|
||||
if (prop == null) {
|
||||
|
||||
@@ -249,7 +245,7 @@ public class MongoExampleMapper {
|
||||
|
||||
Document document = new Document();
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(StringMatcher.DEFAULT, stringMatcher)) {
|
||||
if (StringMatcher.DEFAULT == stringMatcher) {
|
||||
|
||||
if (ignoreCase) {
|
||||
document.put("$regex", Pattern.quote((String) entry.getValue()));
|
||||
@@ -257,8 +253,8 @@ public class MongoExampleMapper {
|
||||
}
|
||||
} else {
|
||||
|
||||
Type type = stringMatcherPartMapping.get(stringMatcher);
|
||||
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), type);
|
||||
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(),
|
||||
toMatchMode(stringMatcher));
|
||||
document.put("$regex", expression);
|
||||
entry.setValue(document);
|
||||
}
|
||||
@@ -267,4 +263,29 @@ public class MongoExampleMapper {
|
||||
document.put("$options", "i");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link MatchMode} for the given {@link StringMatcher}.
|
||||
*
|
||||
* @param matcher must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static MatchMode toMatchMode(StringMatcher matcher) {
|
||||
|
||||
switch (matcher) {
|
||||
case CONTAINING:
|
||||
return MatchMode.CONTAINING;
|
||||
case STARTING:
|
||||
return MatchMode.STARTING_WITH;
|
||||
case ENDING:
|
||||
return MatchMode.ENDING_WITH;
|
||||
case EXACT:
|
||||
return MatchMode.EXACT;
|
||||
case REGEX:
|
||||
return MatchMode.REGEX;
|
||||
case DEFAULT:
|
||||
default:
|
||||
return MatchMode.DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -29,36 +31,39 @@ import org.springframework.util.StringUtils;
|
||||
* when resolving more nested objects. This allows to avoid re-resolving object instances that are logically equivalent
|
||||
* to already resolved ones.
|
||||
* <p>
|
||||
* An immutable ordered set of target objects for {@link Document} to {@link Object} conversions. Object paths can be
|
||||
* constructed by the {@link #toObjectPath(Object)} method and extended via {@link #push(Object)}.
|
||||
*
|
||||
* An immutable ordered set of target objects for {@link org.bson.Document} to {@link Object} conversions. Object paths
|
||||
* can be extended via {@link #push(Object, MongoPersistentEntity, Object)}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
class ObjectPath {
|
||||
|
||||
public static final ObjectPath ROOT = new ObjectPath();
|
||||
static final ObjectPath ROOT = new ObjectPath();
|
||||
|
||||
private final List<ObjectPathItem> items;
|
||||
private final ObjectPathItem[] items;
|
||||
|
||||
private ObjectPath() {
|
||||
this.items = Collections.emptyList();
|
||||
this.items = new ObjectPathItem[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ObjectPath} from the given parent {@link ObjectPath} by adding the provided
|
||||
* {@link ObjectPathItem} to it.
|
||||
*
|
||||
*
|
||||
* @param parent can be {@literal null}.
|
||||
* @param item
|
||||
*/
|
||||
private ObjectPath(ObjectPath parent, ObjectPath.ObjectPathItem item) {
|
||||
|
||||
List<ObjectPath.ObjectPathItem> items = new ArrayList<ObjectPath.ObjectPathItem>(parent.items);
|
||||
items.add(item);
|
||||
ObjectPathItem[] items = new ObjectPathItem[parent.items.length + 1];
|
||||
System.arraycopy(parent.items, 0, items, 0, parent.items.length);
|
||||
items[parent.items.length] = item;
|
||||
|
||||
this.items = Collections.unmodifiableList(items);
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,9 +72,9 @@ class ObjectPath {
|
||||
* @param object must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param id must not be {@literal null}.
|
||||
* @return
|
||||
* @return new instance of {@link ObjectPath}.
|
||||
*/
|
||||
public ObjectPath push(Object object, MongoPersistentEntity<?> entity, Object id) {
|
||||
ObjectPath push(Object object, MongoPersistentEntity<?> entity, Object id) {
|
||||
|
||||
Assert.notNull(object, "Object must not be null!");
|
||||
Assert.notNull(entity, "MongoPersistentEntity must not be null!");
|
||||
@@ -79,14 +84,16 @@ class ObjectPath {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object with the given id and stored in the given collection if it's contained in the {@link ObjectPath}
|
||||
* .
|
||||
*
|
||||
* Returns the object with the given id and stored in the given collection if it's contained in the
|
||||
* {@link ObjectPath}.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param collection must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @deprecated use {@link #getPathItem(Object, String, Class)}.
|
||||
*/
|
||||
public Object getPathItem(Object id, String collection) {
|
||||
@Deprecated
|
||||
Object getPathItem(Object id, String collection) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null!");
|
||||
Assert.hasText(collection, "Collection name must not be null!");
|
||||
@@ -95,11 +102,7 @@ class ObjectPath {
|
||||
|
||||
Object object = item.getObject();
|
||||
|
||||
if (object == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.getIdValue() == null) {
|
||||
if (object == null || item.getIdValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -112,29 +115,62 @@ class ObjectPath {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current object of the {@link ObjectPath} or {@literal null} if the path is empty.
|
||||
*
|
||||
* @return
|
||||
* Get the object with given {@literal id}, stored in the {@literal collection} that is assignable to the given
|
||||
* {@literal type} or {@literal null} if no match found.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param collection must not be {@literal null} or empty.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal null} when no match found.
|
||||
* @since 2.0
|
||||
*/
|
||||
public Optional<Object> getCurrentObject() {
|
||||
return items.isEmpty() ? Optional.empty() : Optional.of(items.get(items.size() - 1).getObject());
|
||||
<T> T getPathItem(Object id, String collection, Class<T> type) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null!");
|
||||
Assert.hasText(collection, "Collection name must not be null!");
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
|
||||
for (ObjectPathItem item : items) {
|
||||
|
||||
Object object = item.getObject();
|
||||
|
||||
if (object == null || item.getIdValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (collection.equals(item.getCollection()) && id.equals(item.getIdValue())
|
||||
&& ClassUtils.isAssignable(type, object.getClass())) {
|
||||
return type.cast(object);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Returns the current object of the {@link ObjectPath} or {@literal null} if the path is empty.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object getCurrentObject() {
|
||||
return items.length == 0 ? null : items[items.length - 1].getObject();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
if (items.isEmpty()) {
|
||||
if (items.length == 0) {
|
||||
return "[empty]";
|
||||
}
|
||||
|
||||
List<String> strings = new ArrayList<String>(items.size());
|
||||
List<String> strings = new ArrayList<>(items.length);
|
||||
|
||||
for (ObjectPathItem item : items) {
|
||||
strings.add(item.object.toString());
|
||||
strings.add(ObjectUtils.nullSafeToString(item.object));
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(strings, " -> ");
|
||||
@@ -142,40 +178,16 @@ class ObjectPath {
|
||||
|
||||
/**
|
||||
* An item in an {@link ObjectPath}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
private static class ObjectPathItem {
|
||||
|
||||
private final Object object;
|
||||
private final Object idValue;
|
||||
private final String collection;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ObjectPathItem}.
|
||||
*
|
||||
* @param object
|
||||
* @param idValue
|
||||
* @param collection
|
||||
*/
|
||||
ObjectPathItem(Object object, Object idValue, String collection) {
|
||||
|
||||
this.object = object;
|
||||
this.idValue = idValue;
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public Object getIdValue() {
|
||||
return idValue;
|
||||
}
|
||||
|
||||
public String getCollection() {
|
||||
return collection;
|
||||
}
|
||||
Object object;
|
||||
Object idValue;
|
||||
String collection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
@@ -122,7 +122,7 @@ public class QueryMapper {
|
||||
if (Query.isRestrictedTypeKey(key)) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<?>> restrictedTypes = (Set<Class<?>>) BsonUtils.get(query, key);
|
||||
Set<Class<?>> restrictedTypes = BsonUtils.get(query, key);
|
||||
this.converter.getTypeMapper().writeTypeRestrictions(result, restrictedTypes);
|
||||
|
||||
continue;
|
||||
@@ -318,11 +318,11 @@ public class QueryMapper {
|
||||
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
|
||||
List<Object> ids = new ArrayList<Object>();
|
||||
for (Object id : (Iterable<?>) valueDbo.get(inKey)) {
|
||||
ids.add(convertId(id).get());
|
||||
ids.add(convertId(id));
|
||||
}
|
||||
resultDbo.put(inKey, ids);
|
||||
} else if (valueDbo.containsField("$ne")) {
|
||||
resultDbo.put("$ne", convertId(valueDbo.get("$ne")).get());
|
||||
resultDbo.put("$ne", convertId(valueDbo.get("$ne")));
|
||||
} else {
|
||||
return getMappedObject(resultDbo, Optional.empty());
|
||||
}
|
||||
@@ -337,18 +337,18 @@ public class QueryMapper {
|
||||
String inKey = valueDbo.containsKey("$in") ? "$in" : "$nin";
|
||||
List<Object> ids = new ArrayList<Object>();
|
||||
for (Object id : (Iterable<?>) valueDbo.get(inKey)) {
|
||||
ids.add(convertId(id).orElse(null));
|
||||
ids.add(convertId(id));
|
||||
}
|
||||
resultDbo.put(inKey, ids);
|
||||
} else if (valueDbo.containsKey("$ne")) {
|
||||
resultDbo.put("$ne", convertId(valueDbo.get("$ne")).orElse(null));
|
||||
resultDbo.put("$ne", convertId(valueDbo.get("$ne")));
|
||||
} else {
|
||||
return getMappedObject(resultDbo, Optional.empty());
|
||||
}
|
||||
return resultDbo;
|
||||
|
||||
} else {
|
||||
return convertId(value).orElse(null);
|
||||
return convertId(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ public class QueryMapper {
|
||||
|
||||
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
|
||||
return entity.hasIdProperty() && (type.equals(DBRef.class)
|
||||
|| entity.getIdProperty().map(it -> it.getActualType().isAssignableFrom(type)).orElse(false));
|
||||
|| entity.getRequiredIdProperty().getActualType().isAssignableFrom(type));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,7 +461,7 @@ public class QueryMapper {
|
||||
if (source instanceof DBRef) {
|
||||
|
||||
DBRef ref = (DBRef) source;
|
||||
return new DBRef(ref.getCollectionName(), convertId(ref.getId()).get());
|
||||
return new DBRef(ref.getCollectionName(), convertId(ref.getId()));
|
||||
}
|
||||
|
||||
if (source instanceof Iterable) {
|
||||
@@ -537,31 +537,28 @@ public class QueryMapper {
|
||||
return converter.toDBRef(source, property);
|
||||
}
|
||||
|
||||
private Optional<Object> convertId(Object id) {
|
||||
return convertId(Optional.ofNullable(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public Optional<Object> convertId(Optional<Object> id) {
|
||||
public Object convertId(Object id) {
|
||||
|
||||
return id.map(it -> {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (it instanceof String) {
|
||||
return ObjectId.isValid(it.toString()) ? conversionService.convert(it, ObjectId.class) : it;
|
||||
}
|
||||
if (id instanceof String) {
|
||||
return ObjectId.isValid(id.toString()) ? conversionService.convert(id, ObjectId.class) : id;
|
||||
}
|
||||
|
||||
try {
|
||||
return conversionService.canConvert(it.getClass(), ObjectId.class)
|
||||
? conversionService.convert(it, ObjectId.class) : delegateConvertToMongoType(it, null);
|
||||
} catch (ConversionException o_O) {
|
||||
return delegateConvertToMongoType(it, null);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
|
||||
: delegateConvertToMongoType(id, null);
|
||||
} catch (ConversionException o_O) {
|
||||
return delegateConvertToMongoType(id, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,9 +833,13 @@ public class QueryMapper {
|
||||
@Override
|
||||
public boolean isIdField() {
|
||||
|
||||
return entity.getIdProperty()//
|
||||
.map(it -> it.getName().equals(name) || it.getFieldName().equals(name))//
|
||||
.orElseGet(() -> DEFAULT_ID_NAMES.contains(name));
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
|
||||
if (idProperty != null) {
|
||||
return idProperty.getName().equals(name) || idProperty.getFieldName().equals(name);
|
||||
}
|
||||
|
||||
return DEFAULT_ID_NAMES.contains(name);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -857,7 +858,7 @@ public class QueryMapper {
|
||||
@Override
|
||||
public MongoPersistentEntity<?> getPropertyEntity() {
|
||||
MongoPersistentProperty property = getProperty();
|
||||
return property == null ? null : mappingContext.getPersistentEntity(property).orElse(null);
|
||||
return property == null ? null : mappingContext.getPersistentEntity(property);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -883,15 +884,15 @@ public class QueryMapper {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private final Association<MongoPersistentProperty> findAssociation() {
|
||||
private Association<MongoPersistentProperty> findAssociation() {
|
||||
|
||||
if (this.path != null) {
|
||||
for (MongoPersistentProperty p : this.path) {
|
||||
|
||||
Optional<Association<MongoPersistentProperty>> association = p.getAssociation();
|
||||
Association<MongoPersistentProperty> association = p.getAssociation();
|
||||
|
||||
if (association.isPresent()) {
|
||||
return association.get();
|
||||
if (association != null) {
|
||||
return association;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
@@ -24,7 +22,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
|
||||
/**
|
||||
* Internal API to trigger the resolution of properties.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@@ -33,13 +31,13 @@ interface ValueResolver {
|
||||
/**
|
||||
* Resolves the value for the given {@link MongoPersistentProperty} within the given {@link Document} using the given
|
||||
* {@link SpELExpressionEvaluator} and {@link ObjectPath}.
|
||||
*
|
||||
*
|
||||
* @param prop
|
||||
* @param bson
|
||||
* @param evaluator
|
||||
* @param parent
|
||||
* @return
|
||||
*/
|
||||
Optional<Object> getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
|
||||
Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
|
||||
ObjectPath path);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.index;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,33 +13,31 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
|
||||
/**
|
||||
* Index operations on a collection.
|
||||
*
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public interface IndexOperations {
|
||||
|
||||
/**
|
||||
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
|
||||
* class. If not it will be created.
|
||||
*
|
||||
*
|
||||
* @param indexDefinition must not be {@literal null}.
|
||||
*/
|
||||
String ensureIndex(IndexDefinition indexDefinition);
|
||||
|
||||
/**
|
||||
* Drops an index from this collection.
|
||||
*
|
||||
*
|
||||
* @param name name of index to drop
|
||||
*/
|
||||
void dropIndex(String name);
|
||||
@@ -51,7 +49,7 @@ public interface IndexOperations {
|
||||
|
||||
/**
|
||||
* Returns the index information on the collection.
|
||||
*
|
||||
*
|
||||
* @return index information on the collection
|
||||
*/
|
||||
List<IndexInfo> getIndexInfo();
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,12 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core;
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
/**
|
||||
* TODO: Revisit for a better pattern.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface IndexOperationsProvider {
|
||||
@@ -28,8 +28,6 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContextEvent;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.UncategorizedMongoDbException;
|
||||
import org.springframework.data.mongodb.core.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.IndexOperationsProvider;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
@@ -131,7 +129,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
|
||||
|
||||
private void checkForAndCreateIndexes(MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (entity.findAnnotation(Document.class).isPresent()) {
|
||||
if (entity.isAnnotationPresent(Document.class)) {
|
||||
for (IndexDefinitionHolder indexToCreate : indexResolver.resolveIndexFor(entity.getTypeInformation())) {
|
||||
createIndex(indexToCreate);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -33,7 +32,7 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.TextIndexIncludeOptions.IncludeStrategy;
|
||||
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexDefinitionBuilder;
|
||||
import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexedFieldSpec;
|
||||
@@ -94,7 +93,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
public List<IndexDefinitionHolder> resolveIndexForEntity(final MongoPersistentEntity<?> root) {
|
||||
|
||||
Assert.notNull(root, "Index cannot be resolved for given 'null' entity.");
|
||||
Document document = root.findAnnotation(Document.class).orElseThrow(() -> new IllegalArgumentException("Given entity is not collection root."));
|
||||
Document document = root.findAnnotation(Document.class);
|
||||
Assert.notNull(document, "Given entity is not collection root.");
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
@@ -255,20 +254,20 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
|
||||
}
|
||||
|
||||
Optional<TextIndexed> indexed = persistentProperty.findAnnotation(TextIndexed.class);
|
||||
TextIndexed indexed = persistentProperty.findAnnotation(TextIndexed.class);
|
||||
|
||||
if (includeOptions.isForce() || indexed.isPresent()|| persistentProperty.isEntity()) {
|
||||
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
|
||||
|
||||
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "")
|
||||
+ persistentProperty.getFieldName();
|
||||
|
||||
Float weight = indexed.isPresent() ? indexed.get().weight()
|
||||
Float weight = indexed != null ? indexed.weight()
|
||||
: (includeOptions.getParentFieldSpec() != null ? includeOptions.getParentFieldSpec().getWeight() : 1.0F);
|
||||
|
||||
if (persistentProperty.isEntity()) {
|
||||
|
||||
TextIndexIncludeOptions optionsForNestedType = includeOptions;
|
||||
if (!IncludeStrategy.FORCE.equals(includeOptions.getStrategy()) && indexed.isPresent()) {
|
||||
if (!IncludeStrategy.FORCE.equals(includeOptions.getStrategy()) && indexed != null) {
|
||||
optionsForNestedType = new TextIndexIncludeOptions(IncludeStrategy.FORCE,
|
||||
new TextIndexedFieldSpec(propertyDotPath, weight));
|
||||
}
|
||||
@@ -282,7 +281,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
LOGGER.info(String.format("Potentially invalid index structure discovered. Breaking operation for %s.",
|
||||
entity.getName()), e);
|
||||
}
|
||||
} else if (includeOptions.isForce() || indexed.isPresent()) {
|
||||
} else if (includeOptions.isForce() || indexed != null) {
|
||||
indexDefinitionBuilder.onField(propertyDotPath, weight);
|
||||
}
|
||||
}
|
||||
@@ -304,18 +303,18 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
Optional<CompoundIndexes> indexes = entity.findAnnotation(CompoundIndexes.class);
|
||||
CompoundIndexes indexes = entity.findAnnotation(CompoundIndexes.class);
|
||||
|
||||
if (indexes.isPresent()) {
|
||||
for (CompoundIndex index : indexes.get().value()) {
|
||||
if (indexes != null) {
|
||||
for (CompoundIndex index : indexes.value()) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
}
|
||||
|
||||
Optional<CompoundIndex> index = entity.findAnnotation(CompoundIndex.class);
|
||||
CompoundIndex index = entity.findAnnotation(CompoundIndex.class);
|
||||
|
||||
if (index.isPresent()) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index.get(), entity));
|
||||
if (index != null) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
|
||||
return indexDefinitions;
|
||||
@@ -382,33 +381,33 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
protected IndexDefinitionHolder createIndexDefinition(String dotPath, String collection,
|
||||
MongoPersistentProperty persitentProperty) {
|
||||
|
||||
Optional<Indexed> index = persitentProperty.findAnnotation(Indexed.class);
|
||||
Indexed index = persitentProperty.findAnnotation(Indexed.class);
|
||||
|
||||
if(!index.isPresent()){
|
||||
if (index == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Index indexDefinition = new Index().on(dotPath,
|
||||
IndexDirection.ASCENDING.equals(index.get().direction()) ? Sort.Direction.ASC : Sort.Direction.DESC);
|
||||
IndexDirection.ASCENDING.equals(index.direction()) ? Sort.Direction.ASC : Sort.Direction.DESC);
|
||||
|
||||
if (!index.get().useGeneratedName()) {
|
||||
indexDefinition.named(pathAwareIndexName(index.get().name(), dotPath, persitentProperty));
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persitentProperty));
|
||||
}
|
||||
|
||||
if (index.get().unique()) {
|
||||
if (index.unique()) {
|
||||
indexDefinition.unique();
|
||||
}
|
||||
|
||||
if (index.get().sparse()) {
|
||||
if (index.sparse()) {
|
||||
indexDefinition.sparse();
|
||||
}
|
||||
|
||||
if (index.get().background()) {
|
||||
if (index.background()) {
|
||||
indexDefinition.background();
|
||||
}
|
||||
|
||||
if (index.get().expireAfterSeconds() >= 0) {
|
||||
indexDefinition.expire(index.get().expireAfterSeconds(), TimeUnit.SECONDS);
|
||||
if (index.expireAfterSeconds() >= 0) {
|
||||
indexDefinition.expire(index.expireAfterSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
|
||||
@@ -426,21 +425,21 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
protected IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection,
|
||||
MongoPersistentProperty persistentProperty) {
|
||||
|
||||
Optional<GeoSpatialIndexed> index = persistentProperty.findAnnotation(GeoSpatialIndexed.class);
|
||||
GeoSpatialIndexed index = persistentProperty.findAnnotation(GeoSpatialIndexed.class);
|
||||
|
||||
if(!index.isPresent()) {
|
||||
if (index == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GeospatialIndex indexDefinition = new GeospatialIndex(dotPath);
|
||||
indexDefinition.withBits(index.get().bits());
|
||||
indexDefinition.withMin(index.get().min()).withMax(index.get().max());
|
||||
indexDefinition.withBits(index.bits());
|
||||
indexDefinition.withMin(index.min()).withMax(index.max());
|
||||
|
||||
if (!index.get().useGeneratedName()) {
|
||||
indexDefinition.named(pathAwareIndexName(index.get().name(), dotPath, persistentProperty));
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persistentProperty));
|
||||
}
|
||||
|
||||
indexDefinition.typed(index.get().type()).withBucketSize(index.get().bucketSize()).withAdditionalField(index.get().additionalField());
|
||||
indexDefinition.typed(index.type()).withBucketSize(index.bucketSize()).withAdditionalField(index.additionalField());
|
||||
|
||||
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,12 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -58,5 +53,4 @@ public interface ReactiveIndexOperations {
|
||||
* @return index information on the collection
|
||||
*/
|
||||
Flux<IndexInfo> getIndexInfo();
|
||||
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -32,7 +31,7 @@ import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mongodb.MongoCollectionUtils;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.Expression;
|
||||
@@ -47,7 +46,7 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* MongoDB specific {@link MongoPersistentEntity} implementation that adds Mongo specific meta-data such as the
|
||||
* collection name and the like.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
@@ -69,23 +68,30 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
/**
|
||||
* Creates a new {@link BasicMongoPersistentEntity} with the given {@link TypeInformation}. Will default the
|
||||
* collection name to the entities simple type name.
|
||||
*
|
||||
*
|
||||
* @param typeInformation must not be {@literal null}.
|
||||
*/
|
||||
public BasicMongoPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
|
||||
super(typeInformation, Optional.of(MongoPersistentPropertyComparator.INSTANCE));
|
||||
super(typeInformation, MongoPersistentPropertyComparator.INSTANCE);
|
||||
|
||||
Class<?> rawType = typeInformation.getType();
|
||||
String fallback = MongoCollectionUtils.getPreferredCollectionName(rawType);
|
||||
|
||||
Optional<Document> document = this.findAnnotation(Document.class);
|
||||
|
||||
this.expression = document.map(it -> detectExpression(it)).orElse(null);
|
||||
this.context = new StandardEvaluationContext();
|
||||
this.collection = document.filter(it -> StringUtils.hasText(it.collection())).map(it -> it.collection())
|
||||
.orElse(fallback);
|
||||
this.language = document.filter(it -> StringUtils.hasText(it.language())).map(it -> it.language()).orElse("");
|
||||
|
||||
if (this.isAnnotationPresent(Document.class)) {
|
||||
Document document = this.findAnnotation(Document.class);
|
||||
|
||||
this.collection = StringUtils.hasText(document.collection()) ? document.collection() : fallback;
|
||||
this.language = StringUtils.hasText(document.language()) ? document.language() : "";
|
||||
this.expression = document != null ? detectExpression(document) : null;
|
||||
} else {
|
||||
|
||||
this.collection = fallback;
|
||||
this.language = "";
|
||||
this.expression = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -122,7 +128,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
*/
|
||||
@Override
|
||||
public MongoPersistentProperty getTextScoreProperty() {
|
||||
return getPersistentProperty(TextScore.class).orElse(null);
|
||||
return getPersistentProperty(TextScore.class);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -159,7 +165,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
|
||||
/**
|
||||
* {@link Comparator} implementation inspecting the {@link MongoPersistentProperty}'s order.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static enum MongoPersistentPropertyComparator implements Comparator<MongoPersistentProperty> {
|
||||
@@ -189,7 +195,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
* that is annotated with @see {@link Id}. The property id is updated according to the following rules: 1) An id
|
||||
* property which is defined explicitly takes precedence over an implicitly defined id property. 2) In case of any
|
||||
* ambiguity a @see {@link MappingException} is thrown.
|
||||
*
|
||||
*
|
||||
* @param property - the new id property candidate
|
||||
* @return
|
||||
*/
|
||||
@@ -202,45 +208,47 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<MongoPersistentProperty> currentIdProperty = getIdProperty();
|
||||
MongoPersistentProperty currentIdProperty = getIdProperty();
|
||||
|
||||
return currentIdProperty.map(it -> {
|
||||
boolean currentIdPropertyIsSet = currentIdProperty != null;
|
||||
@SuppressWarnings("null")
|
||||
boolean currentIdPropertyIsExplicit = currentIdPropertyIsSet ? currentIdProperty.isExplicitIdProperty() : false;
|
||||
boolean newIdPropertyIsExplicit = property.isExplicitIdProperty();
|
||||
|
||||
boolean currentIdPropertyIsExplicit = it.isExplicitIdProperty();
|
||||
boolean newIdPropertyIsExplicit = property.isExplicitIdProperty();
|
||||
Optional<Field> currentIdPropertyField = it.getField();
|
||||
if (!currentIdPropertyIsSet) {
|
||||
return property;
|
||||
|
||||
if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"Attempt to add explicit id property %s but already have an property %s registered "
|
||||
+ "as explicit id. Check your mapping configuration!",
|
||||
property.getField(), currentIdPropertyField));
|
||||
}
|
||||
|
||||
} else if (newIdPropertyIsExplicit && !currentIdPropertyIsExplicit) {
|
||||
// explicit id property takes precedence over implicit id property
|
||||
return property;
|
||||
@SuppressWarnings("null")
|
||||
Field currentIdPropertyField = currentIdProperty.getField();
|
||||
|
||||
} else if (!newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
|
||||
// no id property override - current property is explicitly defined
|
||||
if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
|
||||
throw new MappingException(
|
||||
String.format("Attempt to add explicit id property %s but already have an property %s registered "
|
||||
+ "as explicit id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
|
||||
|
||||
} else {
|
||||
throw new MappingException(
|
||||
String.format("Attempt to add id property %s but already have an property %s registered "
|
||||
+ "as id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
|
||||
}
|
||||
} else if (newIdPropertyIsExplicit && !currentIdPropertyIsExplicit) {
|
||||
// explicit id property takes precedence over implicit id property
|
||||
return property;
|
||||
|
||||
return null;
|
||||
} else if (!newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
|
||||
// no id property override - current property is explicitly defined
|
||||
|
||||
}).orElse(property);
|
||||
} else {
|
||||
throw new MappingException(
|
||||
String.format("Attempt to add id property %s but already have an property %s registered "
|
||||
+ "as id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SpEL {@link Expression} frór the collection String expressed in the given {@link Document} annotation if
|
||||
* present or {@literal null} otherwise. Will also return {@literal null} it the collection {@link String} evaluates
|
||||
* to a {@link LiteralExpression} (indicating that no subsequent evaluation is necessary).
|
||||
*
|
||||
*
|
||||
* @param document can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
@@ -264,7 +272,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
/**
|
||||
* Handler to collect {@link MongoPersistentProperty} instances and check that each of them is mapped to a distinct
|
||||
* field name.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AssertFieldNameUniquenessHandler
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core.mapping;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
@@ -27,19 +26,20 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* MongoDB specific {@link org.springframework.data.mapping.MongoPersistentProperty} implementation.
|
||||
*
|
||||
* MongoDB specific {@link org.springframework.data.mapping.PersistentProperty} implementation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty>
|
||||
implements MongoPersistentProperty {
|
||||
@@ -65,7 +65,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicMongoPersistentProperty}.
|
||||
*
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
@@ -86,7 +86,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
/**
|
||||
* Also considers fields as id that are of supported id type and name.
|
||||
*
|
||||
*
|
||||
* @see #SUPPORTED_ID_PROPERTY_NAMES
|
||||
* @see #SUPPORTED_ID_TYPES
|
||||
*/
|
||||
@@ -113,14 +113,14 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
/**
|
||||
* Returns the key to be used to store the value of the property inside a Mongo {@link org.bson.Document}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFieldName() {
|
||||
|
||||
if (isIdProperty()) {
|
||||
|
||||
if (!getOwner().getIdProperty().isPresent()) {
|
||||
if (getOwner().getIdProperty() == null) {
|
||||
return ID_FIELD_NAME;
|
||||
}
|
||||
|
||||
@@ -154,13 +154,10 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
private String getAnnotatedFieldName() {
|
||||
|
||||
Optional<org.springframework.data.mongodb.core.mapping.Field> annotation = findAnnotation(
|
||||
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(
|
||||
org.springframework.data.mongodb.core.mapping.Field.class);
|
||||
|
||||
return annotation//
|
||||
.filter(it -> StringUtils.hasText(it.value()))//
|
||||
.map(it -> it.value())//
|
||||
.orElse(null);
|
||||
return annotation != null ? annotation.value() : null;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -169,10 +166,10 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
*/
|
||||
public int getFieldOrder() {
|
||||
|
||||
Optional<org.springframework.data.mongodb.core.mapping.Field> annotation = findAnnotation(
|
||||
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(
|
||||
org.springframework.data.mongodb.core.mapping.Field.class);
|
||||
|
||||
return annotation.map(it -> it.order()).orElse(Integer.MAX_VALUE);
|
||||
return annotation != null ? annotation.order() : Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -197,7 +194,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getDBRef()
|
||||
*/
|
||||
public DBRef getDBRef() {
|
||||
return findAnnotation(DBRef.class).orElse(null);
|
||||
return findAnnotation(DBRef.class);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.mapreduce;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
|
||||
/**
|
||||
* Collects the parameters required to perform a group operation on a collection. The query condition and the input
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
|
||||
import com.mongodb.MapReduceCommand;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 the original author or authors.
|
||||
* Copyright 2010-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,13 +18,11 @@ package org.springframework.data.mongodb.core.query;
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Custom {@link Query} implementation to setup a basic query from some arbitrary JSON query string.
|
||||
*
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
@@ -35,6 +33,7 @@ import com.mongodb.util.JSON;
|
||||
public class BasicQuery extends Query {
|
||||
|
||||
private final Document queryObject;
|
||||
|
||||
private Document fieldsObject;
|
||||
private Document sortObject;
|
||||
|
||||
@@ -53,7 +52,7 @@ public class BasicQuery extends Query {
|
||||
* @param queryObject may be {@literal null}.
|
||||
*/
|
||||
public BasicQuery(Document queryObject) {
|
||||
this(queryObject, null);
|
||||
this(queryObject, new Document());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,17 +63,22 @@ public class BasicQuery extends Query {
|
||||
*/
|
||||
public BasicQuery(String query, String fields) {
|
||||
|
||||
this.queryObject = query != null ? Document.parse(query) : null;
|
||||
this.fieldsObject = fields != null ? Document.parse(fields) : null;
|
||||
this.queryObject = query != null ? Document.parse(query) : new Document();
|
||||
this.fieldsObject = fields != null ? Document.parse(fields) : new Document();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicQuery} given a query {@link Document} and field specification {@link Document}.
|
||||
*
|
||||
* @param queryObject may be {@literal null}.
|
||||
* @param fieldsObject may be {@literal null}.
|
||||
* @param queryObject must not be {@literal null}.
|
||||
* @param fieldsObject must not be {@literal null}.
|
||||
* @throws IllegalArgumentException when {@code sortObject} or {@code fieldsObject} is {@literal null}.
|
||||
*/
|
||||
public BasicQuery(Document queryObject, Document fieldsObject) {
|
||||
|
||||
Assert.notNull(queryObject, "Query document must not be null");
|
||||
Assert.notNull(fieldsObject, "Field document must not be null");
|
||||
|
||||
this.queryObject = queryObject;
|
||||
this.fieldsObject = fieldsObject;
|
||||
}
|
||||
@@ -85,15 +89,25 @@ public class BasicQuery extends Query {
|
||||
*/
|
||||
@Override
|
||||
public Query addCriteria(CriteriaDefinition criteria) {
|
||||
|
||||
this.queryObject.putAll(criteria.getCriteriaObject());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Query#getQueryObject()
|
||||
*/
|
||||
@Override
|
||||
public Document getQueryObject() {
|
||||
return this.queryObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Query#getFieldsObject()
|
||||
*/
|
||||
@Override
|
||||
public Document getFieldsObject() {
|
||||
|
||||
@@ -112,31 +126,49 @@ public class BasicQuery extends Query {
|
||||
return fieldsObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Query#getSortObject()
|
||||
*/
|
||||
@Override
|
||||
public Document getSortObject() {
|
||||
|
||||
Document result = new Document();
|
||||
|
||||
if (sortObject != null) {
|
||||
result.putAll(sortObject);
|
||||
}
|
||||
|
||||
Document overrides = super.getSortObject();
|
||||
if (overrides != null) {
|
||||
result.putAll(overrides);
|
||||
}
|
||||
result.putAll(overrides);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort {@link Document}.
|
||||
*
|
||||
* @param sortObject must not be {@literal null}.
|
||||
* @throws IllegalArgumentException when {@code sortObject} is {@literal null}.
|
||||
*/
|
||||
public void setSortObject(Document sortObject) {
|
||||
|
||||
Assert.notNull(sortObject, "Sort document must not be null");
|
||||
|
||||
this.sortObject = sortObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields (projection) {@link Document}.
|
||||
*
|
||||
* @param fieldsObject must not be {@literal null}.
|
||||
* @throws IllegalArgumentException when {@code fieldsObject} is {@literal null}.
|
||||
* @since 1.6
|
||||
* @param fieldsObject
|
||||
*/
|
||||
protected void setFieldsObject(Document fieldsObject) {
|
||||
|
||||
Assert.notNull(sortObject, "Field document must not be null");
|
||||
|
||||
this.fieldsObject = fieldsObject;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
@@ -34,18 +29,24 @@ import com.mongodb.client.model.CollationCaseFirst;
|
||||
import com.mongodb.client.model.CollationMaxVariable;
|
||||
import com.mongodb.client.model.CollationStrength;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Central abstraction for MongoDB collation support. <br />
|
||||
* Allows fluent creation of a collation {@link Document} that can be used for creating collections & indexes as well as
|
||||
* querying data.
|
||||
* <p />
|
||||
* <p/>
|
||||
* <strong>NOTE:</strong> Please keep in mind that queries will only make use of an index with collation settings if the
|
||||
* query itself specifies the same collation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @author Jens Schauder
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/collation/">MongoDB Reference - Collation</a>
|
||||
* @since 2.0
|
||||
*/
|
||||
public class Collation {
|
||||
|
||||
@@ -720,8 +721,8 @@ public class Collation {
|
||||
/**
|
||||
* ICU locale abstraction for usage with MongoDB {@link Collation}.
|
||||
*
|
||||
* @since 2.0
|
||||
* @see <a href="http://site.icu-project.org">ICU - International Components for Unicode</a>
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class CollationLocale {
|
||||
@@ -17,62 +17,95 @@ package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @since 1.8
|
||||
*/
|
||||
public enum MongoRegexCreator {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* Match modes for treatment of {@link String} values.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public enum MatchMode {
|
||||
|
||||
/**
|
||||
* Store specific default.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Matches the exact string
|
||||
*/
|
||||
EXACT,
|
||||
|
||||
/**
|
||||
* Matches string starting with pattern
|
||||
*/
|
||||
STARTING_WITH,
|
||||
|
||||
/**
|
||||
* Matches string ending with pattern
|
||||
*/
|
||||
ENDING_WITH,
|
||||
|
||||
/**
|
||||
* Matches string containing pattern
|
||||
*/
|
||||
CONTAINING,
|
||||
|
||||
/**
|
||||
* Treats strings as regular expression patterns
|
||||
*/
|
||||
REGEX,
|
||||
|
||||
LIKE;
|
||||
}
|
||||
|
||||
private static final Pattern PUNCTATION_PATTERN = Pattern.compile("\\p{Punct}");
|
||||
|
||||
/**
|
||||
* Creates a regular expression String to be used with {@code $regex}.
|
||||
*
|
||||
*
|
||||
* @param source the plain String
|
||||
* @param type
|
||||
* @return {@literal source} when {@literal source} or {@literal type} is {@literal null}.
|
||||
* @param matcherType the type of matching to perform
|
||||
* @return {@literal source} when {@literal source} or {@literal matcherType} is {@literal null}.
|
||||
*/
|
||||
public String toRegularExpression(String source, Type type) {
|
||||
public String toRegularExpression(String source, MatchMode matcherType) {
|
||||
|
||||
if (type == null || source == null) {
|
||||
if (matcherType == null || source == null) {
|
||||
return source;
|
||||
}
|
||||
|
||||
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, type);
|
||||
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, matcherType);
|
||||
|
||||
switch (type) {
|
||||
switch (matcherType) {
|
||||
case STARTING_WITH:
|
||||
regex = "^" + regex;
|
||||
break;
|
||||
return String.format("^%s", regex);
|
||||
case ENDING_WITH:
|
||||
regex = regex + "$";
|
||||
break;
|
||||
return String.format("%s$", regex);
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
regex = ".*" + regex + ".*";
|
||||
break;
|
||||
case SIMPLE_PROPERTY:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
regex = "^" + regex + "$";
|
||||
return String.format(".*%s.*", regex);
|
||||
case EXACT:
|
||||
return String.format("^%s$", regex);
|
||||
default:
|
||||
return regex;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Type type) {
|
||||
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, MatchMode matcherType) {
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(Type.REGEX, type)) {
|
||||
if (MatchMode.REGEX == matcherType) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(Type.LIKE, type) && !ObjectUtils.nullSafeEquals(Type.NOT_LIKE, type)) {
|
||||
if (MatchMode.LIKE != matcherType) {
|
||||
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source;
|
||||
}
|
||||
|
||||
@@ -95,12 +128,13 @@ public enum MongoRegexCreator {
|
||||
if (leadingWildcard) {
|
||||
sb.append(".*");
|
||||
}
|
||||
|
||||
sb.append(valueToUse);
|
||||
|
||||
if (trailingWildcard) {
|
||||
sb.append(".*");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -33,10 +34,11 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
import org.springframework.data.mongodb.core.Collation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* MongoDB Query object representing criteria, projection, sorting and query hints.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
@@ -212,13 +214,14 @@ public class Query {
|
||||
Assert.notNull(additionalTypes, "AdditionalTypes must not be null");
|
||||
|
||||
restrictedTypes.add(type);
|
||||
for (Class<?> additionalType : additionalTypes) {
|
||||
restrictedTypes.add(additionalType);
|
||||
}
|
||||
restrictedTypes.addAll(Arrays.asList(additionalTypes));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the query {@link Document}.
|
||||
*/
|
||||
public Document getQueryObject() {
|
||||
|
||||
Document document = new Document();
|
||||
@@ -234,14 +237,20 @@ public class Query {
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the field {@link Document}.
|
||||
*/
|
||||
public Document getFieldsObject() {
|
||||
return this.fieldSpec == null ? null : fieldSpec.getFieldsObject();
|
||||
return this.fieldSpec == null ? new Document() : fieldSpec.getFieldsObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sort {@link Document}.
|
||||
*/
|
||||
public Document getSortObject() {
|
||||
|
||||
if (this.sort.isUnsorted()) {
|
||||
return null;
|
||||
return new Document();
|
||||
}
|
||||
|
||||
Document document = new Document();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,9 @@ import org.bson.Document;
|
||||
|
||||
/**
|
||||
* {@link Query} implementation to be used to for performing full text searches.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.6
|
||||
*/
|
||||
public class TextQuery extends Query {
|
||||
@@ -36,7 +37,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Creates new {@link TextQuery} using the the given {@code wordsAndPhrases} with {@link TextCriteria}
|
||||
*
|
||||
*
|
||||
* @param wordsAndPhrases
|
||||
* @see TextCriteria#matching(String)
|
||||
*/
|
||||
@@ -48,7 +49,7 @@ public class TextQuery extends Query {
|
||||
* Creates new {@link TextQuery} in {@code language}. <br />
|
||||
* For a full list of supported languages see the mongdodb reference manual for
|
||||
* <a href="https://docs.mongodb.org/manual/reference/text-search-languages/">Text Search Languages</a>.
|
||||
*
|
||||
*
|
||||
* @param wordsAndPhrases
|
||||
* @param language
|
||||
* @see TextCriteria#forLanguage(String)
|
||||
@@ -62,7 +63,7 @@ public class TextQuery extends Query {
|
||||
* Creates new {@link TextQuery} using the {@code locale}s language.<br />
|
||||
* For a full list of supported languages see the mongdodb reference manual for
|
||||
* <a href="https://docs.mongodb.org/manual/reference/text-search-languages/">Text Search Languages</a>.
|
||||
*
|
||||
*
|
||||
* @param wordsAndPhrases
|
||||
* @param locale
|
||||
*/
|
||||
@@ -72,7 +73,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Creates new {@link TextQuery} for given {@link TextCriteria}.
|
||||
*
|
||||
*
|
||||
* @param criteria.
|
||||
*/
|
||||
public TextQuery(TextCriteria criteria) {
|
||||
@@ -81,7 +82,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Creates new {@link TextQuery} searching for given {@link TextCriteria}.
|
||||
*
|
||||
*
|
||||
* @param criteria
|
||||
* @return
|
||||
*/
|
||||
@@ -91,7 +92,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Add sorting by text score. Will also add text score to returned fields.
|
||||
*
|
||||
*
|
||||
* @see TextQuery#includeScore()
|
||||
* @return
|
||||
*/
|
||||
@@ -104,7 +105,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Add field {@literal score} holding the documents textScore to the returned fields.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TextQuery includeScore() {
|
||||
@@ -115,7 +116,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Include text search document score in returned fields using the given fieldname.
|
||||
*
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
@@ -128,7 +129,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Set the fieldname used for scoring.
|
||||
*
|
||||
*
|
||||
* @param fieldName
|
||||
*/
|
||||
public void setScoreFieldName(String fieldName) {
|
||||
@@ -137,7 +138,7 @@ public class TextQuery extends Query {
|
||||
|
||||
/**
|
||||
* Get the fieldname used for scoring
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getScoreFieldName() {
|
||||
@@ -178,9 +179,7 @@ public class TextQuery extends Query {
|
||||
sort.put(getScoreFieldName(), META_TEXT_SCORE);
|
||||
}
|
||||
|
||||
if (super.getSortObject() != null) {
|
||||
sort.putAll(super.getSortObject());
|
||||
}
|
||||
sort.putAll(super.getSortObject());
|
||||
|
||||
return sort;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,6 +34,7 @@ import org.springframework.expression.spel.ast.StringLiteral;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class LiteralNode extends ExpressionNode {
|
||||
|
||||
@@ -56,7 +57,7 @@ public class LiteralNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Creates a new {@link LiteralNode} from the given {@link Literal} and {@link ExpressionState}.
|
||||
*
|
||||
*
|
||||
* @param node must not be {@literal null}.
|
||||
* @param state must not be {@literal null}.
|
||||
*/
|
||||
@@ -67,7 +68,7 @@ public class LiteralNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link ExpressionNode} is a unary minus.
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
* @return
|
||||
*/
|
||||
@@ -78,7 +79,7 @@ public class LiteralNode extends ExpressionNode {
|
||||
}
|
||||
|
||||
OperatorNode operator = (OperatorNode) parent;
|
||||
return operator.isUnaryMinus() && operator.getRight() == null;
|
||||
return operator.isUnaryMinus();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,21 +22,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.ast.OpAnd;
|
||||
import org.springframework.expression.spel.ast.OpDivide;
|
||||
import org.springframework.expression.spel.ast.OpEQ;
|
||||
import org.springframework.expression.spel.ast.OpGE;
|
||||
import org.springframework.expression.spel.ast.OpGT;
|
||||
import org.springframework.expression.spel.ast.OpLE;
|
||||
import org.springframework.expression.spel.ast.OpLT;
|
||||
import org.springframework.expression.spel.ast.OpMinus;
|
||||
import org.springframework.expression.spel.ast.OpModulus;
|
||||
import org.springframework.expression.spel.ast.OpMultiply;
|
||||
import org.springframework.expression.spel.ast.OpNE;
|
||||
import org.springframework.expression.spel.ast.OpOr;
|
||||
import org.springframework.expression.spel.ast.OpPlus;
|
||||
import org.springframework.expression.spel.ast.Operator;
|
||||
import org.springframework.expression.spel.ast.OperatorPower;
|
||||
import org.springframework.expression.spel.ast.*;
|
||||
|
||||
/**
|
||||
* An {@link ExpressionNode} representing an operator.
|
||||
@@ -44,6 +30,7 @@ import org.springframework.expression.spel.ast.OperatorPower;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class OperatorNode extends ExpressionNode {
|
||||
|
||||
@@ -102,7 +89,7 @@ public class OperatorNode extends ExpressionNode {
|
||||
this.operator = node;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.spel.ExpressionNode#isMathematicalOperation()
|
||||
*/
|
||||
@@ -122,16 +109,16 @@ public class OperatorNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Returns whether the operator is unary.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isUnaryOperator() {
|
||||
return operator.getRightOperand() == null;
|
||||
return operator.getChildCount() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Mongo expression of the operator.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getMongoOperator() {
|
||||
@@ -147,7 +134,7 @@ public class OperatorNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Returns whether the operator is a unary minus, e.g. -1.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isUnaryMinus() {
|
||||
@@ -156,7 +143,7 @@ public class OperatorNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Returns the left operand as {@link ExpressionNode}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ExpressionNode getLeft() {
|
||||
@@ -165,7 +152,7 @@ public class OperatorNode extends ExpressionNode {
|
||||
|
||||
/**
|
||||
* Returns the right operand as {@link ExpressionNode}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ExpressionNode getRight() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user