Compare commits
366 Commits
1.7.0.M1
...
1.10.0.RC1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
737f7b4f30 | ||
|
|
408c5d8684 | ||
|
|
982adf317e | ||
|
|
7914e8a630 | ||
|
|
dc4a30a7f8 | ||
|
|
29e405b800 | ||
|
|
1a105333aa | ||
|
|
14e326dc09 | ||
|
|
f026ab419d | ||
|
|
75139042e0 | ||
|
|
6236384c1d | ||
|
|
6993054d6a | ||
|
|
35bfb92ace | ||
|
|
89a02bb822 | ||
|
|
c9c5fe62ca | ||
|
|
d250f88c38 | ||
|
|
bcb63b2732 | ||
|
|
2c4377c9a6 | ||
|
|
e992d813fb | ||
|
|
aa1e91c761 | ||
|
|
9737464f9a | ||
|
|
204a0515c4 | ||
|
|
c6dae3c444 | ||
|
|
c1c7daf0ed | ||
|
|
14678ce7a9 | ||
|
|
4e56d9c575 | ||
|
|
cab35759db | ||
|
|
7b49b120e3 | ||
|
|
dc57b66adf | ||
|
|
0449719a16 | ||
|
|
3d8b6868c7 | ||
|
|
68db0d4cb0 | ||
|
|
c9dfeea0c7 | ||
|
|
1a11877ae9 | ||
|
|
ea4782c421 | ||
|
|
a0ac3510a0 | ||
|
|
192399413d | ||
|
|
a0be890437 | ||
|
|
438dbc4b33 | ||
|
|
407affb458 | ||
|
|
c6a4e7166c | ||
|
|
7f39c42eb7 | ||
|
|
40da4701de | ||
|
|
3ae6aebebb | ||
|
|
bbfa0f7b83 | ||
|
|
63d6234446 | ||
|
|
9ff86feb4f | ||
|
|
8c838e8350 | ||
|
|
a79930145d | ||
|
|
9059a77712 | ||
|
|
a741400e9b | ||
|
|
b786b8220a | ||
|
|
710770e88d | ||
|
|
e631e2d7c5 | ||
|
|
3dc1e9355a | ||
|
|
2985b4ca3d | ||
|
|
578441ee9f | ||
|
|
36838ffe31 | ||
|
|
5bd0e21173 | ||
|
|
255d32513c | ||
|
|
6a9823fd24 | ||
|
|
2ae75a4ff9 | ||
|
|
9c20da3e8f | ||
|
|
f782338581 | ||
|
|
cb90bfc6a6 | ||
|
|
189d4dd1b7 | ||
|
|
10208001f8 | ||
|
|
98dca5a65e | ||
|
|
b6bc0ea316 | ||
|
|
b67c551e19 | ||
|
|
e0bc1e0f20 | ||
|
|
fcb436dd30 | ||
|
|
0e57dd473c | ||
|
|
8d36e42b1b | ||
|
|
b66bfae105 | ||
|
|
eb3d55e0bd | ||
|
|
84dbfdfd5e | ||
|
|
1813b1aea0 | ||
|
|
69241737b7 | ||
|
|
f053bed447 | ||
|
|
1a1cd9ef14 | ||
|
|
7effc0e10f | ||
|
|
395bb1faa4 | ||
|
|
eb1392cc1a | ||
|
|
ace01e4e6d | ||
|
|
116dda63c2 | ||
|
|
4649872394 | ||
|
|
ecc6f3fc4e | ||
|
|
512f68a611 | ||
|
|
fbcd4ba367 | ||
|
|
760d7d6a32 | ||
|
|
29a6688e8c | ||
|
|
b6e7683202 | ||
|
|
286a977575 | ||
|
|
d7f70e219b | ||
|
|
728cc390f6 | ||
|
|
ddcc3914ff | ||
|
|
9a385599af | ||
|
|
c14c42fb0c | ||
|
|
5a8e4f3dae | ||
|
|
5d50155d81 | ||
|
|
babab54ffd | ||
|
|
1ba137b98a | ||
|
|
353b836a77 | ||
|
|
325bcd11b9 | ||
|
|
9db2dde19b | ||
|
|
318ba53e2f | ||
|
|
3db30bd4a6 | ||
|
|
dd1fbfeb66 | ||
|
|
3fa17272bb | ||
|
|
9361fc3c71 | ||
|
|
ac55f5e77f | ||
|
|
116baf9a92 | ||
|
|
026dce2612 | ||
|
|
eae32be568 | ||
|
|
9d51ea4c01 | ||
|
|
f4a5482005 | ||
|
|
0db36aff8f | ||
|
|
ba8ece334a | ||
|
|
a5394074c5 | ||
|
|
fe5bb515b7 | ||
|
|
c84bfbccf4 | ||
|
|
d147f80a39 | ||
|
|
7b8dadeb74 | ||
|
|
d1251c42ca | ||
|
|
4140dd573f | ||
|
|
0e60630393 | ||
|
|
9bc35512fd | ||
|
|
b626c2f82b | ||
|
|
c3e894ee8d | ||
|
|
2f713bede5 | ||
|
|
e03520d2fb | ||
|
|
3829d58dc2 | ||
|
|
7b87fa9509 | ||
|
|
b2b9f3406a | ||
|
|
d610761019 | ||
|
|
8983bd26ce | ||
|
|
5485f2fcd4 | ||
|
|
f8681fec66 | ||
|
|
0dd904894d | ||
|
|
7d70a8677e | ||
|
|
13a52b5ac9 | ||
|
|
f5cfcda673 | ||
|
|
cf44a7105f | ||
|
|
0228255d2b | ||
|
|
50e37355d4 | ||
|
|
a15dababfa | ||
|
|
9942451017 | ||
|
|
e144c29316 | ||
|
|
64d4880983 | ||
|
|
47c348e03a | ||
|
|
dea86535c1 | ||
|
|
eee6b62589 | ||
|
|
771ca8d84c | ||
|
|
8f5b334951 | ||
|
|
0dc6169282 | ||
|
|
abe78f0428 | ||
|
|
9930ec2d19 | ||
|
|
83d7f4477e | ||
|
|
18c3704c2e | ||
|
|
bef581caa5 | ||
|
|
2f0abe0604 | ||
|
|
4235b44c47 | ||
|
|
f318185ad0 | ||
|
|
43b496287c | ||
|
|
9d0c8ecdc3 | ||
|
|
5a78d99af0 | ||
|
|
693f5ddf6e | ||
|
|
ece655f67d | ||
|
|
119692c979 | ||
|
|
6068f3243a | ||
|
|
a7cda2e793 | ||
|
|
2687cb85f0 | ||
|
|
b2ce1700d2 | ||
|
|
0b634f8340 | ||
|
|
9a078b743f | ||
|
|
65b6576cfc | ||
|
|
78e99e6df2 | ||
|
|
bb0a42733d | ||
|
|
a2ae08e263 | ||
|
|
eaa9d6c7e6 | ||
|
|
8900695153 | ||
|
|
bfe548d573 | ||
|
|
7ab4002771 | ||
|
|
6eace856aa | ||
|
|
f10e5a19c5 | ||
|
|
90a4a63776 | ||
|
|
0f14e35ba3 | ||
|
|
ad0c4207d6 | ||
|
|
97da43645a | ||
|
|
42b7c42617 | ||
|
|
bd81e25e6b | ||
|
|
debe6aa649 | ||
|
|
6f433902f0 | ||
|
|
ba902e7f8e | ||
|
|
7e8ec21684 | ||
|
|
b7131b7efc | ||
|
|
ace99c3464 | ||
|
|
83fc5bc113 | ||
|
|
160de0adf6 | ||
|
|
b4753f3a83 | ||
|
|
bce6e2c78c | ||
|
|
b5ea0eccd2 | ||
|
|
87865b9761 | ||
|
|
13fa4703c0 | ||
|
|
5a21e00322 | ||
|
|
3feed2bc5a | ||
|
|
501b9501e0 | ||
|
|
727271e68c | ||
|
|
63a619dddf | ||
|
|
113566a6ab | ||
|
|
7862841b48 | ||
|
|
fe6cbaa03d | ||
|
|
9ef1fc7304 | ||
|
|
cf3a9d3ced | ||
|
|
1d1c80db7b | ||
|
|
eeb37e9104 | ||
|
|
18bf0daee7 | ||
|
|
1e9189aee7 | ||
|
|
95f6dfafdd | ||
|
|
bedaae8a90 | ||
|
|
7bfa3fe7fd | ||
|
|
143b0b73b9 | ||
|
|
cbfc46270e | ||
|
|
b31efb46ec | ||
|
|
ef3477098f | ||
|
|
9dce117555 | ||
|
|
e66e1e0502 | ||
|
|
19e1e9daeb | ||
|
|
ec8a948f3f | ||
|
|
38fc7641a0 | ||
|
|
ddc3925659 | ||
|
|
f8416edf8f | ||
|
|
4f94f37ce8 | ||
|
|
528de58418 | ||
|
|
e6ea34aed8 | ||
|
|
f171938b00 | ||
|
|
7b27368d2d | ||
|
|
f754df51bc | ||
|
|
77dce53c7a | ||
|
|
73f268e7c4 | ||
|
|
075d7d8131 | ||
|
|
206337044a | ||
|
|
55b44ff7aa | ||
|
|
ae48639ae9 | ||
|
|
6b5e78f810 | ||
|
|
3e485e0a88 | ||
|
|
335c78f908 | ||
|
|
b103e4eaf6 | ||
|
|
c4a6c63d23 | ||
|
|
4a4f10f97b | ||
|
|
a5712daab7 | ||
|
|
28cb1ef106 | ||
|
|
0d99a3e527 | ||
|
|
9da43263ce | ||
|
|
784e199068 | ||
|
|
1ffee802c0 | ||
|
|
6f0ac7f0c2 | ||
|
|
941d4d8985 | ||
|
|
44c76d8ffb | ||
|
|
df9a9f5fb6 | ||
|
|
bebd0fa0e6 | ||
|
|
594e90789d | ||
|
|
f2ab42cb80 | ||
|
|
3224fa8ce7 | ||
|
|
ce156c1344 | ||
|
|
434e553022 | ||
|
|
de5b5ee4b0 | ||
|
|
60636bf56d | ||
|
|
1ca71f93e9 | ||
|
|
63ff39bed6 | ||
|
|
cb0b9604d4 | ||
|
|
1dbe3b62d7 | ||
|
|
5c0707d221 | ||
|
|
c4ffc37dd5 | ||
|
|
aaf93b0f6f | ||
|
|
23eab1e84f | ||
|
|
218f32e552 | ||
|
|
62fbe4d08c | ||
|
|
41ffd00619 | ||
|
|
98b9a604cf | ||
|
|
01468b640a | ||
|
|
4d96b036a2 | ||
|
|
2d1ac15e24 | ||
|
|
2c27e8576f | ||
|
|
67f638d953 | ||
|
|
ea5bd5f7d3 | ||
|
|
394f695416 | ||
|
|
e4db466ab9 | ||
|
|
ee04c014c9 | ||
|
|
ea84f08de8 | ||
|
|
7d8a2b2d56 | ||
|
|
995d1e5aac | ||
|
|
3b918492ae | ||
|
|
66b419163c | ||
|
|
52bff39c22 | ||
|
|
d151a13e87 | ||
|
|
5e7e7d3598 | ||
|
|
356248bd05 | ||
|
|
73a60153f6 | ||
|
|
67cf0e62a7 | ||
|
|
21fbcc3e67 | ||
|
|
0d63ff92a0 | ||
|
|
983645e222 | ||
|
|
d2805bfa47 | ||
|
|
3f16b30631 | ||
|
|
8ebcbe3c5c | ||
|
|
363bed5c37 | ||
|
|
1547a646dd | ||
|
|
1408d51065 | ||
|
|
f5c319f18f | ||
|
|
a3c29054d0 | ||
|
|
01533ca34c | ||
|
|
a1f6dc6db4 | ||
|
|
37d53d936d | ||
|
|
bc0a2df653 | ||
|
|
7e50fd8273 | ||
|
|
ba560ffbad | ||
|
|
50ca32c8b9 | ||
|
|
bdfe3af505 | ||
|
|
798b56055d | ||
|
|
ce68e4a070 | ||
|
|
5da3130d26 | ||
|
|
6687cdc101 | ||
|
|
7e74ec6b62 | ||
|
|
b887fa70a5 | ||
|
|
1c6ab25253 | ||
|
|
1c43a3d1ee | ||
|
|
60ca1b3509 | ||
|
|
39d9312005 | ||
|
|
a0e42f5dfe | ||
|
|
7a3aff12a5 | ||
|
|
d4f1ef8704 | ||
|
|
a86d704bec | ||
|
|
57ab27aa5b | ||
|
|
909cc8b5d3 | ||
|
|
b7acbc4347 | ||
|
|
d276306ddc | ||
|
|
25b98b7ad2 | ||
|
|
819b424142 | ||
|
|
5d0328ba4b | ||
|
|
b219cff29c | ||
|
|
409eeaf962 | ||
|
|
4e5e8bd026 | ||
|
|
b91ec53ae0 | ||
|
|
ce0624b8b0 | ||
|
|
b4de2769cf | ||
|
|
3f7b0f1eb6 | ||
|
|
4055365c57 | ||
|
|
db7f782ca6 | ||
|
|
cde9d8d23a | ||
|
|
3dd9b0a2b6 | ||
|
|
59e54cecd2 | ||
|
|
5ed7e8efc2 | ||
|
|
fa85adfe0b | ||
|
|
a3e4f44a64 | ||
|
|
4a7a485e62 | ||
|
|
c353e02b3e | ||
|
|
1c2964cab4 | ||
|
|
47e083280a | ||
|
|
7db003100b | ||
|
|
f814b1ef47 | ||
|
|
f3d2ae366e | ||
|
|
b6ecce3aa2 | ||
|
|
c5235be9a7 | ||
|
|
23300de9d4 |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Thank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.
|
||||
Make sure that:
|
||||
|
||||
- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).
|
||||
- [ ] There is a ticket in the bug tracker for the project in our [JIRA](https://jira.spring.io/browse/DATAMONGO).
|
||||
- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Don’t submit any formatting related changes.
|
||||
- [ ] You submit test cases (unit or integration tests) that back your changes.
|
||||
- [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).
|
||||
- [ ] You provide your full name and an email address registered with your GitHub account. If you’re a first-time submitter, make sure you have completed the [Contributor’s License Agreement form](https://support.springsource.com/spring_committer_signup).
|
||||
40
.travis.yml
Normal file
40
.travis.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
language: java
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
before_script:
|
||||
- mongod --version
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- PROFILE=ci
|
||||
- PROFILE=mongo-next
|
||||
- PROFILE=mongo3
|
||||
- PROFILE=mongo3-next
|
||||
- PROFILE=mongo31
|
||||
- PROFILE=mongo32
|
||||
- PROFILE=mongo33
|
||||
- PROFILE=mongo34
|
||||
- PROFILE=mongo34-next
|
||||
|
||||
# Current MongoDB version is 2.4.2 as of 2016-04, see https://github.com/travis-ci/travis-ci/issues/3694
|
||||
# apt-get starts a MongoDB instance so it's not started using before_script
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- mongodb-3.4-precise
|
||||
packages:
|
||||
- mongodb-org-server
|
||||
- mongodb-org-shell
|
||||
- oracle-java8-installer
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
install: true
|
||||
|
||||
script: "mvn clean dependency:list test -P${PROFILE} -Dsort"
|
||||
27
CODE_OF_CONDUCT.adoc
Normal file
27
CODE_OF_CONDUCT.adoc
Normal file
@@ -0,0 +1,27 @@
|
||||
= Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io.
|
||||
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
|
||||
Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].
|
||||
@@ -1 +0,0 @@
|
||||
You find the contribution guidelines for Spring Data projects [here](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.md).
|
||||
3
CONTRIBUTING.adoc
Normal file
3
CONTRIBUTING.adoc
Normal file
@@ -0,0 +1,3 @@
|
||||
= Spring Data contribution guidelines
|
||||
|
||||
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc[here].
|
||||
@@ -1,3 +1,6 @@
|
||||
[](http://projects.spring.io/spring-data-mongodb#quick-start)
|
||||
[](http://projects.spring.io/spring-data-mongodb#quick-start)
|
||||
|
||||
# Spring Data MongoDB
|
||||
|
||||
The primary goal of the [Spring Data](http://projects.spring.io/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
@@ -26,7 +29,7 @@ Add the Maven dependency:
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.5.0.RELEASE</version>
|
||||
<version>${version}.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -36,7 +39,7 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.6.0.BUILD-SNAPSHOT</version>
|
||||
<version>${version}.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
|
||||
105
pom.xml
105
pom.xml
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,8 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.6.0.M1</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
<version>1.9.0.RC1</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -29,9 +28,9 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.10.0.M1</springdata.commons>
|
||||
<mongo>2.12.3</mongo>
|
||||
<mongo.osgi>2.12.3</mongo.osgi>
|
||||
<springdata.commons>1.13.0.RC1</springdata.commons>
|
||||
<mongo>2.14.3</mongo>
|
||||
<mongo.osgi>2.13.0</mongo.osgi>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
@@ -108,7 +107,7 @@
|
||||
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.12.5-SNAPSHOT</mongo>
|
||||
<mongo>2.15.0-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -122,9 +121,18 @@
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo-3-next</id>
|
||||
<id>mongo3</id>
|
||||
<properties>
|
||||
<mongo>3.0.0-SNAPSHOT</mongo>
|
||||
<mongo>3.0.4</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo3-next</id>
|
||||
<properties>
|
||||
<mongo>3.0.5-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -135,6 +143,79 @@
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo31</id>
|
||||
<properties>
|
||||
<mongo>3.1.1</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo32</id>
|
||||
<properties>
|
||||
<mongo>3.2.2</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo33</id>
|
||||
<properties>
|
||||
<mongo>3.3.0</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mongo-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo34</id>
|
||||
<properties>
|
||||
<mongo>3.4.0</mongo>
|
||||
</properties>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
|
||||
<id>mongo34-next</id>
|
||||
<properties>
|
||||
<mongo>3.4.1-SNAPSHOT</mongo>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mongo-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jfrog.buildinfo</groupId>
|
||||
<artifactId>artifactory-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
@@ -149,14 +230,14 @@
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>http://repo.spring.io/libs-milestone</url>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>http://repo.spring.io/plugins-release</url>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<name>Spring Data MongoDB - Cross-Store Support</name>
|
||||
|
||||
<properties>
|
||||
<jpa>1.0.0.Final</jpa>
|
||||
<jpa>2.0.0</jpa>
|
||||
<hibernate>3.6.10.Final</hibernate>
|
||||
</properties>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -59,8 +59,8 @@
|
||||
|
||||
<!-- JPA -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>javax.persistence</artifactId>
|
||||
<version>${jpa}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -81,7 +81,7 @@
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>1.0.0.GA</version>
|
||||
<version>${validation}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -37,6 +37,8 @@ import com.mongodb.MongoException;
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @author Alex Vengrovsk
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
|
||||
@@ -45,7 +47,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
private static final String ENTITY_FIELD_NAME = "_entity_field_name";
|
||||
private static final String ENTITY_FIELD_CLASS = "_entity_field_class";
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private MongoTemplate mongoTemplate;
|
||||
private EntityManagerFactory entityManagerFactory;
|
||||
@@ -76,25 +78,25 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
dbk.put(ENTITY_ID, id);
|
||||
dbk.put(ENTITY_CLASS, entityClass.getName());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Loading MongoDB data for " + dbk);
|
||||
log.debug("Loading MongoDB data for {}", dbk);
|
||||
}
|
||||
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
for (DBObject dbo : collection.find(dbk)) {
|
||||
String key = (String) dbo.get(ENTITY_FIELD_NAME);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Processing key: " + key);
|
||||
log.debug("Processing key: {}", key);
|
||||
}
|
||||
if (!changeSet.getValues().containsKey(key)) {
|
||||
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
|
||||
if (className == null) {
|
||||
throw new DataIntegrityViolationException("Unble to convert property " + key + ": Invalid metadata, "
|
||||
+ ENTITY_FIELD_CLASS + " not available");
|
||||
throw new DataIntegrityViolationException(
|
||||
"Unble to convert property " + key + ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
|
||||
}
|
||||
Class<?> clazz = ClassUtils.resolveClassName(className, ClassUtils.getDefaultClassLoader());
|
||||
Object value = mongoTemplate.getConverter().read(clazz, dbo);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Adding to ChangeSet: " + key);
|
||||
log.debug("Adding to ChangeSet: {}", key);
|
||||
}
|
||||
changeSet.set(key, value);
|
||||
}
|
||||
@@ -109,9 +111,9 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
* @see org.springframework.data.crossstore.ChangeSetPersister#getPersistentId(org.springframework.data.crossstore.ChangeSetBacked, org.springframework.data.crossstore.ChangeSet)
|
||||
*/
|
||||
public Object getPersistentId(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException {
|
||||
|
||||
log.debug("getPersistentId called on " + entity);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("getPersistentId called on {}", entity);
|
||||
}
|
||||
if (entityManagerFactory == null) {
|
||||
throw new DataAccessResourceFailureException("EntityManagerFactory cannot be null");
|
||||
}
|
||||
@@ -130,7 +132,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Flush: changeset: " + cs.getValues());
|
||||
log.debug("Flush: changeset: {}", cs.getValues());
|
||||
}
|
||||
|
||||
String collName = getCollectionNameForEntity(entity.getClass());
|
||||
@@ -152,7 +154,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
});
|
||||
if (value == null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Flush: removing: " + dbQuery);
|
||||
log.debug("Flush: removing: {}", dbQuery);
|
||||
}
|
||||
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
@@ -164,7 +166,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
final DBObject dbDoc = new BasicDBObject();
|
||||
dbDoc.putAll(dbQuery);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Flush: saving: " + dbQuery);
|
||||
log.debug("Flush: saving: {}", dbQuery);
|
||||
}
|
||||
mongoTemplate.getConverter().write(value, dbDoc);
|
||||
dbDoc.put(ENTITY_FIELD_CLASS, value.getClass().getName());
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,8 @@ 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;
|
||||
@@ -30,13 +32,18 @@ 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 {
|
||||
|
||||
@@ -54,17 +61,19 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
|
||||
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.SAFE;
|
||||
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
|
||||
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.ACKNOWLEDGED;
|
||||
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.UNACKNOWLEDGED;
|
||||
protected Mongo mongo;
|
||||
protected DB db;
|
||||
|
||||
public MongoLog4jAppender() {
|
||||
}
|
||||
public MongoLog4jAppender() {}
|
||||
|
||||
public MongoLog4jAppender(boolean isActive) {
|
||||
super(isActive);
|
||||
@@ -86,6 +95,53 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
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;
|
||||
}
|
||||
@@ -111,14 +167,14 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
public void setWarnOrHigherWriteConcern(String wc) {
|
||||
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc);
|
||||
}
|
||||
|
||||
public String getWarnOrHigherWriteConcern() {
|
||||
return warnOrHigherWriteConcern.toString();
|
||||
}
|
||||
|
||||
public void setWarnOrHigherWriteConcern(String wc) {
|
||||
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc);
|
||||
}
|
||||
|
||||
public String getInfoOrLowerWriteConcern() {
|
||||
return infoOrLowerWriteConcern.toString();
|
||||
}
|
||||
@@ -128,10 +184,26 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
}
|
||||
|
||||
protected void connectToMongo() throws UnknownHostException {
|
||||
this.mongo = new Mongo(host, port);
|
||||
|
||||
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)
|
||||
@@ -160,7 +232,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
|
||||
|
||||
// Copy properties into document
|
||||
Map<Object, Object> props = event.getProperties();
|
||||
if (null != props && props.size() > 0) {
|
||||
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());
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,39 +20,51 @@ 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.Mongo;
|
||||
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 {
|
||||
|
||||
static final String NAME = MongoLog4jAppenderIntegrationTests.class.getName();
|
||||
|
||||
Logger log = Logger.getLogger(NAME);
|
||||
Mongo mongo;
|
||||
MongoClient mongo;
|
||||
DB db;
|
||||
String collection;
|
||||
ServerAddress serverLocation;
|
||||
Logger log;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
serverLocation = new ServerAddress("localhost", 27017);
|
||||
|
||||
mongo = new Mongo("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);
|
||||
db.getCollection(collection).drop();
|
||||
|
||||
log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
db.getCollection(collection).remove(new BasicDBObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,13 +76,11 @@ public class MongoLog4jAppenderIntegrationTests {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
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 +1,13 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
log4j.rootCategory=INFO, mongo
|
||||
|
||||
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 = %X{year}%X{month}
|
||||
log4j.appender.stdout.applicationId = my.application
|
||||
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<context version="7.1.10.209">
|
||||
<context version="7.2.2.230">
|
||||
<scope type="Project" name="spring-data-mongodb">
|
||||
<element type="TypeFilterReferenceOverridden" name="Filter">
|
||||
<element type="IncludeTypePattern" name="org.springframework.data.mongodb.**"/>
|
||||
@@ -35,6 +35,12 @@
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|API" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Repositories::Subsystem|Implementation" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="CDI">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.cdi.**"/>
|
||||
</element>
|
||||
<stereotype name="Unrestricted"/>
|
||||
</element>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Config" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core" type="AllowedDependency"/>
|
||||
</element>
|
||||
@@ -76,6 +82,11 @@
|
||||
</element>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Script">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.script.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Conversion">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.core.convert.**"/>
|
||||
@@ -83,6 +94,7 @@
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Script" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="SpEL">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
@@ -105,6 +117,11 @@
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="MapReduce">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="**.mapreduce.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Core">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="WeakTypePattern" name="**.core.**"/>
|
||||
@@ -113,8 +130,10 @@
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Conversion" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Geospatial" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Index" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|MapReduce" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Mapping" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Query" type="AllowedDependency"/>
|
||||
<dependency toName="Project|spring-data-mongodb::Layer|Core::Subsystem|Script" type="AllowedDependency"/>
|
||||
</element>
|
||||
<element type="Subsystem" name="Util">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
@@ -169,7 +188,32 @@
|
||||
</element>
|
||||
<element type="Subsystem" name="Querydsl">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="com.mysema.query.**"/>
|
||||
<element type="IncludeTypePattern" name="com.querydsl.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Slf4j">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="org.slf4j.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Jackson">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="com.fasterxml.jackson.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="DOM">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="org.w3c.dom.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="AOP Alliance">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="org.aopalliance.**"/>
|
||||
</element>
|
||||
</element>
|
||||
<element type="Subsystem" name="Guava">
|
||||
<element type="TypeFilter" name="Assignment">
|
||||
<element type="IncludeTypePattern" name="com.google.common.**"/>
|
||||
</element>
|
||||
</element>
|
||||
</architecture>
|
||||
|
||||
@@ -11,12 +11,11 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.7.0.M1</version>
|
||||
<version>1.10.0.RC1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<validation>1.0.0.GA</validation>
|
||||
<objenesis>1.3</objenesis>
|
||||
<equalsverifier>1.5</equalsverifier>
|
||||
</properties>
|
||||
@@ -59,14 +58,14 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-mongodb</artifactId>
|
||||
<version>${querydsl}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl}</version>
|
||||
<scope>provided</scope>
|
||||
@@ -138,6 +137,20 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.threeten</groupId>
|
||||
<artifactId>threetenbp</artifactId>
|
||||
<version>${threetenbp}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
@@ -151,6 +164,13 @@
|
||||
<version>${equalsverifier}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -162,7 +182,7 @@
|
||||
<version>${apt}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import com.mongodb.BulkWriteError;
|
||||
import com.mongodb.BulkWriteException;
|
||||
import com.mongodb.BulkWriteResult;
|
||||
|
||||
/**
|
||||
* Is thrown when errors occur during bulk operations.
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @since 1.9
|
||||
*/
|
||||
public class BulkOperationException extends DataAccessException {
|
||||
|
||||
private static final long serialVersionUID = 73929601661154421L;
|
||||
|
||||
private final List<BulkWriteError> errors;
|
||||
private final BulkWriteResult result;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BulkOperationException} with the given message and source {@link BulkWriteException}.
|
||||
*
|
||||
* @param message must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
*/
|
||||
public BulkOperationException(String message, BulkWriteException source) {
|
||||
|
||||
super(message, source);
|
||||
|
||||
this.errors = source.getWriteErrors();
|
||||
this.result = source.getWriteResult();
|
||||
}
|
||||
|
||||
public List<BulkWriteError> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
public BulkWriteResult getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -46,6 +47,7 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* Base class for Spring Data MongoDB configuration using JavaConfig.
|
||||
@@ -54,6 +56,7 @@ import com.mongodb.Mongo;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Ryan Tenney
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractMongoConfiguration {
|
||||
@@ -70,7 +73,10 @@ public abstract class AbstractMongoConfiguration {
|
||||
* returned by {@link #getDatabaseName()} later on effectively.
|
||||
*
|
||||
* @return
|
||||
* @deprecated since 1.7. {@link MongoClient} should hold authentication data within
|
||||
* {@link MongoClient#getCredentialsList()}
|
||||
*/
|
||||
@Deprecated
|
||||
protected String getAuthenticationDatabaseName() {
|
||||
return null;
|
||||
}
|
||||
@@ -113,23 +119,42 @@ public abstract class AbstractMongoConfiguration {
|
||||
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
|
||||
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
|
||||
* {@link AbstractMongoConfiguration} the base package will be considered {@code com.acme} unless the method is
|
||||
* overriden to implement alternate behaviour.
|
||||
* overridden to implement alternate behavior.
|
||||
*
|
||||
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
|
||||
* entities.
|
||||
* @deprecated use {@link #getMappingBasePackages()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected String getMappingBasePackage() {
|
||||
|
||||
Package mappingBasePackage = getClass().getPackage();
|
||||
return mappingBasePackage == null ? null : mappingBasePackage.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base packages to scan for MongoDB mapped entities at startup. Will return the package name of the
|
||||
* configuration class' (the concrete class, not this one here) by default. So if you have a
|
||||
* {@code com.acme.AppConfig} extending {@link AbstractMongoConfiguration} the base package will be considered
|
||||
* {@code com.acme} unless the method is overridden to implement alternate behavior.
|
||||
*
|
||||
* @return the base packages to scan for mapped {@link Document} classes or an empty collection to not enable scanning
|
||||
* for entities.
|
||||
* @since 1.10
|
||||
*/
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
return Collections.singleton(getMappingBasePackage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link UserCredentials} to be used when connecting to the MongoDB instance or {@literal null} if none shall
|
||||
* be used.
|
||||
*
|
||||
* @return
|
||||
* @deprecated since 1.7. {@link MongoClient} should hold authentication data within
|
||||
* {@link MongoClient#getCredentialsList()}
|
||||
*/
|
||||
@Deprecated
|
||||
protected UserCredentials getUserCredentials() {
|
||||
return null;
|
||||
}
|
||||
@@ -196,26 +221,52 @@ public abstract class AbstractMongoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the mapping base package for classes annotated with {@link Document}.
|
||||
* Scans the mapping base package for classes annotated with {@link Document}. By default, it scans for entities in
|
||||
* all packages returned by {@link #getMappingBasePackages()}.
|
||||
*
|
||||
* @see #getMappingBasePackage()
|
||||
* @see #getMappingBasePackages()
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
|
||||
|
||||
String basePackage = getMappingBasePackage();
|
||||
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
|
||||
for (String basePackage : getMappingBasePackages()) {
|
||||
initialEntitySet.addAll(scanForEntities(basePackage));
|
||||
}
|
||||
|
||||
return initialEntitySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given base package for entities, i.e. MongoDB specific types annotated with {@link Document} and
|
||||
* {@link Persistent}.
|
||||
*
|
||||
* @param basePackage must not be {@literal null}.
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
* @since 1.10
|
||||
*/
|
||||
protected Set<Class<?>> scanForEntities(String basePackage) throws ClassNotFoundException {
|
||||
|
||||
if (!StringUtils.hasText(basePackage)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
|
||||
if (StringUtils.hasText(basePackage)) {
|
||||
|
||||
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
|
||||
false);
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
|
||||
|
||||
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
|
||||
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
|
||||
AbstractMongoConfiguration.class.getClassLoader()));
|
||||
|
||||
initialEntitySet
|
||||
.add(ClassUtils.forName(candidate.getBeanClassName(), AbstractMongoConfiguration.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -15,24 +15,24 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.*;
|
||||
import static org.springframework.data.mongodb.config.BeanNames.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.auditing.config.AuditingConfiguration;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
|
||||
import org.springframework.data.support.IsNewStrategyFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,6 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
defaultDependenciesIfNecessary(registry, annotationMetadata);
|
||||
super.registerBeanDefinitions(annotationMetadata, registry);
|
||||
}
|
||||
|
||||
@@ -85,7 +84,11 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
|
||||
builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
|
||||
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContextLookup.class);
|
||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
|
||||
builder.addConstructorArgValue(definition.getBeanDefinition());
|
||||
return configureDefaultAuditHandlerAttributes(configuration, builder);
|
||||
}
|
||||
|
||||
@@ -102,29 +105,58 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
|
||||
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AuditingEventListener.class);
|
||||
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
|
||||
getAuditingHandlerBeanName(), registry));
|
||||
listenerBeanDefinitionBuilder
|
||||
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
|
||||
|
||||
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
|
||||
AuditingEventListener.class.getName(), registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default bean definitions for a {@link MongoMappingContext} and an {@link IsNewStrategyFactory} in case we
|
||||
* don't find beans with the assumed names in the registry.
|
||||
*
|
||||
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
|
||||
* @param source the source which the registered components shall be registered with
|
||||
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingMongoConverter} bean available in
|
||||
* the application context.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
static class MongoMappingContextLookup
|
||||
implements FactoryBean<MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>> {
|
||||
|
||||
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
|
||||
private final MappingMongoConverter converter;
|
||||
|
||||
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
|
||||
definition.setRole(ROLE_INFRASTRUCTURE);
|
||||
definition.setSource(source);
|
||||
/**
|
||||
* Creates a new {@link MongoMappingContextLookup} for the given {@link MappingMongoConverter}.
|
||||
*
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public MongoMappingContextLookup(MappingMongoConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Override
|
||||
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getObject() throws Exception {
|
||||
return converter.getMappingContext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MappingContext.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.config.BeanComponentDefinitionBuilder;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mongodb.core.MongoClientFactoryBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for {@code mongo-client} definitions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MongoClientParser implements BeanDefinitionParser {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
|
||||
Object source = parserContext.extractSource(element);
|
||||
String id = element.getAttribute("id");
|
||||
|
||||
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoClientFactoryBean.class);
|
||||
|
||||
ParsingUtils.setPropertyValue(builder, element, "port", "port");
|
||||
ParsingUtils.setPropertyValue(builder, element, "host", "host");
|
||||
ParsingUtils.setPropertyValue(builder, element, "credentials", "credentials");
|
||||
|
||||
MongoParsingUtils.parseMongoClientOptions(element, builder);
|
||||
MongoParsingUtils.parseReplicaSet(element, builder);
|
||||
|
||||
String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO_BEAN_NAME;
|
||||
|
||||
parserContext.pushContainingComponent(new CompositeComponentDefinition("Mongo", source));
|
||||
|
||||
BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId);
|
||||
parserContext.registerBeanComponent(mongoComponent);
|
||||
|
||||
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils
|
||||
.getServerAddressPropertyEditorBuilder());
|
||||
parserContext.registerBeanComponent(serverAddressPropertyEditor);
|
||||
|
||||
BeanComponentDefinition writeConcernEditor = helper.getComponent(MongoParsingUtils
|
||||
.getWriteConcernPropertyEditorBuilder());
|
||||
parserContext.registerBeanComponent(writeConcernEditor);
|
||||
|
||||
BeanComponentDefinition readPreferenceEditor = helper.getComponent(MongoParsingUtils
|
||||
.getReadPreferencePropertyEditorBuilder());
|
||||
parserContext.registerBeanComponent(readPreferenceEditor);
|
||||
|
||||
BeanComponentDefinition credentialsEditor = helper.getComponent(MongoParsingUtils
|
||||
.getMongoCredentialPropertyEditor());
|
||||
parserContext.registerBeanComponent(credentialsEditor);
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
return mongoComponent.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.MongoCredential;
|
||||
|
||||
/**
|
||||
* Parse a {@link String} to a Collection of {@link MongoCredential}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private static final Pattern GROUP_PATTERN = Pattern.compile("(\\\\?')(.*?)\\1");
|
||||
|
||||
private static final String AUTH_MECHANISM_KEY = "uri.authMechanism";
|
||||
private static final String USERNAME_PASSWORD_DELIMINATOR = ":";
|
||||
private static final String DATABASE_DELIMINATOR = "@";
|
||||
private static final String OPTIONS_DELIMINATOR = "?";
|
||||
private static final String OPTION_VALUE_DELIMINATOR = "&";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
|
||||
if (!StringUtils.hasText(text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
|
||||
|
||||
for (String credentialString : extractCredentialsString(text)) {
|
||||
|
||||
String[] userNameAndPassword = extractUserNameAndPassword(credentialString);
|
||||
String database = extractDB(credentialString);
|
||||
Properties options = extractOptions(credentialString);
|
||||
|
||||
if (!options.isEmpty()) {
|
||||
|
||||
if (options.containsKey(AUTH_MECHANISM_KEY)) {
|
||||
|
||||
String authMechanism = options.getProperty(AUTH_MECHANISM_KEY);
|
||||
|
||||
if (MongoCredential.GSSAPI_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUserNamePresent(userNameAndPassword);
|
||||
credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0]));
|
||||
} else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUserNamePresent(userNameAndPassword);
|
||||
credentials.add(MongoCredential.createMongoX509Credential(userNameAndPassword[0]));
|
||||
} else if (MongoCredential.PLAIN_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createPlainCredential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else if (MongoCredential.SCRAM_SHA_1_MECHANISM.equals(authMechanism)) {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(MongoCredential.createScramSha1Credential(userNameAndPassword[0], database,
|
||||
userNameAndPassword[1].toCharArray()));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Cannot create MongoCredentials for unknown auth mechanism '%s'!", authMechanism));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
verifyUsernameAndPasswordPresent(userNameAndPassword);
|
||||
verifyDatabasePresent(database);
|
||||
credentials.add(
|
||||
MongoCredential.createCredential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray()));
|
||||
}
|
||||
}
|
||||
|
||||
setValue(credentials);
|
||||
}
|
||||
|
||||
private List<String> extractCredentialsString(String source) {
|
||||
|
||||
Matcher matcher = GROUP_PATTERN.matcher(source);
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
while (matcher.find()) {
|
||||
|
||||
String value = StringUtils.trimLeadingCharacter(matcher.group(), '\'');
|
||||
list.add(StringUtils.trimTrailingCharacter(value, '\''));
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
return Arrays.asList(source.split(","));
|
||||
}
|
||||
|
||||
private static String[] extractUserNameAndPassword(String text) {
|
||||
|
||||
int index = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
|
||||
index = index != -1 ? index : text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
|
||||
return index == -1 ? new String[] {} : text.substring(0, index).split(USERNAME_PASSWORD_DELIMINATOR);
|
||||
}
|
||||
|
||||
private static String extractDB(String text) {
|
||||
|
||||
int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR);
|
||||
|
||||
if (dbSeperationIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String tmp = text.substring(dbSeperationIndex + 1);
|
||||
int optionsSeperationIndex = tmp.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
|
||||
return optionsSeperationIndex > -1 ? tmp.substring(0, optionsSeperationIndex) : tmp;
|
||||
}
|
||||
|
||||
private static Properties extractOptions(String text) {
|
||||
|
||||
int optionsSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
int dbSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR);
|
||||
|
||||
if (optionsSeperationIndex == -1 || dbSeperationIndex > optionsSeperationIndex) {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
Properties properties = new Properties();
|
||||
|
||||
for (String option : text.substring(optionsSeperationIndex + 1).split(OPTION_VALUE_DELIMINATOR)) {
|
||||
String[] optionArgs = option.split("=");
|
||||
properties.put(optionArgs[0], optionArgs[1]);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static void verifyUsernameAndPasswordPresent(String[] source) {
|
||||
|
||||
verifyUserNamePresent(source);
|
||||
|
||||
if (source.length != 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Credentials need to specify username and password like in 'username:password@database'!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDatabasePresent(String source) {
|
||||
|
||||
if (!StringUtils.hasText(source)) {
|
||||
throw new IllegalArgumentException("Credentials need to specify database like in 'username:password@database'!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyUserNamePresent(String[] source) {
|
||||
|
||||
if (source.length == 0 || !StringUtils.hasText(source[0])) {
|
||||
throw new IllegalArgumentException("Credentials need to specify username!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 by the original author(s).
|
||||
* Copyright 2011-2015 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,10 @@ package org.springframework.data.mongodb.config;
|
||||
import static org.springframework.data.config.ParsingUtils.*;
|
||||
import static org.springframework.data.mongodb.config.MongoParsingUtils.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
@@ -34,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.MongoURI;
|
||||
|
||||
/**
|
||||
@@ -42,9 +47,22 @@ import com.mongodb.MongoURI;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Viktor Khoroshko
|
||||
*/
|
||||
public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
private static final Set<String> MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES;
|
||||
|
||||
static {
|
||||
|
||||
Set<String> mongoUriAllowedAdditionalAttributes = new HashSet<String>();
|
||||
mongoUriAllowedAdditionalAttributes.add("id");
|
||||
mongoUriAllowedAdditionalAttributes.add("write-concern");
|
||||
|
||||
MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES = Collections.unmodifiableSet(mongoUriAllowedAdditionalAttributes);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
|
||||
@@ -64,29 +82,25 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
|
||||
|
||||
String uri = element.getAttribute("uri");
|
||||
String mongoRef = element.getAttribute("mongo-ref");
|
||||
String dbname = element.getAttribute("dbname");
|
||||
|
||||
BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element, parserContext);
|
||||
|
||||
// Common setup
|
||||
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
|
||||
setPropertyValue(dbFactoryBuilder, element, "write-concern", "writeConcern");
|
||||
|
||||
if (StringUtils.hasText(uri)) {
|
||||
if (StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) {
|
||||
parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", source);
|
||||
}
|
||||
BeanDefinition mongoUri = getMongoUri(element, parserContext);
|
||||
|
||||
dbFactoryBuilder.addConstructorArgValue(getMongoUri(uri));
|
||||
if (mongoUri != null) {
|
||||
|
||||
dbFactoryBuilder.addConstructorArgValue(mongoUri);
|
||||
return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element);
|
||||
}
|
||||
|
||||
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
|
||||
|
||||
String mongoRef = element.getAttribute("mongo-ref");
|
||||
String dbname = element.getAttribute("dbname");
|
||||
|
||||
BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element, parserContext);
|
||||
|
||||
// Defaulting
|
||||
if (StringUtils.hasText(mongoRef)) {
|
||||
dbFactoryBuilder.addConstructorArgReference(mongoRef);
|
||||
@@ -147,14 +161,42 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BeanDefinition} for a {@link MongoURI}.
|
||||
* Creates a {@link BeanDefinition} for a {@link MongoURI} or {@link MongoClientURI} depending on configured
|
||||
* attributes. <br />
|
||||
* Errors when configured element contains {@literal uri} or {@literal client-uri} along with other attributes except
|
||||
* {@literal write-concern} and/or {@literal id}.
|
||||
*
|
||||
* @param uri
|
||||
* @return
|
||||
* @param element must not be {@literal null}.
|
||||
* @param parserContext
|
||||
* @return {@literal null} in case no client-/uri defined.
|
||||
*/
|
||||
private BeanDefinition getMongoUri(String uri) {
|
||||
private BeanDefinition getMongoUri(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoURI.class);
|
||||
boolean hasClientUri = element.hasAttribute("client-uri");
|
||||
|
||||
if (!hasClientUri && !element.hasAttribute("uri")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int allowedAttributesCount = 1;
|
||||
for (String attribute : MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES) {
|
||||
|
||||
if (element.hasAttribute(attribute)) {
|
||||
allowedAttributesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (element.getAttributes().getLength() > allowedAttributesCount) {
|
||||
|
||||
parserContext.getReaderContext().error(
|
||||
"Configure either " + (hasClientUri ? "Mongo Client URI" : "Mongo URI") + " or details individually!",
|
||||
parserContext.extractSource(element));
|
||||
}
|
||||
|
||||
Class<?> type = hasClientUri ? MongoClientURI.class : MongoURI.class;
|
||||
String uri = hasClientUri ? element.getAttribute("client-uri") : element.getAttribute("uri");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
|
||||
builder.addConstructorArgValue(uri);
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Martin Baumgartner
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@@ -33,6 +34,7 @@ public class MongoNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser());
|
||||
registerBeanDefinitionParser("mongo", new MongoParser());
|
||||
registerBeanDefinitionParser("mongo-client", new MongoClientParser());
|
||||
registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser());
|
||||
registerBeanDefinitionParser("jmx", new MongoJmxParser());
|
||||
registerBeanDefinitionParser("auditing", new MongoAuditingBeanDefinitionParser());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.CustomEditorConfigurer;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.config.BeanComponentDefinitionBuilder;
|
||||
@@ -36,6 +32,7 @@ import org.w3c.dom.Element;
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoParser implements BeanDefinitionParser {
|
||||
|
||||
@@ -64,7 +61,8 @@ public class MongoParser implements BeanDefinitionParser {
|
||||
|
||||
BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId);
|
||||
parserContext.registerBeanComponent(mongoComponent);
|
||||
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(registerServerAddressPropertyEditor());
|
||||
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils
|
||||
.getServerAddressPropertyEditorBuilder());
|
||||
parserContext.registerBeanComponent(serverAddressPropertyEditor);
|
||||
BeanComponentDefinition writeConcernPropertyEditor = helper.getComponent(MongoParsingUtils
|
||||
.getWriteConcernPropertyEditorBuilder());
|
||||
@@ -75,19 +73,4 @@ public class MongoParser implements BeanDefinitionParser {
|
||||
return mongoComponent.getBeanDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* One should only register one bean definition but want to have the convenience of using
|
||||
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
|
||||
* container.
|
||||
*/
|
||||
private BeanDefinitionBuilder registerServerAddressPropertyEditor() {
|
||||
|
||||
Map<String, String> customEditors = new ManagedMap<String, String>();
|
||||
customEditors.put("com.mongodb.ServerAddress[]",
|
||||
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
|
||||
builder.addPropertyValue("customEditors", customEditors);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.config.CustomEditorConfigurer;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.data.mongodb.core.MongoClientOptionsFactoryBean;
|
||||
import org.springframework.data.mongodb.core.MongoOptionsFactoryBean;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -33,13 +34,13 @@ import org.w3c.dom.Element;
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
abstract class MongoParsingUtils {
|
||||
|
||||
private MongoParsingUtils() {
|
||||
|
||||
}
|
||||
private MongoParsingUtils() {}
|
||||
|
||||
/**
|
||||
* Parses the mongo replica-set element.
|
||||
@@ -54,12 +55,14 @@ abstract class MongoParsingUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes.
|
||||
* Parses the {@code mongo:options} sub-element. Populates the given attribute factory with the proper attributes.
|
||||
*
|
||||
* @return true if parsing actually occured, false otherwise
|
||||
* @return true if parsing actually occured, {@literal false} otherwise
|
||||
*/
|
||||
static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBuilder) {
|
||||
|
||||
Element optionsElement = DomUtils.getChildElementByTagName(element, "options");
|
||||
|
||||
if (optionsElement == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -80,13 +83,58 @@ abstract class MongoParsingUtils {
|
||||
setPropertyValue(optionsDefBuilder, optionsElement, "write-timeout", "writeTimeout");
|
||||
setPropertyValue(optionsDefBuilder, optionsElement, "write-fsync", "writeFsync");
|
||||
setPropertyValue(optionsDefBuilder, optionsElement, "slave-ok", "slaveOk");
|
||||
setPropertyValue(optionsDefBuilder, optionsElement, "ssl", "ssl");
|
||||
setPropertyReference(optionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory");
|
||||
setPropertyValue(optionsDefBuilder, optionsElement, "ssl", "ssl");
|
||||
setPropertyReference(optionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory");
|
||||
|
||||
mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code mongo:client-options} sub-element. Populates the given attribute factory with the proper
|
||||
* attributes.
|
||||
*
|
||||
* @param element must not be {@literal null}.
|
||||
* @param mongoClientBuilder must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public static boolean parseMongoClientOptions(Element element, BeanDefinitionBuilder mongoClientBuilder) {
|
||||
|
||||
Element optionsElement = DomUtils.getChildElementByTagName(element, "client-options");
|
||||
|
||||
if (optionsElement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder clientOptionsDefBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(MongoClientOptionsFactoryBean.class);
|
||||
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "description", "description");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-connections-per-host", "minConnectionsPerHost");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier",
|
||||
"threadsAllowedToBlockForConnectionMultiplier");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-idle-time", "maxConnectionIdleTime");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-life-time", "maxConnectionLifeTime");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "read-preference", "readPreference");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "write-concern", "writeConcern");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-frequency", "heartbeatFrequency");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-heartbeat-frequency", "minHeartbeatFrequency");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-connect-timeout", "heartbeatConnectTimeout");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-socket-timeout", "heartbeatSocketTimeout");
|
||||
setPropertyValue(clientOptionsDefBuilder, optionsElement, "ssl", "ssl");
|
||||
setPropertyReference(clientOptionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory");
|
||||
|
||||
mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
|
||||
* {@link WriteConcernPropertyEditor}.
|
||||
@@ -103,4 +151,56 @@ abstract class MongoParsingUtils {
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* One should only register one bean definition but want to have the convenience of using
|
||||
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
|
||||
* container.
|
||||
*/
|
||||
static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() {
|
||||
|
||||
Map<String, String> customEditors = new ManagedMap<String, String>();
|
||||
customEditors.put("com.mongodb.ServerAddress[]",
|
||||
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
|
||||
builder.addPropertyValue("customEditors", customEditors);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
|
||||
* {@link ReadPreferencePropertyEditor}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
static BeanDefinitionBuilder getReadPreferencePropertyEditorBuilder() {
|
||||
|
||||
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
|
||||
customEditors.put("com.mongodb.ReadPreference", ReadPreferencePropertyEditor.class);
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
|
||||
builder.addPropertyValue("customEditors", customEditors);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
|
||||
* {@link MongoCredentialPropertyEditor}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
static BeanDefinitionBuilder getMongoCredentialPropertyEditor() {
|
||||
|
||||
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
|
||||
customEditors.put("com.mongodb.MongoCredential[]", MongoCredentialPropertyEditor.class);
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
|
||||
builder.addPropertyValue("customEditors", customEditors);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import com.mongodb.ReadPreference;
|
||||
|
||||
/**
|
||||
* Parse a {@link String} to a {@link ReadPreference}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class ReadPreferencePropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String readPreferenceString) throws IllegalArgumentException {
|
||||
|
||||
if (readPreferenceString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReadPreference preference = null;
|
||||
|
||||
try {
|
||||
preference = ReadPreference.valueOf(readPreferenceString);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// ignore this one and try to map it differently
|
||||
}
|
||||
|
||||
if (preference != null) {
|
||||
setValue(preference);
|
||||
} else if ("PRIMARY".equalsIgnoreCase(readPreferenceString)) {
|
||||
setValue(ReadPreference.primary());
|
||||
} else if ("PRIMARY_PREFERRED".equalsIgnoreCase(readPreferenceString)) {
|
||||
setValue(ReadPreference.primaryPreferred());
|
||||
} else if ("SECONDARY".equalsIgnoreCase(readPreferenceString)) {
|
||||
setValue(ReadPreference.secondary());
|
||||
} else if ("SECONDARY_PREFERRED".equalsIgnoreCase(readPreferenceString)) {
|
||||
setValue(ReadPreference.secondaryPreferred());
|
||||
} else if ("NEAREST".equalsIgnoreCase(readPreferenceString)) {
|
||||
setValue(ReadPreference.nearest());
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Cannot find matching ReadPreference for %s",
|
||||
readPreferenceString));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2015-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 java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.util.Pair;
|
||||
|
||||
import com.mongodb.BulkWriteResult;
|
||||
|
||||
/**
|
||||
* Bulk operations for insert/update/remove actions on a collection. These bulks operation are available since MongoDB
|
||||
* 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
|
||||
*/
|
||||
public interface BulkOperations {
|
||||
|
||||
/**
|
||||
* Mode for bulk operation.
|
||||
**/
|
||||
public enum BulkMode {
|
||||
|
||||
/** Perform bulk operations in sequence. The first error will cancel processing. */
|
||||
ORDERED,
|
||||
|
||||
/** Perform bulk operations in parallel. Processing will continue on errors. */
|
||||
UNORDERED
|
||||
};
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations insert(Object documents);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations insert(List<? extends Object> documents);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations updateOne(Query query, Update update);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations updateOne(List<Pair<Query, Update>> updates);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations updateMulti(Query query, Update update);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations updateMulti(List<Pair<Query, Update>> updates);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
|
||||
*/
|
||||
BulkOperations upsert(Query query, Update update);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations upsert(List<Pair<Query, Update>> updates);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations remove(Query remove);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
BulkOperations remove(List<Query> removes);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
BulkWriteResult execute();
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import com.mongodb.DBCursor;
|
||||
interface CursorPreparer {
|
||||
|
||||
/**
|
||||
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.
|
||||
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
|
||||
*
|
||||
* @param cursor
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright 2015-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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BulkWriteException;
|
||||
import com.mongodb.BulkWriteOperation;
|
||||
import com.mongodb.BulkWriteRequestBuilder;
|
||||
import com.mongodb.BulkWriteResult;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BulkOperations}.
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.9
|
||||
*/
|
||||
class DefaultBulkOperations implements BulkOperations {
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final BulkMode bulkMode;
|
||||
private final String collectionName;
|
||||
private final Class<?> entityType;
|
||||
|
||||
private PersistenceExceptionTranslator exceptionTranslator;
|
||||
private WriteConcernResolver writeConcernResolver;
|
||||
private WriteConcern defaultWriteConcern;
|
||||
|
||||
private BulkWriteOperation bulk;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultBulkOperations} for the given {@link MongoOperations}, {@link BulkMode}, collection
|
||||
* name and {@link WriteConcern}.
|
||||
*
|
||||
* @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}.
|
||||
*/
|
||||
DefaultBulkOperations(MongoOperations mongoOperations, BulkMode bulkMode, String collectionName,
|
||||
Class<?> entityType) {
|
||||
|
||||
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!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
this.bulkMode = bulkMode;
|
||||
this.collectionName = collectionName;
|
||||
this.entityType = entityType;
|
||||
|
||||
this.exceptionTranslator = new MongoExceptionTranslator();
|
||||
this.writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
|
||||
|
||||
this.bulk = initBulkOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link PersistenceExceptionTranslator} to be used. Defaults to {@link MongoExceptionTranslator}.
|
||||
*
|
||||
* @param exceptionTranslator can be {@literal null}.
|
||||
*/
|
||||
public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) {
|
||||
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) {
|
||||
this.defaultWriteConcern = defaultWriteConcern;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#insert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations insert(Object document) {
|
||||
|
||||
Assert.notNull(document, "Document must not be null!");
|
||||
|
||||
if (document instanceof DBObject) {
|
||||
|
||||
bulk.insert((DBObject) document);
|
||||
return this;
|
||||
}
|
||||
|
||||
DBObject sink = new BasicDBObject();
|
||||
mongoOperations.getConverter().write(document, sink);
|
||||
bulk.insert(sink);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#insert(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations insert(List<? extends Object> documents) {
|
||||
|
||||
Assert.notNull(documents, "Documents must not be null!");
|
||||
|
||||
for (Object document : documents) {
|
||||
insert(document);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#updateOne(org.springframework.data.mongodb.core.query.Query, org.springframework.data.mongodb.core.query.Update)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BulkOperations updateOne(Query query, Update update) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return updateOne(Arrays.asList(Pair.of(query, update)));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#updateOne(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations updateOne(List<Pair<Query, Update>> updates) {
|
||||
|
||||
Assert.notNull(updates, "Updates must not be null!");
|
||||
|
||||
for (Pair<Query, Update> update : updates) {
|
||||
update(update.getFirst(), update.getSecond(), false, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#updateMulti(org.springframework.data.mongodb.core.query.Query, org.springframework.data.mongodb.core.query.Update)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BulkOperations updateMulti(Query query, Update update) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
return updateMulti(Arrays.asList(Pair.of(query, update)));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#updateMulti(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations updateMulti(List<Pair<Query, Update>> updates) {
|
||||
|
||||
Assert.notNull(updates, "Updates must not be null!");
|
||||
|
||||
for (Pair<Query, Update> update : updates) {
|
||||
update(update.getFirst(), update.getSecond(), false, true);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#upsert(org.springframework.data.mongodb.core.query.Query, org.springframework.data.mongodb.core.query.Update)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations upsert(Query query, Update update) {
|
||||
return update(query, update, true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#upsert(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations upsert(List<Pair<Query, Update>> updates) {
|
||||
|
||||
for (Pair<Query, Update> update : updates) {
|
||||
upsert(update.getFirst(), update.getSecond());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#remove(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations remove(Query query) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
bulk.find(query.getQueryObject()).remove();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#remove(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public BulkOperations remove(List<Query> removes) {
|
||||
|
||||
Assert.notNull(removes, "Removals must not be null!");
|
||||
|
||||
for (Query query : removes) {
|
||||
remove(query);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.BulkOperations#executeBulk()
|
||||
*/
|
||||
@Override
|
||||
public BulkWriteResult execute() {
|
||||
|
||||
MongoAction action = new MongoAction(defaultWriteConcern, MongoActionOperation.BULK, collectionName, entityType,
|
||||
null, null);
|
||||
WriteConcern writeConcern = writeConcernResolver.resolve(action);
|
||||
|
||||
try {
|
||||
|
||||
return writeConcern == null ? bulk.execute() : bulk.execute(writeConcern);
|
||||
|
||||
} catch (BulkWriteException o_O) {
|
||||
|
||||
DataAccessException toThrow = exceptionTranslator.translateExceptionIfPossible(o_O);
|
||||
throw toThrow == null ? o_O : toThrow;
|
||||
|
||||
} finally {
|
||||
this.bulk = initBulkOperation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param multi whether to issue a multi-update.
|
||||
* @return the {@link BulkOperations} with the update registered.
|
||||
*/
|
||||
private BulkOperations update(Query query, Update update, boolean upsert, boolean multi) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
Assert.notNull(update, "Update must not be null!");
|
||||
|
||||
BulkWriteRequestBuilder builder = bulk.find(query.getQueryObject());
|
||||
|
||||
if (upsert) {
|
||||
|
||||
if (multi) {
|
||||
builder.upsert().update(update.getUpdateObject());
|
||||
} else {
|
||||
builder.upsert().updateOne(update.getUpdateObject());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (multi) {
|
||||
builder.update(update.getUpdateObject());
|
||||
} else {
|
||||
builder.updateOne(update.getUpdateObject());
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private final BulkWriteOperation initBulkOperation() {
|
||||
|
||||
DBCollection collection = mongoOperations.getCollection(collectionName);
|
||||
|
||||
switch (bulkMode) {
|
||||
case ORDERED:
|
||||
return collection.initializeOrderedBulkOperation();
|
||||
case UNORDERED:
|
||||
return collection.initializeUnorderedBulkOperation();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("BulkMode was null!");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -15,17 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.IndexDefinition;
|
||||
import org.springframework.data.mongodb.core.index.IndexField;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
@@ -42,12 +40,12 @@ import com.mongodb.MongoException;
|
||||
*/
|
||||
public class DefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private static final Double ONE = Double.valueOf(1);
|
||||
private static final Double MINUS_ONE = Double.valueOf(-1);
|
||||
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
|
||||
private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final String collectionName;
|
||||
private final QueryMapper mapper;
|
||||
private final Class<?> type;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultIndexOperations}.
|
||||
@@ -56,12 +54,26 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
* @param collectionName must not be {@literal null}.
|
||||
*/
|
||||
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
|
||||
this(mongoOperations, collectionName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultIndexOperations}.
|
||||
*
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null}.
|
||||
* @param type Type used for mapping potential partial index filter expression. Can be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName, Class<?> type) {
|
||||
|
||||
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
|
||||
Assert.notNull(collectionName, "Collection name can not be null!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
this.collectionName = collectionName;
|
||||
this.mapper = new QueryMapper(mongoOperations.getConverter());
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -69,14 +81,43 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
|
||||
*/
|
||||
public void ensureIndex(final IndexDefinition indexDefinition) {
|
||||
|
||||
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject indexOptions = indexDefinition.getIndexOptions();
|
||||
if (indexOptions != null) {
|
||||
collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions);
|
||||
} else {
|
||||
collection.ensureIndex(indexDefinition.getIndexKeys());
|
||||
|
||||
if (indexOptions != null && indexOptions.containsField(PARTIAL_FILTER_EXPRESSION_KEY)) {
|
||||
|
||||
Assert.isInstanceOf(DBObject.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
|
||||
|
||||
indexOptions.put(PARTIAL_FILTER_EXPRESSION_KEY,
|
||||
mapper.getMappedObject((DBObject) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
|
||||
lookupPersistentEntity(type, collectionName)));
|
||||
}
|
||||
|
||||
if (indexOptions != null) {
|
||||
collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
|
||||
} else {
|
||||
collection.createIndex(indexDefinition.getIndexKeys());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
|
||||
|
||||
if (entityType != null) {
|
||||
return mongoOperations.getConverter().getMappingContext().getPersistentEntity(entityType);
|
||||
}
|
||||
|
||||
Collection<? extends MongoPersistentEntity<?>> entities = mongoOperations.getConverter().getMappingContext()
|
||||
.getPersistentEntities();
|
||||
|
||||
for (MongoPersistentEntity<?> entity : entities) {
|
||||
if (entity.getCollection().equals(collection)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -108,10 +149,12 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.IndexOperations#resetIndexCache()
|
||||
*/
|
||||
@Deprecated
|
||||
public void resetIndexCache() {
|
||||
mongoOperations.execute(collectionName, new CollectionCallback<Void>() {
|
||||
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
collection.resetIndexCache();
|
||||
|
||||
ReflectiveDBCollectionInvoker.resetIndexCache(collection);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -124,7 +167,9 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
public List<IndexInfo> getIndexInfo() {
|
||||
|
||||
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
|
||||
|
||||
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
|
||||
List<DBObject> dbObjectList = collection.getIndexInfo();
|
||||
return getIndexData(dbObjectList);
|
||||
}
|
||||
@@ -134,44 +179,7 @@ public class DefaultIndexOperations implements IndexOperations {
|
||||
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
|
||||
|
||||
for (DBObject ix : dbObjectList) {
|
||||
|
||||
DBObject keyDbObject = (DBObject) ix.get("key");
|
||||
int numberOfElements = keyDbObject.keySet().size();
|
||||
|
||||
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
|
||||
|
||||
for (String key : keyDbObject.keySet()) {
|
||||
|
||||
Object value = keyDbObject.get(key);
|
||||
|
||||
if (TWO_D_IDENTIFIERS.contains(value)) {
|
||||
indexFields.add(IndexField.geo(key));
|
||||
} else if ("text".equals(value)) {
|
||||
|
||||
DBObject weights = (DBObject) ix.get("weights");
|
||||
for (String fieldName : weights.keySet()) {
|
||||
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Double keyValue = new Double(value.toString());
|
||||
|
||||
if (ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, ASC));
|
||||
} else if (MINUS_ONE.equals(keyValue)) {
|
||||
indexFields.add(IndexField.create(key, DESC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String name = ix.get("name").toString();
|
||||
|
||||
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
|
||||
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
|
||||
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
|
||||
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
|
||||
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
|
||||
indexInfoList.add(IndexInfo.indexInfoOf(ix));
|
||||
}
|
||||
|
||||
return indexInfoList;
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2014-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 static java.util.UUID.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
|
||||
import org.springframework.data.mongodb.core.script.NamedMongoScript;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ScriptOperations} capable of saving and executing {@link ServerSideJavaScript}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
class DefaultScriptOperations implements ScriptOperations {
|
||||
|
||||
private static final String SCRIPT_COLLECTION_NAME = "system.js";
|
||||
private static final String SCRIPT_NAME_PREFIX = "func_";
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
|
||||
/**
|
||||
* Creates new {@link DefaultScriptOperations} using given {@link MongoOperations}.
|
||||
*
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
*/
|
||||
public DefaultScriptOperations(MongoOperations mongoOperations) {
|
||||
|
||||
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#register(org.springframework.data.mongodb.core.script.ExecutableMongoScript)
|
||||
*/
|
||||
@Override
|
||||
public NamedMongoScript register(ExecutableMongoScript script) {
|
||||
return register(new NamedMongoScript(generateScriptName(), script));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#register(org.springframework.data.mongodb.core.script.NamedMongoScript)
|
||||
*/
|
||||
@Override
|
||||
public NamedMongoScript register(NamedMongoScript script) {
|
||||
|
||||
Assert.notNull(script, "Script must not be null!");
|
||||
|
||||
mongoOperations.save(script, SCRIPT_COLLECTION_NAME);
|
||||
return script;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#execute(org.springframework.data.mongodb.core.script.ExecutableMongoScript, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public Object execute(final ExecutableMongoScript script, final Object... args) {
|
||||
|
||||
Assert.notNull(script, "Script must not be null!");
|
||||
|
||||
return mongoOperations.execute(new DbCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInDB(DB db) throws MongoException, DataAccessException {
|
||||
return db.eval(script.getCode(), convertScriptArgs(false, args));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#call(java.lang.String, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public Object call(final String scriptName, final Object... args) {
|
||||
|
||||
Assert.hasText(scriptName, "ScriptName must not be null or empty!");
|
||||
|
||||
return mongoOperations.execute(new DbCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInDB(DB db) throws MongoException, DataAccessException {
|
||||
return db.eval(String.format("%s(%s)", scriptName, convertAndJoinScriptArgs(args)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#exists(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean exists(String scriptName) {
|
||||
|
||||
Assert.hasText(scriptName, "ScriptName must not be null or empty!");
|
||||
|
||||
return mongoOperations.exists(query(where("name").is(scriptName)), NamedMongoScript.class, SCRIPT_COLLECTION_NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.ScriptOperations#getScriptNames()
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getScriptNames() {
|
||||
|
||||
List<NamedMongoScript> scripts = mongoOperations.findAll(NamedMongoScript.class, SCRIPT_COLLECTION_NAME);
|
||||
|
||||
if (CollectionUtils.isEmpty(scripts)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<String> scriptNames = new HashSet<String>();
|
||||
|
||||
for (NamedMongoScript script : scripts) {
|
||||
scriptNames.add(script.getName());
|
||||
}
|
||||
|
||||
return scriptNames;
|
||||
}
|
||||
|
||||
private Object[] convertScriptArgs(boolean quote, Object... args) {
|
||||
|
||||
if (ObjectUtils.isEmpty(args)) {
|
||||
return args;
|
||||
}
|
||||
|
||||
List<Object> convertedValues = new ArrayList<Object>(args.length);
|
||||
|
||||
for (Object arg : args) {
|
||||
convertedValues.add(arg instanceof String && quote ? String.format("'%s'", arg)
|
||||
: this.mongoOperations.getConverter().convertToMongoType(arg));
|
||||
}
|
||||
|
||||
return convertedValues.toArray();
|
||||
}
|
||||
|
||||
private String convertAndJoinScriptArgs(Object... args) {
|
||||
return ObjectUtils.isEmpty(args) ? "" : StringUtils.arrayToCommaDelimitedString(convertScriptArgs(true, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a valid name for the {@literal JavaScript}. MongoDB requires an id of type String for scripts. Calling
|
||||
* scripts having {@link ObjectId} as id fails. Therefore we create a random UUID without {@code -} (as this won't
|
||||
* work) an prefix the result with {@link #SCRIPT_NAME_PREFIX}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static String generateScriptName() {
|
||||
return SCRIPT_NAME_PREFIX + randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* Default {@link WriteConcernResolver} resolving the {@link WriteConcern} from the given {@link MongoAction}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
enum DefaultWriteConcernResolver implements WriteConcernResolver {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
public WriteConcern resolve(MongoAction action) {
|
||||
return action.getDefaultWriteConcern();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Value object to mitigate different representations of geo command execution results in MongoDB.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @soundtrack Fruitcake - Jeff Coffin (The Inside of the Outside)
|
||||
*/
|
||||
class GeoCommandStatistics {
|
||||
|
||||
private static final GeoCommandStatistics NONE = new GeoCommandStatistics(new BasicDBObject());
|
||||
|
||||
private final DBObject source;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoCommandStatistics} instance with the given source document.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
*/
|
||||
private GeoCommandStatistics(DBObject source) {
|
||||
|
||||
Assert.notNull(source, "Source document must not be null!");
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoCommandStatistics} from the given command result extracting the statistics.
|
||||
*
|
||||
* @param commandResult must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static GeoCommandStatistics from(DBObject commandResult) {
|
||||
|
||||
Assert.notNull(commandResult, "Command result must not be null!");
|
||||
|
||||
Object stats = commandResult.get("stats");
|
||||
return stats == null ? NONE : new GeoCommandStatistics((DBObject) stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average distance reported by the command result. Mitigating a removal of the field in case the command
|
||||
* didn't return any result introduced in MongoDB 3.2 RC1.
|
||||
*
|
||||
* @return
|
||||
* @see https://jira.mongodb.org/browse/SERVER-21024
|
||||
*/
|
||||
public double getAverageDistance() {
|
||||
|
||||
Object averageDistance = source.get("avgDistance");
|
||||
return averageDistance == null ? Double.NaN : (Double) averageDistance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2015-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 org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
|
||||
import org.springframework.data.web.config.SpringDataJacksonModules;
|
||||
|
||||
/**
|
||||
* Configuration class to expose {@link GeoJsonModule} as a Spring bean.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class GeoJsonConfiguration implements SpringDataJacksonModules {
|
||||
|
||||
@Bean
|
||||
public GeoJsonModule geoJsonModule() {
|
||||
return new GeoJsonModule();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,12 +20,12 @@ 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
|
||||
*/
|
||||
public interface IndexOperations {
|
||||
|
||||
@@ -51,7 +51,11 @@ public interface IndexOperations {
|
||||
|
||||
/**
|
||||
* Clears all indices that have not yet been applied to this collection.
|
||||
*
|
||||
* @deprecated since 1.7. The MongoDB Java driver version 3.0 does no longer support reseting the index cache.
|
||||
* @throws {@link UnsupportedOperationException} when used with MongoDB Java driver version 3.0.
|
||||
*/
|
||||
@Deprecated
|
||||
void resetIndexCache();
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MongoAction {
|
||||
* @param collectionName the collection name, must not be {@literal null} or empty.
|
||||
* @param entityType the POJO that is being operated against
|
||||
* @param document the converted DBObject from the POJO or Spring Update object
|
||||
* @param query the converted DBOjbect from the Spring Query object
|
||||
* @param query the converted DBObject from the Spring Query object
|
||||
*/
|
||||
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
|
||||
String collectionName, Class<?> entityType, DBObject document, DBObject query) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,5 +25,5 @@ package org.springframework.data.mongodb.core;
|
||||
*/
|
||||
public enum MongoActionOperation {
|
||||
|
||||
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE
|
||||
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE, BULK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientOptions;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
/**
|
||||
* Convenient factory for configuring MongoDB.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MongoClientFactoryBean extends AbstractFactoryBean<Mongo> implements PersistenceExceptionTranslator {
|
||||
|
||||
private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator();
|
||||
|
||||
private MongoClientOptions mongoClientOptions;
|
||||
private String host;
|
||||
private Integer port;
|
||||
private List<ServerAddress> replicaSetSeeds;
|
||||
private List<MongoCredential> credentials;
|
||||
|
||||
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR;
|
||||
|
||||
/**
|
||||
* Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}.
|
||||
*
|
||||
* @param mongoClientOptions
|
||||
*/
|
||||
public void setMongoClientOptions(MongoClientOptions mongoClientOptions) {
|
||||
this.mongoClientOptions = mongoClientOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of credentials to be used when creating {@link MongoClient}.
|
||||
*
|
||||
* @param credentials can be {@literal null}.
|
||||
*/
|
||||
public void setCredentials(MongoCredential[] credentials) {
|
||||
this.credentials = filterNonNullElementsAsList(credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link ServerAddress} to build up a replica set for.
|
||||
*
|
||||
* @param replicaSetSeeds can be {@literal null}.
|
||||
*/
|
||||
public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) {
|
||||
this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the host to connect to.
|
||||
*
|
||||
* @param host
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the port to connect to.
|
||||
*
|
||||
* @param port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link PersistenceExceptionTranslator} to use.
|
||||
*
|
||||
* @param exceptionTranslator
|
||||
*/
|
||||
public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) {
|
||||
this.exceptionTranslator = exceptionTranslator == null ? DEFAULT_EXCEPTION_TRANSLATOR : exceptionTranslator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<? extends Mongo> getObjectType() {
|
||||
return Mongo.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
*/
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return exceptionTranslator.translateExceptionIfPossible(ex);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@Override
|
||||
protected Mongo createInstance() throws Exception {
|
||||
|
||||
if (mongoClientOptions == null) {
|
||||
mongoClientOptions = MongoClientOptions.builder().build();
|
||||
}
|
||||
|
||||
if (credentials == null) {
|
||||
credentials = Collections.emptyList();
|
||||
}
|
||||
|
||||
return createMongoClient();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#destroyInstance(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected void destroyInstance(Mongo instance) throws Exception {
|
||||
instance.close();
|
||||
}
|
||||
|
||||
private MongoClient createMongoClient() throws UnknownHostException {
|
||||
|
||||
if (!CollectionUtils.isEmpty(replicaSetSeeds)) {
|
||||
return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions);
|
||||
}
|
||||
|
||||
return new MongoClient(createConfiguredOrDefaultServerAddress(), credentials, mongoClientOptions);
|
||||
}
|
||||
|
||||
private ServerAddress createConfiguredOrDefaultServerAddress() throws UnknownHostException {
|
||||
|
||||
ServerAddress defaultAddress = new ServerAddress();
|
||||
|
||||
return new ServerAddress(StringUtils.hasText(host) ? host : defaultAddress.getHost(),
|
||||
port != null ? port.intValue() : defaultAddress.getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given array as {@link List} with all {@literal null} elements removed.
|
||||
*
|
||||
* @param elements the elements to filter <T>, can be {@literal null}.
|
||||
* @return a new unmodifiable {@link List#} from the given elements without {@literal null}s.
|
||||
*/
|
||||
private static <T> List<T> filterNonNullElementsAsList(T[] elements) {
|
||||
|
||||
if (elements == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<T> candidateElements = new ArrayList<T>();
|
||||
|
||||
for (T element : elements) {
|
||||
if (element != null) {
|
||||
candidateElements.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(candidateElements);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
|
||||
import com.mongodb.DBDecoderFactory;
|
||||
import com.mongodb.DBEncoderFactory;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientOptions;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* A factory bean for construction of a {@link MongoClientOptions} instance.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClientOptions> {
|
||||
|
||||
private static final MongoClientOptions DEFAULT_MONGO_OPTIONS = MongoClientOptions.builder().build();
|
||||
|
||||
private String description = DEFAULT_MONGO_OPTIONS.getDescription();
|
||||
private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost();
|
||||
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
|
||||
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
|
||||
.getThreadsAllowedToBlockForConnectionMultiplier();
|
||||
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime();
|
||||
private int maxConnectionIdleTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionIdleTime();
|
||||
private int maxConnectionLifeTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionLifeTime();
|
||||
private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout();
|
||||
private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout();
|
||||
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive();
|
||||
private ReadPreference readPreference = DEFAULT_MONGO_OPTIONS.getReadPreference();
|
||||
private DBDecoderFactory dbDecoderFactory = DEFAULT_MONGO_OPTIONS.getDbDecoderFactory();
|
||||
private DBEncoderFactory dbEncoderFactory = DEFAULT_MONGO_OPTIONS.getDbEncoderFactory();
|
||||
private WriteConcern writeConcern = DEFAULT_MONGO_OPTIONS.getWriteConcern();
|
||||
private SocketFactory socketFactory = DEFAULT_MONGO_OPTIONS.getSocketFactory();
|
||||
private boolean cursorFinalizerEnabled = DEFAULT_MONGO_OPTIONS.isCursorFinalizerEnabled();
|
||||
private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans();
|
||||
private int heartbeatFrequency = DEFAULT_MONGO_OPTIONS.getHeartbeatFrequency();
|
||||
private int minHeartbeatFrequency = DEFAULT_MONGO_OPTIONS.getMinHeartbeatFrequency();
|
||||
private int heartbeatConnectTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatConnectTimeout();
|
||||
private int heartbeatSocketTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatSocketTimeout();
|
||||
private String requiredReplicaSetName = DEFAULT_MONGO_OPTIONS.getRequiredReplicaSetName();
|
||||
|
||||
private boolean ssl;
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
|
||||
/**
|
||||
* Set the {@link MongoClient} description.
|
||||
*
|
||||
* @param description
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum number of connections per host.
|
||||
*
|
||||
* @param minConnectionsPerHost
|
||||
*/
|
||||
public void setMinConnectionsPerHost(int minConnectionsPerHost) {
|
||||
this.minConnectionsPerHost = minConnectionsPerHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of connections allowed per host. Will block if run out. Default is 10. System property
|
||||
* {@code MONGO.POOLSIZE} can override
|
||||
*
|
||||
* @param connectionsPerHost
|
||||
*/
|
||||
public void setConnectionsPerHost(int connectionsPerHost) {
|
||||
this.connectionsPerHost = connectionsPerHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the multiplier for connectionsPerHost for # of threads that can block. Default is 5. If connectionsPerHost is
|
||||
* 10, and threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block more than that and an
|
||||
* exception will be thrown.
|
||||
*
|
||||
* @param threadsAllowedToBlockForConnectionMultiplier
|
||||
*/
|
||||
public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) {
|
||||
this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max wait time of a blocking thread for a connection. Default is 12000 ms (2 minutes)
|
||||
*
|
||||
* @param maxWaitTime
|
||||
*/
|
||||
public void setMaxWaitTime(int maxWaitTime) {
|
||||
this.maxWaitTime = maxWaitTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum idle time for a pooled connection.
|
||||
*
|
||||
* @param maxConnectionIdleTime
|
||||
*/
|
||||
public void setMaxConnectionIdleTime(int maxConnectionIdleTime) {
|
||||
this.maxConnectionIdleTime = maxConnectionIdleTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum life time for a pooled connection.
|
||||
*
|
||||
* @param maxConnectionLifeTime
|
||||
*/
|
||||
public void setMaxConnectionLifeTime(int maxConnectionLifeTime) {
|
||||
this.maxConnectionLifeTime = maxConnectionLifeTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connect timeout in milliseconds. 0 is default and infinite.
|
||||
*
|
||||
* @param connectTimeout
|
||||
*/
|
||||
public void setConnectTimeout(int connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket timeout. 0 is default and infinite.
|
||||
*
|
||||
* @param socketTimeout
|
||||
*/
|
||||
public void setSocketTimeout(int socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keep alive flag, controls whether or not to have socket keep alive timeout. Defaults to false.
|
||||
*
|
||||
* @param socketKeepAlive
|
||||
*/
|
||||
public void setSocketKeepAlive(boolean socketKeepAlive) {
|
||||
this.socketKeepAlive = socketKeepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ReadPreference}.
|
||||
*
|
||||
* @param readPreference
|
||||
*/
|
||||
public void setReadPreference(ReadPreference readPreference) {
|
||||
this.readPreference = readPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link WriteConcern} that will be the default value used when asking the {@link MongoDbFactory} for a DB
|
||||
* object.
|
||||
*
|
||||
* @param writeConcern
|
||||
*/
|
||||
public void setWriteConcern(WriteConcern writeConcern) {
|
||||
this.writeConcern = writeConcern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param socketFactory
|
||||
*/
|
||||
public void setSocketFactory(SocketFactory socketFactory) {
|
||||
this.socketFactory = socketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency that the driver will attempt to determine the current state of each server in the cluster.
|
||||
*
|
||||
* @param heartbeatFrequency
|
||||
*/
|
||||
public void setHeartbeatFrequency(int heartbeatFrequency) {
|
||||
this.heartbeatFrequency = heartbeatFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the event that the driver has to frequently re-check a server's availability, it will wait at least this long
|
||||
* since the previous check to avoid wasted effort.
|
||||
*
|
||||
* @param minHeartbeatFrequency
|
||||
*/
|
||||
public void setMinHeartbeatFrequency(int minHeartbeatFrequency) {
|
||||
this.minHeartbeatFrequency = minHeartbeatFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connect timeout for connections used for the cluster heartbeat.
|
||||
*
|
||||
* @param heartbeatConnectTimeout
|
||||
*/
|
||||
public void setHeartbeatConnectTimeout(int heartbeatConnectTimeout) {
|
||||
this.heartbeatConnectTimeout = heartbeatConnectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket timeout for connections used for the cluster heartbeat.
|
||||
*
|
||||
* @param heartbeatSocketTimeout
|
||||
*/
|
||||
public void setHeartbeatSocketTimeout(int heartbeatSocketTimeout) {
|
||||
this.heartbeatSocketTimeout = heartbeatSocketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the name of the replica set.
|
||||
*
|
||||
* @param requiredReplicaSetName
|
||||
*/
|
||||
public void setRequiredReplicaSetName(String requiredReplicaSetName) {
|
||||
this.requiredReplicaSetName = requiredReplicaSetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* This controls if the driver should us an SSL connection. Defaults to |@literal false}.
|
||||
*
|
||||
* @param ssl
|
||||
*/
|
||||
public void setSsl(boolean ssl) {
|
||||
this.ssl = ssl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link SSLSocketFactory} to use for the {@literal SSL} connection. If none is configured here,
|
||||
* {@link SSLSocketFactory#getDefault()} will be used.
|
||||
*
|
||||
* @param sslSocketFactory
|
||||
*/
|
||||
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
|
||||
this.sslSocketFactory = sslSocketFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@Override
|
||||
protected MongoClientOptions createInstance() throws Exception {
|
||||
|
||||
SocketFactory socketFactoryToUse = ssl ? (sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory
|
||||
.getDefault()) : this.socketFactory;
|
||||
|
||||
return MongoClientOptions.builder() //
|
||||
.alwaysUseMBeans(this.alwaysUseMBeans) //
|
||||
.connectionsPerHost(this.connectionsPerHost) //
|
||||
.connectTimeout(connectTimeout) //
|
||||
.cursorFinalizerEnabled(cursorFinalizerEnabled) //
|
||||
.dbDecoderFactory(dbDecoderFactory) //
|
||||
.dbEncoderFactory(dbEncoderFactory) //
|
||||
.description(description) //
|
||||
.heartbeatConnectTimeout(heartbeatConnectTimeout) //
|
||||
.heartbeatFrequency(heartbeatFrequency) //
|
||||
.heartbeatSocketTimeout(heartbeatSocketTimeout) //
|
||||
.maxConnectionIdleTime(maxConnectionIdleTime) //
|
||||
.maxConnectionLifeTime(maxConnectionLifeTime) //
|
||||
.maxWaitTime(maxWaitTime) //
|
||||
.minConnectionsPerHost(minConnectionsPerHost) //
|
||||
.minHeartbeatFrequency(minHeartbeatFrequency) //
|
||||
.readPreference(readPreference) //
|
||||
.requiredReplicaSetName(requiredReplicaSetName) //
|
||||
.socketFactory(socketFactoryToUse) //
|
||||
.socketKeepAlive(socketKeepAlive) //
|
||||
.socketTimeout(socketTimeout) //
|
||||
.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) //
|
||||
.writeConcern(writeConcern).build();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<?> getObjectType() {
|
||||
return MongoClientOptions.class;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.data.mongodb.core;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the
|
||||
@@ -34,6 +35,7 @@ import com.mongodb.Mongo;
|
||||
* @author Oliver Gierke
|
||||
* @author Randy Watler
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class MongoDbUtils {
|
||||
@@ -43,9 +45,7 @@ public abstract class MongoDbUtils {
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private MongoDbUtils() {
|
||||
|
||||
}
|
||||
private MongoDbUtils() {}
|
||||
|
||||
/**
|
||||
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
|
||||
@@ -65,11 +65,24 @@ public abstract class MongoDbUtils {
|
||||
* @param databaseName the database name, must not be {@literal null} or empty.
|
||||
* @param credentials the credentials to use, must not be {@literal null}.
|
||||
* @return the {@link DB} connection
|
||||
* @deprecated since 1.7. The {@link MongoClient} itself should hold credentials within
|
||||
* {@link MongoClient#getCredentialsList()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials) {
|
||||
return getDB(mongo, databaseName, credentials, databaseName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mongo
|
||||
* @param databaseName
|
||||
* @param credentials
|
||||
* @param authenticationDatabaseName
|
||||
* @return
|
||||
* @deprecated since 1.7. The {@link MongoClient} itself should hold credentials within
|
||||
* {@link MongoClient#getCredentialsList()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials,
|
||||
String authenticationDatabaseName) {
|
||||
|
||||
@@ -109,22 +122,9 @@ public abstract class MongoDbUtils {
|
||||
LOGGER.debug("Getting Mongo Database name=[{}]", databaseName);
|
||||
|
||||
DB db = mongo.getDB(databaseName);
|
||||
boolean credentialsGiven = credentials.hasUsername() && credentials.hasPassword();
|
||||
|
||||
DB authDb = databaseName.equals(authenticationDatabaseName) ? db : mongo.getDB(authenticationDatabaseName);
|
||||
|
||||
synchronized (authDb) {
|
||||
|
||||
if (credentialsGiven && !authDb.isAuthenticated()) {
|
||||
|
||||
String username = credentials.getUsername();
|
||||
String password = credentials.hasPassword() ? credentials.getPassword() : null;
|
||||
|
||||
if (!authDb.authenticate(username, password == null ? null : password.toCharArray())) {
|
||||
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], "
|
||||
+ credentials.toString(), databaseName, credentials);
|
||||
}
|
||||
}
|
||||
if (!(mongo instanceof MongoClient) && requiresAuthDbAuthentication(credentials)) {
|
||||
ReflectiveDbInvoker.authenticate(mongo, db, credentials, authenticationDatabaseName);
|
||||
}
|
||||
|
||||
// TX sync active, bind new database to thread
|
||||
@@ -181,16 +181,36 @@ public abstract class MongoDbUtils {
|
||||
* Perform actual closing of the Mongo DB object, catching and logging any cleanup exceptions thrown.
|
||||
*
|
||||
* @param db the DB to close (may be <code>null</code>)
|
||||
* @deprecated since 1.7. The main use case for this method is to ensure that applications can read their own
|
||||
* unacknowledged writes, but this is no longer so prevalent since the MongoDB Java driver version 3
|
||||
* started defaulting to acknowledged writes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void closeDB(DB db) {
|
||||
|
||||
if (db != null) {
|
||||
LOGGER.debug("Closing Mongo DB object");
|
||||
try {
|
||||
db.requestDone();
|
||||
ReflectiveDbInvoker.requestDone(db);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.debug("Unexpected exception on closing Mongo DB object", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if credentials present. In case we're using a mongo-java-driver version 3 or above we do not have the need
|
||||
* for authentication as the auth data has to be provided within the MongoClient
|
||||
*
|
||||
* @param credentials
|
||||
* @return
|
||||
*/
|
||||
private static boolean requiresAuthDbAuthentication(UserCredentials credentials) {
|
||||
|
||||
if (credentials == null || !credentials.hasUsername()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !MongoClientVersion.isMongo3Driver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,23 +15,25 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.BulkOperationException;
|
||||
import org.springframework.data.mongodb.UncategorizedMongoDbException;
|
||||
import org.springframework.data.mongodb.util.MongoDbErrorCodes;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.MongoCursorNotFoundException;
|
||||
import com.mongodb.BulkWriteException;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.MongoException.CursorNotFound;
|
||||
import com.mongodb.MongoException.DuplicateKey;
|
||||
import com.mongodb.MongoException.Network;
|
||||
import com.mongodb.MongoInternalException;
|
||||
import com.mongodb.MongoServerSelectionException;
|
||||
import com.mongodb.MongoSocketException;
|
||||
import com.mongodb.MongoTimeoutException;
|
||||
|
||||
/**
|
||||
* Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate
|
||||
@@ -40,9 +42,23 @@ import com.mongodb.MongoTimeoutException;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Michal Vich
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MongoExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
private static final Set<String> DULICATE_KEY_EXCEPTIONS = new HashSet<String>(
|
||||
Arrays.asList("MongoException.DuplicateKey", "DuplicateKeyException"));
|
||||
|
||||
private static final Set<String> RESOURCE_FAILURE_EXCEPTIONS = new HashSet<String>(
|
||||
Arrays.asList("MongoException.Network", "MongoSocketException", "MongoException.CursorNotFound",
|
||||
"MongoCursorNotFoundException", "MongoServerSelectionException", "MongoTimeoutException"));
|
||||
|
||||
private static final Set<String> RESOURCE_USAGE_EXCEPTIONS = new HashSet<String>(
|
||||
Arrays.asList("MongoInternalException"));
|
||||
|
||||
private static final Set<String> DATA_INTEGRETY_EXCEPTIONS = new HashSet<String>(
|
||||
Arrays.asList("WriteConcernException"));
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
@@ -51,41 +67,42 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
|
||||
|
||||
// Check for well-known MongoException subclasses.
|
||||
|
||||
if (ex instanceof DuplicateKey || ex instanceof DuplicateKeyException) {
|
||||
String exception = ClassUtils.getShortName(ClassUtils.getUserClass(ex.getClass()));
|
||||
|
||||
if (DULICATE_KEY_EXCEPTIONS.contains(exception)) {
|
||||
return new DuplicateKeyException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof Network || ex instanceof MongoSocketException) {
|
||||
if (RESOURCE_FAILURE_EXCEPTIONS.contains(exception)) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof CursorNotFound || ex instanceof MongoCursorNotFoundException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof MongoServerSelectionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof MongoTimeoutException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof MongoInternalException) {
|
||||
if (RESOURCE_USAGE_EXCEPTIONS.contains(exception)) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (DATA_INTEGRETY_EXCEPTIONS.contains(exception)) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof BulkWriteException) {
|
||||
return new BulkOperationException(ex.getMessage(), (BulkWriteException) ex);
|
||||
}
|
||||
|
||||
// All other MongoExceptions
|
||||
if (ex instanceof MongoException) {
|
||||
|
||||
int code = ((MongoException) ex).getCode();
|
||||
|
||||
if (code == 11000 || code == 11001) {
|
||||
if (MongoDbErrorCodes.isDuplicateKeyCode(code)) {
|
||||
throw new DuplicateKeyException(ex.getMessage(), ex);
|
||||
} else if (code == 12000 || code == 13440) {
|
||||
} else if (MongoDbErrorCodes.isDataAccessResourceFailureCode(code)) {
|
||||
throw new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
} else if (code == 10003 || code == 12001 || code == 12010 || code == 12011 || code == 12012) {
|
||||
} else if (MongoDbErrorCodes.isInvalidDataAccessApiUsageCode(code) || code == 10003 || code == 12001
|
||||
|| code == 12010 || code == 12011 || code == 12012) {
|
||||
throw new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
} else if (MongoDbErrorCodes.isPermissionDeniedCode(code)) {
|
||||
throw new PermissionDeniedDataAccessException(ex.getMessage(), ex);
|
||||
}
|
||||
return new UncategorizedMongoDbException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,9 +20,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
|
||||
@@ -40,12 +38,14 @@ import com.mongodb.WriteConcern;
|
||||
* @author Graeme Rocher
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @since 1.0
|
||||
* @deprecated since 1.7. Please use {@link MongoClientFactoryBean} instead.
|
||||
*/
|
||||
public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, DisposableBean,
|
||||
PersistenceExceptionTranslator {
|
||||
@Deprecated
|
||||
public class MongoFactoryBean extends AbstractFactoryBean<Mongo> implements PersistenceExceptionTranslator {
|
||||
|
||||
private Mongo mongo;
|
||||
private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator();
|
||||
|
||||
private MongoOptions mongoOptions;
|
||||
private String host;
|
||||
@@ -53,9 +53,11 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
private WriteConcern writeConcern;
|
||||
private List<ServerAddress> replicaSetSeeds;
|
||||
private List<ServerAddress> replicaPair;
|
||||
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR;
|
||||
|
||||
private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator();
|
||||
|
||||
/**
|
||||
* @param mongoOptions
|
||||
*/
|
||||
public void setMongoOptions(MongoOptions mongoOptions) {
|
||||
this.mongoOptions = mongoOptions;
|
||||
}
|
||||
@@ -66,7 +68,6 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setReplicaSetSeeds(ServerAddress[])} instead
|
||||
*
|
||||
* @param replicaPair
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -75,30 +76,19 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
}
|
||||
|
||||
/**
|
||||
* @param elements the elements to filter <T>
|
||||
* @return a new unmodifiable {@link List#} from the given elements without nulls
|
||||
* Configures the host to connect to.
|
||||
*
|
||||
* @param host
|
||||
*/
|
||||
private <T> List<T> filterNonNullElementsAsList(T[] elements) {
|
||||
|
||||
if (elements == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<T> candidateElements = new ArrayList<T>();
|
||||
|
||||
for (T element : elements) {
|
||||
if (element != null) {
|
||||
candidateElements.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(candidateElements);
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the port to connect to.
|
||||
*
|
||||
* @param port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
@@ -112,12 +102,13 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
this.writeConcern = writeConcern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link PersistenceExceptionTranslator} to use.
|
||||
*
|
||||
* @param exceptionTranslator can be {@literal null}.
|
||||
*/
|
||||
public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) {
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
public Mongo getObject() throws Exception {
|
||||
return mongo;
|
||||
this.exceptionTranslator = exceptionTranslator == null ? DEFAULT_EXCEPTION_TRANSLATOR : exceptionTranslator;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -128,14 +119,6 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
return Mongo.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
@@ -146,10 +129,10 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@Override
|
||||
protected Mongo createInstance() throws Exception {
|
||||
|
||||
Mongo mongo;
|
||||
ServerAddress defaultOptions = new ServerAddress();
|
||||
@@ -175,18 +158,42 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, D
|
||||
mongo.setWriteConcern(writeConcern);
|
||||
}
|
||||
|
||||
this.mongo = mongo;
|
||||
}
|
||||
|
||||
private boolean isNullOrEmpty(Collection<?> elements) {
|
||||
return elements == null || elements.isEmpty();
|
||||
return mongo;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#destroyInstance(java.lang.Object)
|
||||
*/
|
||||
public void destroy() throws Exception {
|
||||
this.mongo.close();
|
||||
@Override
|
||||
protected void destroyInstance(Mongo mongo) throws Exception {
|
||||
mongo.close();
|
||||
}
|
||||
|
||||
private static boolean isNullOrEmpty(Collection<?> elements) {
|
||||
return elements == null || elements.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given array as {@link List} with all {@literal null} elements removed.
|
||||
*
|
||||
* @param elements the elements to filter <T>
|
||||
* @return a new unmodifiable {@link List#} from the given elements without nulls
|
||||
*/
|
||||
private static <T> List<T> filterNonNullElementsAsList(T[] elements) {
|
||||
|
||||
if (elements == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<T> candidateElements = new ArrayList<T>();
|
||||
|
||||
for (T element : elements) {
|
||||
if (element != null) {
|
||||
candidateElements.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(candidateElements);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
@@ -33,10 +34,14 @@ import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
|
||||
import com.mongodb.CommandResult;
|
||||
import com.mongodb.Cursor;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.WriteResult;
|
||||
|
||||
/**
|
||||
@@ -85,9 +90,23 @@ public interface MongoOperations {
|
||||
*
|
||||
* @param command a MongoDB command
|
||||
* @param options query options to use
|
||||
* @deprecated since 1.7. Please use {@link #executeCommand(DBObject, ReadPreference)}, as the MongoDB Java driver
|
||||
* version 3 no longer supports this operation.
|
||||
*/
|
||||
@Deprecated
|
||||
CommandResult executeCommand(DBObject command, int options);
|
||||
|
||||
/**
|
||||
* Execute a MongoDB command. Any errors that result from executing this command will be converted into Spring's data
|
||||
* access exception hierarchy.
|
||||
*
|
||||
* @param command a MongoDB command, must not be {@literal null}.
|
||||
* @param readPreference read preferences to use, can be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
CommandResult executeCommand(DBObject command, ReadPreference readPreference);
|
||||
|
||||
/**
|
||||
* Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler.
|
||||
*
|
||||
@@ -143,9 +162,41 @@ public interface MongoOperations {
|
||||
* @param <T> return type
|
||||
* @param action callback that specified the MongoDB actions to perform on the DB instance
|
||||
* @return a result object returned by the action or <tt>null</tt>
|
||||
* @deprecated since 1.7 as the MongoDB Java driver version 3 does not longer support request boundaries via
|
||||
* {@link DB#requestStart()} and {@link DB#requestDone()}.
|
||||
*/
|
||||
@Deprecated
|
||||
<T> T executeInSession(DbCallback<T> action);
|
||||
|
||||
/**
|
||||
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} backed by a Mongo DB
|
||||
* {@link Cursor}.
|
||||
* <p>
|
||||
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
|
||||
*
|
||||
* @param <T> element return type
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
* @since 1.7
|
||||
*/
|
||||
<T> CloseableIterator<T> stream(Query query, Class<T> entityType);
|
||||
|
||||
/**
|
||||
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} and collection backed
|
||||
* by a Mongo DB {@link Cursor}.
|
||||
* <p>
|
||||
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
|
||||
*
|
||||
* @param <T> element return type
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
* @return will never be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
<T> CloseableIterator<T> stream(Query query, Class<T> entityType, String collectionName);
|
||||
|
||||
/**
|
||||
* Create an uncapped collection with a name based on the provided entity class.
|
||||
*
|
||||
@@ -155,7 +206,7 @@ public interface MongoOperations {
|
||||
<T> DBCollection createCollection(Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Create a collect with a name based on the provided entity class using the options.
|
||||
* Create a collection with a name based on the provided entity class using the options.
|
||||
*
|
||||
* @param entityClass class that determines the collection to create
|
||||
* @param collectionOptions options to use when creating the collection.
|
||||
@@ -172,7 +223,7 @@ public interface MongoOperations {
|
||||
DBCollection createCollection(String collectionName);
|
||||
|
||||
/**
|
||||
* Create a collect with the provided name and options.
|
||||
* Create a collection with the provided name and options.
|
||||
*
|
||||
* @param collectionName name of the collection
|
||||
* @param collectionOptions options to use when creating the collection.
|
||||
@@ -249,6 +300,42 @@ public interface MongoOperations {
|
||||
*/
|
||||
IndexOperations indexOps(Class<?> entityClass);
|
||||
|
||||
/**
|
||||
* Returns the {@link ScriptOperations} that can be performed on {@link com.mongodb.DB} level.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
ScriptOperations scriptOps();
|
||||
|
||||
/**
|
||||
* Returns a new {@link BulkOperations} for the given collection.
|
||||
*
|
||||
* @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.
|
||||
* @return {@link BulkOperations} on the named collection
|
||||
*/
|
||||
BulkOperations bulkOps(BulkMode mode, String collectionName);
|
||||
|
||||
/**
|
||||
* Returns a new {@link BulkOperations} for the given entity type.
|
||||
*
|
||||
* @param mode the {@link BulkMode} to use for bulk operations, must not be {@literal null}.
|
||||
* @param entityType the name of the entity class, must not be {@literal null}.
|
||||
* @return {@link BulkOperations} on the named collection associated of the given entity class.
|
||||
*/
|
||||
BulkOperations bulkOps(BulkMode mode, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Returns a new {@link BulkOperations} for the given entity type and collection name.
|
||||
*
|
||||
* @param mode the {@link BulkMode} to use for bulk operations, must not be {@literal null}.
|
||||
* @param entityClass the name of the entity class, must not be {@literal null}.
|
||||
* @param collectionName the name of the collection to work on, must not be {@literal null} or empty.
|
||||
* @return {@link BulkOperations} on the named collection associated with the given entity class.
|
||||
*/
|
||||
BulkOperations bulkOps(BulkMode mode, Class<?> entityType, String collectionName);
|
||||
|
||||
/**
|
||||
* Query for a list of objects of type T from the collection used by the entity class.
|
||||
* <p/>
|
||||
@@ -415,7 +502,9 @@ public interface MongoOperations {
|
||||
|
||||
/**
|
||||
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}. Will consider entity mapping
|
||||
* information to determine the collection the query is ran against.
|
||||
* 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}.
|
||||
@@ -424,7 +513,9 @@ public interface MongoOperations {
|
||||
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}.
|
||||
* Returns {@link GeoResults} 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}.
|
||||
@@ -553,8 +644,8 @@ public interface MongoOperations {
|
||||
<T> T findById(Object id, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify
|
||||
* <a/> to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
@@ -565,8 +656,8 @@ public interface MongoOperations {
|
||||
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify
|
||||
* <a/> to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification.
|
||||
@@ -578,8 +669,8 @@ public interface MongoOperations {
|
||||
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify
|
||||
* <a/> to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* {@link FindAndModifyOptions} into account.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
@@ -592,8 +683,8 @@ public interface MongoOperations {
|
||||
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
|
||||
* to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* Triggers <a href="http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify
|
||||
* <a/> to apply provided {@link Update} on documents matching {@link Criteria} of given {@link Query} taking
|
||||
* {@link FindAndModifyOptions} into account.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
@@ -652,14 +743,28 @@ public interface MongoOperations {
|
||||
long count(Query query, Class<?> entityClass);
|
||||
|
||||
/**
|
||||
* Returns the number of documents for the given {@link Query} querying the given collection.
|
||||
* 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)}.
|
||||
*
|
||||
* @param query
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @see #count(Query, Class, String)
|
||||
*/
|
||||
long count(Query query, String collectionName);
|
||||
|
||||
/**
|
||||
* Returns the number of documents for the given {@link Query} by querying the given collection using the given entity
|
||||
* class to map the given {@link Query}.
|
||||
*
|
||||
* @param query
|
||||
* @param entityClass must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
long count(Query query, Class<?> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Insert the object into the collection for the entity type of the object to save.
|
||||
* <p/>
|
||||
@@ -667,9 +772,9 @@ public interface MongoOperations {
|
||||
* <p/>
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
|
||||
* >Spring's Type Conversion"</a> for more details.
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Insert is used to initially store the object into the database. To update an existing object use the save method.
|
||||
@@ -724,9 +829,9 @@ public interface MongoOperations {
|
||||
* <p/>
|
||||
* If you object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
|
||||
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
|
||||
* href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert"
|
||||
* >Spring's Type Conversion"</a> for more details.
|
||||
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert" >
|
||||
* Spring's Type Conversion"</a> for more details.
|
||||
*
|
||||
* @param objectToSave the object to store in the collection
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
* Copyright 2010-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,41 +17,48 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
|
||||
import com.mongodb.MongoOptions;
|
||||
|
||||
/**
|
||||
* A factory bean for construction of a {@link MongoOptions} instance.
|
||||
*
|
||||
* A factory bean for construction of a {@link MongoOptions} instance. In case used with MongoDB Java driver version 3
|
||||
* porperties not suppprted by the driver will be ignored.
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @author Mark Pollack
|
||||
* @author Mike Saavedra
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @deprecated since 1.7. Please use {@link MongoClientOptionsFactoryBean} instead.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, InitializingBean {
|
||||
@Deprecated
|
||||
public class MongoOptionsFactoryBean extends AbstractFactoryBean<MongoOptions> {
|
||||
|
||||
private static final MongoOptions DEFAULT_MONGO_OPTIONS = new MongoOptions();
|
||||
|
||||
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.connectionsPerHost;
|
||||
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier;
|
||||
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.maxWaitTime;
|
||||
private int connectTimeout = DEFAULT_MONGO_OPTIONS.connectTimeout;
|
||||
private int socketTimeout = DEFAULT_MONGO_OPTIONS.socketTimeout;
|
||||
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.socketKeepAlive;
|
||||
private boolean autoConnectRetry = DEFAULT_MONGO_OPTIONS.autoConnectRetry;
|
||||
private long maxAutoConnectRetryTime = DEFAULT_MONGO_OPTIONS.maxAutoConnectRetryTime;
|
||||
private int writeNumber = DEFAULT_MONGO_OPTIONS.w;
|
||||
private int writeTimeout = DEFAULT_MONGO_OPTIONS.wtimeout;
|
||||
private boolean writeFsync = DEFAULT_MONGO_OPTIONS.fsync;
|
||||
private boolean slaveOk = DEFAULT_MONGO_OPTIONS.slaveOk;
|
||||
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
|
||||
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
|
||||
.getThreadsAllowedToBlockForConnectionMultiplier();
|
||||
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime();
|
||||
private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout();
|
||||
private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout();
|
||||
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive();
|
||||
private int writeNumber = DEFAULT_MONGO_OPTIONS.getW();
|
||||
private int writeTimeout = DEFAULT_MONGO_OPTIONS.getWtimeout();
|
||||
private boolean writeFsync = DEFAULT_MONGO_OPTIONS.isFsync();
|
||||
|
||||
private boolean autoConnectRetry = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker
|
||||
.getAutoConnectRetry(DEFAULT_MONGO_OPTIONS) : false;
|
||||
private long maxAutoConnectRetryTime = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker
|
||||
.getMaxAutoConnectRetryTime(DEFAULT_MONGO_OPTIONS) : -1;
|
||||
private boolean slaveOk = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker
|
||||
.getSlaveOk(DEFAULT_MONGO_OPTIONS) : false;
|
||||
|
||||
private boolean ssl;
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
|
||||
private MongoOptions options;
|
||||
|
||||
/**
|
||||
* Configures the maximum number of connections allowed per host until we will block.
|
||||
*
|
||||
@@ -144,7 +151,10 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
|
||||
|
||||
/**
|
||||
* Configures whether or not the system retries automatically on a failed connect. This defaults to {@literal false}.
|
||||
*
|
||||
* @deprecated since 1.7.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setAutoConnectRetry(boolean autoConnectRetry) {
|
||||
this.autoConnectRetry = autoConnectRetry;
|
||||
}
|
||||
@@ -154,7 +164,9 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
|
||||
* defaults to {@literal 0}, which means to use the default {@literal 15s} if {@link #autoConnectRetry} is on.
|
||||
*
|
||||
* @param maxAutoConnectRetryTime the maxAutoConnectRetryTime to set
|
||||
* @deprecated since 1.7
|
||||
*/
|
||||
@Deprecated
|
||||
public void setMaxAutoConnectRetryTime(long maxAutoConnectRetryTime) {
|
||||
this.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
|
||||
}
|
||||
@@ -163,7 +175,9 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
|
||||
* Specifies if the driver is allowed to read from secondaries or slaves. Defaults to {@literal false}.
|
||||
*
|
||||
* @param slaveOk true if the driver should read from secondaries or slaves.
|
||||
* @deprecated since 1.7
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSlaveOk(boolean slaveOk) {
|
||||
this.slaveOk = slaveOk;
|
||||
}
|
||||
@@ -194,40 +208,41 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
|
||||
this.sslSocketFactory = sslSocketFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
@Override
|
||||
protected MongoOptions createInstance() throws Exception {
|
||||
|
||||
if (MongoClientVersion.isMongo3Driver()) {
|
||||
throw new IllegalArgumentException(
|
||||
String
|
||||
.format("Usage of 'mongo-options' is no longer supported for MongoDB Java driver version 3 and above. Please use 'mongo-client-options' and refer to chapter 'MongoDB 3.0 Support' for details."));
|
||||
}
|
||||
|
||||
MongoOptions options = new MongoOptions();
|
||||
|
||||
options.connectionsPerHost = connectionsPerHost;
|
||||
options.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
|
||||
options.maxWaitTime = maxWaitTime;
|
||||
options.connectTimeout = connectTimeout;
|
||||
options.socketTimeout = socketTimeout;
|
||||
options.socketKeepAlive = socketKeepAlive;
|
||||
options.autoConnectRetry = autoConnectRetry;
|
||||
options.maxAutoConnectRetryTime = maxAutoConnectRetryTime;
|
||||
options.slaveOk = slaveOk;
|
||||
options.w = writeNumber;
|
||||
options.wtimeout = writeTimeout;
|
||||
options.fsync = writeFsync;
|
||||
options.setConnectionsPerHost(connectionsPerHost);
|
||||
options.setThreadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier);
|
||||
options.setMaxWaitTime(maxWaitTime);
|
||||
options.setConnectTimeout(connectTimeout);
|
||||
options.setSocketTimeout(socketTimeout);
|
||||
options.setSocketKeepAlive(socketKeepAlive);
|
||||
|
||||
options.setW(writeNumber);
|
||||
options.setWtimeout(writeTimeout);
|
||||
options.setFsync(writeFsync);
|
||||
|
||||
if (ssl) {
|
||||
options.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault());
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
}
|
||||
ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry);
|
||||
ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime);
|
||||
ReflectiveMongoOptionsInvoker.setSlaveOk(options, slaveOk);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
public MongoOptions getObject() {
|
||||
return this.options;
|
||||
return options;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -237,12 +252,4 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
|
||||
public Class<?> getObjectType() {
|
||||
return MongoOptions.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveDBCollectionInvoker} provides reflective access to {@link DBCollection} API that is not consistently
|
||||
* available for various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
class ReflectiveDBCollectionInvoker {
|
||||
|
||||
private static final Method GEN_INDEX_NAME_METHOD;
|
||||
private static final Method RESET_INDEX_CHACHE_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
GEN_INDEX_NAME_METHOD = findMethod(DBCollection.class, "genIndexName", DBObject.class);
|
||||
RESET_INDEX_CHACHE_METHOD = findMethod(DBCollection.class, "resetIndexCache");
|
||||
}
|
||||
|
||||
private ReflectiveDBCollectionInvoker() {}
|
||||
|
||||
/**
|
||||
* Convenience method to generate an index name from the set of fields it is over. Will fall back to a MongoDB Java
|
||||
* driver version 2 compatible way of generating index name in case of {@link MongoClientVersion#isMongo3Driver()}.
|
||||
*
|
||||
* @param keys the names of the fields used in this index
|
||||
* @return
|
||||
*/
|
||||
public static String generateIndexName(DBObject keys) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return genIndexName(keys);
|
||||
}
|
||||
return (String) invokeMethod(GEN_INDEX_NAME_METHOD, null, keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of MongoDB Java driver version 2 all indices that have not yet been applied to this collection will be
|
||||
* cleared. Since this method is not available for the MongoDB Java driver version 3 the operation will throw
|
||||
* {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @param dbCollection
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public static void resetIndexCache(DBCollection dbCollection) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
throw new UnsupportedOperationException("The mongo java driver 3 does no loger support resetIndexCache!");
|
||||
}
|
||||
|
||||
invokeMethod(RESET_INDEX_CHACHE_METHOD, dbCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Borrowed from MongoDB Java driver version 2. See <a
|
||||
* href="http://github.com/mongodb/mongo-java-driver/blob/r2.13.0/src/main/com/mongodb/DBCollection.java#L754"
|
||||
* >http://github.com/mongodb/mongo-java-driver/blob/r2.13.0/src/main/com/mongodb/DBCollection.java#L754</a>
|
||||
*
|
||||
* @param keys
|
||||
* @return
|
||||
*/
|
||||
private static String genIndexName(DBObject keys) {
|
||||
|
||||
StringBuilder name = new StringBuilder();
|
||||
|
||||
for (String s : keys.keySet()) {
|
||||
|
||||
if (name.length() > 0) {
|
||||
name.append('_');
|
||||
}
|
||||
|
||||
name.append(s).append('_');
|
||||
Object val = keys.get(s);
|
||||
|
||||
if (val instanceof Number || val instanceof String) {
|
||||
name.append(val.toString().replace(' ', '_'));
|
||||
}
|
||||
}
|
||||
|
||||
return name.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
|
||||
import org.springframework.data.mongodb.util.MongoClientVersion;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveDbInvoker} provides reflective access to {@link DB} API that is not consistently available for
|
||||
* various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
final class ReflectiveDbInvoker {
|
||||
|
||||
private static final Method DB_IS_AUTHENTICATED_METHOD;
|
||||
private static final Method DB_AUTHENTICATE_METHOD;
|
||||
private static final Method DB_REQUEST_DONE_METHOD;
|
||||
private static final Method DB_ADD_USER_METHOD;
|
||||
private static final Method DB_REQUEST_START_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
DB_IS_AUTHENTICATED_METHOD = findMethod(DB.class, "isAuthenticated");
|
||||
DB_AUTHENTICATE_METHOD = findMethod(DB.class, "authenticate", String.class, char[].class);
|
||||
DB_REQUEST_DONE_METHOD = findMethod(DB.class, "requestDone");
|
||||
DB_ADD_USER_METHOD = findMethod(DB.class, "addUser", String.class, char[].class);
|
||||
DB_REQUEST_START_METHOD = findMethod(DB.class, "requestStart");
|
||||
}
|
||||
|
||||
private ReflectiveDbInvoker() {}
|
||||
|
||||
/**
|
||||
* Authenticate against database using provided credentials in case of a MongoDB Java driver version 2.
|
||||
*
|
||||
* @param mongo must not be {@literal null}.
|
||||
* @param db must not be {@literal null}.
|
||||
* @param credentials must not be {@literal null}.
|
||||
* @param authenticationDatabaseName
|
||||
*/
|
||||
public static void authenticate(Mongo mongo, DB db, UserCredentials credentials, String authenticationDatabaseName) {
|
||||
|
||||
String databaseName = db.getName();
|
||||
|
||||
DB authDb = databaseName.equals(authenticationDatabaseName) ? db : mongo.getDB(authenticationDatabaseName);
|
||||
|
||||
synchronized (authDb) {
|
||||
|
||||
Boolean isAuthenticated = (Boolean) invokeMethod(DB_IS_AUTHENTICATED_METHOD, authDb);
|
||||
if (!isAuthenticated) {
|
||||
|
||||
String username = credentials.getUsername();
|
||||
String password = credentials.hasPassword() ? credentials.getPassword() : null;
|
||||
|
||||
Boolean authenticated = (Boolean) invokeMethod(DB_AUTHENTICATE_METHOD, authDb, username,
|
||||
password == null ? null : password.toCharArray());
|
||||
if (!authenticated) {
|
||||
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], "
|
||||
+ credentials.toString(), databaseName, credentials);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new 'consistent request' in case of MongoDB Java driver version 2. Will do nothing for MongoDB Java driver
|
||||
* version 3 since the operation is no longer available.
|
||||
*
|
||||
* @param db
|
||||
*/
|
||||
public static void requestStart(DB db) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(DB_REQUEST_START_METHOD, db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the current 'consistent request'. a new 'consistent request' in case of MongoDB Java driver version 2. Will do
|
||||
* nothing for MongoDB Java driver version 3 since the operation is no longer available
|
||||
*
|
||||
* @param db
|
||||
*/
|
||||
public static void requestDone(DB db) {
|
||||
|
||||
if (MongoClientVersion.isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(DB_REQUEST_DONE_METHOD, db);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param db
|
||||
* @param username
|
||||
* @param password
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public static void addUser(DB db, String username, char[] password) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Please use DB.command(…) to call either the createUser or updateUser command!");
|
||||
}
|
||||
|
||||
invokeMethod(DB_ADD_USER_METHOD, db, username, password);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.MapReduceCommand;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveMapReduceInvoker} provides reflective access to {@link MapReduceCommand} API that is not
|
||||
* consistently available for various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
final class ReflectiveMapReduceInvoker {
|
||||
|
||||
private static final Method ADD_EXTRA_OPTION_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
ADD_EXTRA_OPTION_METHOD = findMethod(MapReduceCommand.class, "addExtraOption", String.class, Object.class);
|
||||
}
|
||||
|
||||
private ReflectiveMapReduceInvoker() {}
|
||||
|
||||
/**
|
||||
* Sets the extra option for MongoDB Java driver version 2. Will do nothing for MongoDB Java driver version 2.
|
||||
*
|
||||
* @param cmd can be {@literal null} for MongoDB Java driver version 2.
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public static void addExtraOption(MapReduceCommand cmd, String key, Object value) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.notNull(cmd, "MapReduceCommand must not be null!");
|
||||
invokeMethod(ADD_EXTRA_OPTION_METHOD, cmd, key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.mongodb.MongoOptions;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveMongoOptionsInvoker} provides reflective access to {@link MongoOptions} API that is not consistently
|
||||
* available for various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class ReflectiveMongoOptionsInvoker {
|
||||
|
||||
private static final Method GET_AUTO_CONNECT_RETRY_METHOD;
|
||||
private static final Method SET_AUTO_CONNECT_RETRY_METHOD;
|
||||
private static final Method GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD;
|
||||
private static final Method SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
SET_AUTO_CONNECT_RETRY_METHOD = ReflectionUtils
|
||||
.findMethod(MongoOptions.class, "setAutoConnectRetry", boolean.class);
|
||||
GET_AUTO_CONNECT_RETRY_METHOD = ReflectionUtils.findMethod(MongoOptions.class, "isAutoConnectRetry");
|
||||
SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = ReflectionUtils.findMethod(MongoOptions.class,
|
||||
"setMaxAutoConnectRetryTime", long.class);
|
||||
GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = ReflectionUtils.findMethod(MongoOptions.class,
|
||||
"getMaxAutoConnectRetryTime");
|
||||
}
|
||||
|
||||
private ReflectiveMongoOptionsInvoker() {}
|
||||
|
||||
/**
|
||||
* Sets the retry connection flag for MongoDB Java driver version 2. Will do nothing for MongoDB Java driver version 3
|
||||
* since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @param autoConnectRetry
|
||||
*/
|
||||
public static void setAutoConnectRetry(MongoOptions options, boolean autoConnectRetry) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(SET_AUTO_CONNECT_RETRY_METHOD, options, autoConnectRetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maxAutoConnectRetryTime attribute for MongoDB Java driver version 2. Will do nothing for MongoDB Java
|
||||
* driver version 3 since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @param maxAutoConnectRetryTime
|
||||
*/
|
||||
public static void setMaxAutoConnectRetryTime(MongoOptions options, long maxAutoConnectRetryTime) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options, maxAutoConnectRetryTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the slaveOk attribute for MongoDB Java driver version 2. Will do nothing for MongoDB Java driver version 3
|
||||
* since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @param slaveOk
|
||||
*/
|
||||
public static void setSlaveOk(MongoOptions options, boolean slaveOk) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return;
|
||||
}
|
||||
|
||||
new DirectFieldAccessor(options).setPropertyValue("slaveOk", slaveOk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slaveOk attribute for MongoDB Java driver version 2. Throws {@link UnsupportedOperationException} for
|
||||
* MongoDB Java driver version 3 since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @return
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public static boolean getSlaveOk(MongoOptions options) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot get value for autoConnectRetry which has been removed in MongoDB Java driver version 3.");
|
||||
}
|
||||
|
||||
return ((Boolean) new DirectFieldAccessor(options).getPropertyValue("slaveOk")).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the autoConnectRetry attribute for MongoDB Java driver version 2. Throws {@link UnsupportedOperationException}
|
||||
* for MongoDB Java driver version 3 since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @return
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public static boolean getAutoConnectRetry(MongoOptions options) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot get value for autoConnectRetry which has been removed in MongoDB Java driver version 3.");
|
||||
}
|
||||
|
||||
return ((Boolean) invokeMethod(GET_AUTO_CONNECT_RETRY_METHOD, options)).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maxAutoConnectRetryTime attribute for MongoDB Java driver version 2. Throws
|
||||
* {@link UnsupportedOperationException} for MongoDB Java driver version 3 since the method has been removed.
|
||||
*
|
||||
* @param options can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @return
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public static long getMaxAutoConnectRetryTime(MongoOptions options) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot get value for maxAutoConnectRetryTime which has been removed in MongoDB Java driver version 3.");
|
||||
}
|
||||
|
||||
return ((Long) invokeMethod(GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options)).longValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveWriteConcernInvoker} provides reflective access to {@link WriteConcern} API that is not consistently
|
||||
* available for various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
class ReflectiveWriteConcernInvoker {
|
||||
|
||||
private static final WriteConcern NONE_OR_UNACKNOWLEDGED;
|
||||
|
||||
static {
|
||||
|
||||
NONE_OR_UNACKNOWLEDGED = isMongo3Driver() ? WriteConcern.UNACKNOWLEDGED : (WriteConcern) new DirectFieldAccessor(
|
||||
new WriteConcern()).getPropertyValue("NONE");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link WriteConcern#NONE} for MongoDB Java driver version 2, otherwise {@link WriteConcern#UNACKNOWLEDGED}.
|
||||
*/
|
||||
public static WriteConcern noneOrUnacknowledged() {
|
||||
return NONE_OR_UNACKNOWLEDGED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.util.MongoClientVersion.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.WriteResult;
|
||||
|
||||
/**
|
||||
* {@link ReflectiveWriteResultInvoker} provides reflective access to {@link WriteResult} API that is not consistently
|
||||
* available for various driver versions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
final class ReflectiveWriteResultInvoker {
|
||||
|
||||
private static final Method GET_ERROR_METHOD;
|
||||
private static final Method WAS_ACKNOWLEDGED_METHOD;
|
||||
|
||||
private ReflectiveWriteResultInvoker() {}
|
||||
|
||||
static {
|
||||
|
||||
GET_ERROR_METHOD = findMethod(WriteResult.class, "getError");
|
||||
WAS_ACKNOWLEDGED_METHOD = findMethod(WriteResult.class, "wasAcknowledged");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param writeResult can be {@literal null} for MongoDB Java driver version 3.
|
||||
* @return null in case of MongoDB Java driver version 3 since errors are thrown as {@link MongoException}.
|
||||
*/
|
||||
public static String getError(WriteResult writeResult) {
|
||||
|
||||
if (isMongo3Driver()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (String) invokeMethod(GET_ERROR_METHOD, writeResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param writeResult
|
||||
* @return return in case of MongoDB Java driver version 2.
|
||||
*/
|
||||
public static boolean wasAcknowledged(WriteResult writeResult) {
|
||||
return isMongo3Driver() ? ((Boolean) invokeMethod(WAS_ACKNOWLEDGED_METHOD, writeResult)).booleanValue() : true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.Set;
|
||||
|
||||
import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
|
||||
import org.springframework.data.mongodb.core.script.NamedMongoScript;
|
||||
|
||||
import com.mongodb.DB;
|
||||
|
||||
/**
|
||||
* Script operations on {@link com.mongodb.DB} level. Allows interaction with server side JavaScript functions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface ScriptOperations {
|
||||
|
||||
/**
|
||||
* Store given {@link ExecutableMongoScript} generating a syntheitcal name so that it can be called by it
|
||||
* subsequently.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @return {@link NamedMongoScript} with name under which the {@code JavaScript} function can be called.
|
||||
*/
|
||||
NamedMongoScript register(ExecutableMongoScript script);
|
||||
|
||||
/**
|
||||
* Registers the given {@link NamedMongoScript} in the database.
|
||||
*
|
||||
* @param script the {@link NamedMongoScript} to be registered.
|
||||
* @return
|
||||
*/
|
||||
NamedMongoScript register(NamedMongoScript script);
|
||||
|
||||
/**
|
||||
* Executes the {@literal script} by either calling it via its {@literal name} or directly sending it.
|
||||
*
|
||||
* @param script must not be {@literal null}.
|
||||
* @param args arguments to pass on for script execution.
|
||||
* @return the script evaluation result.
|
||||
* @throws org.springframework.dao.DataAccessException
|
||||
*/
|
||||
Object execute(ExecutableMongoScript script, Object... args);
|
||||
|
||||
/**
|
||||
* Call the {@literal JavaScript} by its name.
|
||||
*
|
||||
* @param scriptName must not be {@literal null} or empty.
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
Object call(String scriptName, Object... args);
|
||||
|
||||
/**
|
||||
* Checks {@link DB} for existence of {@link ServerSideJavaScript} with given name.
|
||||
*
|
||||
* @param scriptName must not be {@literal null} or empty.
|
||||
* @return false if no {@link ServerSideJavaScript} with given name exists.
|
||||
*/
|
||||
boolean exists(String scriptName);
|
||||
|
||||
/**
|
||||
* Returns names of {@literal JavaScript} functions that can be called.
|
||||
*
|
||||
* @return empty {@link Set} if no scripts found.
|
||||
*/
|
||||
Set<String> getScriptNames();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ import java.net.UnknownHostException;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
@@ -27,6 +28,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.MongoURI;
|
||||
import com.mongodb.WriteConcern;
|
||||
@@ -37,6 +40,7 @@ import com.mongodb.WriteConcern;
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
|
||||
@@ -54,7 +58,9 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
*
|
||||
* @param mongo Mongo instance, must not be {@literal null}.
|
||||
* @param databaseName database name, not be {@literal null} or empty.
|
||||
* @deprecated since 1.7. Please use {@link #SimpleMongoDbFactory(MongoClient, String)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName) {
|
||||
this(mongo, databaseName, null);
|
||||
}
|
||||
@@ -65,7 +71,9 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
* @param mongo Mongo instance, must not be {@literal null}.
|
||||
* @param databaseName Database name, must not be {@literal null} or empty.
|
||||
* @param credentials username and password.
|
||||
* @deprecated since 1.7. The credentials used should be provided by {@link MongoClient#getCredentialsList()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials) {
|
||||
this(mongo, databaseName, credentials, false, null);
|
||||
}
|
||||
@@ -77,7 +85,9 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
* @param databaseName Database name, must not be {@literal null} or empty.
|
||||
* @param credentials username and password.
|
||||
* @param authenticationDatabaseName the database name to use for authentication
|
||||
* @deprecated since 1.7. The credentials used should be provided by {@link MongoClient#getCredentialsList()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
|
||||
String authenticationDatabaseName) {
|
||||
this(mongo, databaseName, credentials, false, authenticationDatabaseName);
|
||||
@@ -90,16 +100,44 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
* @throws MongoException
|
||||
* @throws UnknownHostException
|
||||
* @see MongoURI
|
||||
* @deprecated since 1.7. Please use {@link #SimpleMongoDbFactory(MongoClientURI)} instead.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Deprecated
|
||||
public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException {
|
||||
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())),
|
||||
true, uri.getDatabase());
|
||||
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())), true,
|
||||
uri.getDatabase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @throws UnknownHostException
|
||||
* @since 1.7
|
||||
*/
|
||||
public SimpleMongoDbFactory(MongoClientURI uri) throws UnknownHostException {
|
||||
this(new MongoClient(uri), uri.getDatabase(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}.
|
||||
*
|
||||
* @param mongoClient must not be {@literal null}.
|
||||
* @param databaseName must not be {@literal null}.
|
||||
* @since 1.7
|
||||
*/
|
||||
public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) {
|
||||
this(mongoClient, databaseName, false);
|
||||
}
|
||||
|
||||
private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
|
||||
boolean mongoInstanceCreated, String authenticationDatabaseName) {
|
||||
|
||||
if (mongo instanceof MongoClient && (credentials != null && !UserCredentials.NO_CREDENTIALS.equals(credentials))) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"Usage of 'UserCredentials' with 'MongoClient' is no longer supported. Please use 'MongoCredential' for 'MongoClient' or just 'Mongo'.");
|
||||
}
|
||||
|
||||
Assert.notNull(mongo, "Mongo must not be null");
|
||||
Assert.hasText(databaseName, "Database name must not be empty");
|
||||
Assert.isTrue(databaseName.matches("[\\w-]+"),
|
||||
@@ -117,6 +155,25 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
"Authentication database name must only contain letters, numbers, underscores and dashes!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param client
|
||||
* @param databaseName
|
||||
* @param mongoInstanceCreated
|
||||
* @since 1.7
|
||||
*/
|
||||
private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) {
|
||||
|
||||
Assert.notNull(client, "MongoClient must not be null!");
|
||||
Assert.hasText(databaseName, "Database name must not be empty!");
|
||||
|
||||
this.mongo = client;
|
||||
this.databaseName = databaseName;
|
||||
this.mongoInstanceCreated = mongoInstanceCreated;
|
||||
this.exceptionTranslator = new MongoExceptionTranslator();
|
||||
this.credentials = UserCredentials.NO_CREDENTIALS;
|
||||
this.authenticationDatabaseName = databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link WriteConcern} to be used on the {@link DB} instance being created.
|
||||
*
|
||||
@@ -138,6 +195,7 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public DB getDb(String dbName) throws DataAccessException {
|
||||
|
||||
Assert.hasText(dbName, "Database name must not be empty.");
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
abstract class AbstractAggregationExpression implements AggregationExpression {
|
||||
|
||||
private final Object value;
|
||||
|
||||
protected AbstractAggregationExpression(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return toDbObject(this.value, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
Object valueToUse;
|
||||
if (value instanceof List) {
|
||||
|
||||
List<Object> arguments = (List<Object>) value;
|
||||
List<Object> args = new ArrayList<Object>(arguments.size());
|
||||
|
||||
for (Object val : arguments) {
|
||||
args.add(unpack(val, context));
|
||||
}
|
||||
valueToUse = args;
|
||||
} else if (value instanceof java.util.Map) {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
for (java.util.Map.Entry<String, Object> entry : ((java.util.Map<String, Object>) value).entrySet()) {
|
||||
dbo.put(entry.getKey(), unpack(entry.getValue(), context));
|
||||
}
|
||||
valueToUse = dbo;
|
||||
} else {
|
||||
valueToUse = unpack(value, context);
|
||||
}
|
||||
|
||||
return new BasicDBObject(getMongoMethod(), valueToUse);
|
||||
}
|
||||
|
||||
protected static List<Field> asFields(String... fieldRefs) {
|
||||
|
||||
if (ObjectUtils.isEmpty(fieldRefs)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Fields.fields(fieldRefs).asList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object unpack(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
if (value instanceof List) {
|
||||
|
||||
List<Object> sourceList = (List<Object>) value;
|
||||
List<Object> mappedList = new ArrayList<Object>(sourceList.size());
|
||||
|
||||
for (Object item : sourceList) {
|
||||
mappedList.add(unpack(item, context));
|
||||
}
|
||||
return mappedList;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
protected List<Object> append(Object value) {
|
||||
|
||||
if (this.value instanceof List) {
|
||||
|
||||
List<Object> clone = new ArrayList<Object>((List) this.value);
|
||||
|
||||
if (value instanceof List) {
|
||||
for (Object val : (List) value) {
|
||||
clone.add(val);
|
||||
}
|
||||
} else {
|
||||
clone.add(value);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
return Arrays.asList(this.value, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected java.util.Map<String, Object> append(String key, Object value) {
|
||||
|
||||
if (!(this.value instanceof java.util.Map)) {
|
||||
throw new IllegalArgumentException("o_O");
|
||||
}
|
||||
java.util.Map<String, Object> clone = new LinkedHashMap<String, Object>((java.util.Map<String, Object>) this.value);
|
||||
clone.put(key, value);
|
||||
return clone;
|
||||
|
||||
}
|
||||
|
||||
protected List<Object> values() {
|
||||
|
||||
if (value instanceof List) {
|
||||
return new ArrayList<Object>((List) value);
|
||||
}
|
||||
if (value instanceof java.util.Map) {
|
||||
return new ArrayList<Object>(((java.util.Map) value).values());
|
||||
}
|
||||
return new ArrayList<Object>(Collections.singletonList(value));
|
||||
}
|
||||
|
||||
protected abstract String getMongoMethod();
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal accumulator} aggregation operations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @soundtrack Rage Against The Machine - Killing In The Name
|
||||
*/
|
||||
public class AccumulatorOperators {
|
||||
|
||||
/**
|
||||
* Take the numeric value referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AccumulatorOperatorFactory valueOf(String fieldReference) {
|
||||
return new AccumulatorOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the numeric value referenced resulting from given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AccumulatorOperatorFactory valueOf(AggregationExpression expression) {
|
||||
return new AccumulatorOperatorFactory(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class AccumulatorOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link AccumulatorOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public AccumulatorOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AccumulatorOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public AccumulatorOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates and
|
||||
* returns the sum.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Sum sum() {
|
||||
return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
||||
* average value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Avg avg() {
|
||||
return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
||||
* maximum value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Max max() {
|
||||
return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
||||
* minimum value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Min min() {
|
||||
return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates the
|
||||
* population standard deviation of the input values.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public StdDevPop stdDevPop() {
|
||||
return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates the
|
||||
* sample standard deviation of the input values.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public StdDevSamp stdDevSamp() {
|
||||
return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return fieldReference != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $sum}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Sum extends AbstractAggregationExpression {
|
||||
|
||||
private Sum(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$sum";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Sum sumOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Sum(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Sum sumOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Sum(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Sum and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Sum(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Sum and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Sum(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $avg}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Avg extends AbstractAggregationExpression {
|
||||
|
||||
private Avg(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$avg";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Avg}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Avg avgOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Avg(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Avg}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Avg avgOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Avg(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Avg} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Avg and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Avg(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Avg} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Avg and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Avg(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $max}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Max extends AbstractAggregationExpression {
|
||||
|
||||
private Max(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$max";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Max}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Max maxOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Max(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Max}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Max maxOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Max(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Max} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Max and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Max(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Max} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Max and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Max(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $min}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Min extends AbstractAggregationExpression {
|
||||
|
||||
private Min(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$min";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Min}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Min minOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Min(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Min}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Min minOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Min(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Min} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Min and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Min(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Min} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Min and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Min(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $stdDevPop}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class StdDevPop extends AbstractAggregationExpression {
|
||||
|
||||
private StdDevPop(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$stdDevPop";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevPop}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static StdDevPop stdDevPopOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new StdDevPop(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevPop} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static StdDevPop stdDevPopOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new StdDevPop(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevPop} with all previously added arguments appending the given one. <br/>
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public StdDevPop and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new StdDevPop(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevSamp} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public StdDevPop and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new StdDevPop(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $stdDevSamp}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class StdDevSamp extends AbstractAggregationExpression {
|
||||
|
||||
private StdDevSamp(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$stdDevSamp";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevSamp}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static StdDevSamp stdDevSampOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new StdDevSamp(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevSamp}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static StdDevSamp stdDevSampOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new StdDevSamp(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevSamp} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public StdDevSamp and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new StdDevSamp(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link StdDevSamp} with all previously added arguments appending the given one. <br />
|
||||
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public StdDevSamp and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new StdDevSamp(append(expression));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DBObject toDbObject(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof List) {
|
||||
if (((List) value).size() == 1) {
|
||||
return super.toDbObject(((List<Object>) value).iterator().next(), context);
|
||||
}
|
||||
}
|
||||
|
||||
return super.toDbObject(value, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -23,10 +23,19 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FacetOperation.FacetOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -36,10 +45,14 @@ import com.mongodb.DBObject;
|
||||
/**
|
||||
* An {@code Aggregation} is a representation of a list of aggregation steps to be performed by the MongoDB Aggregation
|
||||
* Framework.
|
||||
*
|
||||
*
|
||||
* @author Tobias Trelle
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Alessio Fachechi
|
||||
* @author Christoph Strobl
|
||||
* @author Nikolay Bogdanov
|
||||
* @since 1.3
|
||||
*/
|
||||
public class Aggregation {
|
||||
@@ -56,7 +69,7 @@ public class Aggregation {
|
||||
*/
|
||||
public static final String CURRENT = SystemVariable.CURRENT.toString();
|
||||
|
||||
public static final AggregationOperationContext DEFAULT_CONTEXT = new NoOpAggregationOperationContext();
|
||||
public static final AggregationOperationContext DEFAULT_CONTEXT = AggregationOperationRenderer.DEFAULT_CONTEXT;
|
||||
public static final AggregationOptions DEFAULT_OPTIONS = newAggregationOptions().build();
|
||||
|
||||
protected final List<AggregationOperation> operations;
|
||||
@@ -64,7 +77,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
*/
|
||||
public static Aggregation newAggregation(List<? extends AggregationOperation> operations) {
|
||||
@@ -73,7 +86,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
*/
|
||||
public static Aggregation newAggregation(AggregationOperation... operations) {
|
||||
@@ -83,7 +96,7 @@ public class Aggregation {
|
||||
/**
|
||||
* Returns a copy of this {@link Aggregation} with the given {@link AggregationOptions} set. Note that options are
|
||||
* supported in MongoDB version 2.6+.
|
||||
*
|
||||
*
|
||||
* @param options must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.6
|
||||
@@ -96,7 +109,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link TypedAggregation} for the given type and {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
*/
|
||||
@@ -106,7 +119,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link TypedAggregation} for the given type and {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
*/
|
||||
@@ -116,7 +129,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param aggregationOperations must not be {@literal null} or empty.
|
||||
*/
|
||||
protected Aggregation(AggregationOperation... aggregationOperations) {
|
||||
@@ -136,7 +149,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param aggregationOperations must not be {@literal null} or empty.
|
||||
*/
|
||||
protected Aggregation(List<AggregationOperation> aggregationOperations) {
|
||||
@@ -145,23 +158,34 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
|
||||
*
|
||||
*
|
||||
* @param aggregationOperations must not be {@literal null} or empty.
|
||||
* @param options must not be {@literal null} or empty.
|
||||
*/
|
||||
protected Aggregation(List<AggregationOperation> aggregationOperations, AggregationOptions options) {
|
||||
|
||||
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
|
||||
Assert.isTrue(aggregationOperations.size() > 0, "At least one AggregationOperation has to be provided");
|
||||
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
|
||||
Assert.notNull(options, "AggregationOptions must not be null!");
|
||||
|
||||
// check $out is the last operation if it exists
|
||||
for (AggregationOperation aggregationOperation : aggregationOperations) {
|
||||
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation, aggregationOperations)) {
|
||||
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
|
||||
}
|
||||
}
|
||||
|
||||
this.operations = aggregationOperations;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
private boolean isLast(AggregationOperation aggregationOperation, List<AggregationOperation> aggregationOperations) {
|
||||
return aggregationOperations.indexOf(aggregationOperation) == aggregationOperations.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pointer to the previous {@link AggregationOperation}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String previousOperation() {
|
||||
@@ -170,7 +194,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation} including the given fields.
|
||||
*
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -179,8 +203,8 @@ public class Aggregation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation} includeing the given {@link Fields}.
|
||||
*
|
||||
* Creates a new {@link ProjectionOperation} including the given {@link Fields}.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -190,17 +214,95 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public static UnwindOperation unwind(String field) {
|
||||
return new UnwindOperation(field(field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ReplaceRootOperation} for the field with the given name.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static ReplaceRootOperation replaceRoot(String fieldName) {
|
||||
return ReplaceRootOperation.builder().withValueOf(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ReplaceRootOperation} for the field with the given
|
||||
* {@link AggregationExpression}.
|
||||
*
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static ReplaceRootOperation replaceRoot(AggregationExpression aggregationExpression) {
|
||||
return ReplaceRootOperation.builder().withValueOf(aggregationExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ReplaceRootDocumentOperationBuilder} to configure a
|
||||
* {@link ReplaceRootOperation}.
|
||||
*
|
||||
* @return the {@literal ReplaceRootDocumentOperationBuilder}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static ReplaceRootOperationBuilder replaceRoot() {
|
||||
return ReplaceRootOperation.builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link UnwindOperation} for the field with the given name and
|
||||
* {@code preserveNullAndEmptyArrays}. Note that extended unwind is supported in MongoDB version 3.2+.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
|
||||
* array is empty.
|
||||
* @return new {@link UnwindOperation}
|
||||
* @since 1.10
|
||||
*/
|
||||
public static UnwindOperation unwind(String field, boolean preserveNullAndEmptyArrays) {
|
||||
return new UnwindOperation(field(field), preserveNullAndEmptyArrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link UnwindOperation} for the field with the given name including the name of a
|
||||
* new field to hold the array index of the element as {@code arrayIndex}. Note that extended unwind is supported in
|
||||
* MongoDB version 3.2+.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param arrayIndex must not be {@literal null} or empty.
|
||||
* @return new {@link UnwindOperation}
|
||||
* @since 1.10
|
||||
*/
|
||||
public static UnwindOperation unwind(String field, String arrayIndex) {
|
||||
return new UnwindOperation(field(field), field(arrayIndex), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link UnwindOperation} for the field with the given nameincluding the name of a new
|
||||
* field to hold the array index of the element as {@code arrayIndex} using {@code preserveNullAndEmptyArrays}. Note
|
||||
* that extended unwind is supported in MongoDB version 3.2+.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
* @param arrayIndex must not be {@literal null} or empty.
|
||||
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
|
||||
* array is empty.
|
||||
* @return new {@link UnwindOperation}
|
||||
* @since 1.10
|
||||
*/
|
||||
public static UnwindOperation unwind(String field, String arrayIndex, boolean preserveNullAndEmptyArrays) {
|
||||
return new UnwindOperation(field(field), field(arrayIndex), preserveNullAndEmptyArrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GroupOperation} for the given fields.
|
||||
*
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -210,7 +312,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link GroupOperation} for the given {@link Fields}.
|
||||
*
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -218,9 +320,21 @@ public class Aggregation {
|
||||
return new GroupOperation(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GraphLookupOperation.GraphLookupOperationFromBuilder} to construct a
|
||||
* {@link GraphLookupOperation} given {@literal fromCollection}.
|
||||
*
|
||||
* @param fromCollection must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static StartWithBuilder graphLookup(String fromCollection) {
|
||||
return GraphLookupOperation.builder().from(fromCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SortOperation} for the given {@link Sort}.
|
||||
*
|
||||
*
|
||||
* @param sort must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -230,7 +344,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SortOperation} for the given sort {@link Direction} and {@code fields}.
|
||||
*
|
||||
*
|
||||
* @param direction must not be {@literal null}.
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
@@ -241,17 +355,28 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link SkipOperation} skipping the given number of elements.
|
||||
*
|
||||
*
|
||||
* @param elementsToSkip must not be less than zero.
|
||||
* @return
|
||||
* @deprecated prepare to get this one removed in favor of {@link #skip(long)}.
|
||||
*/
|
||||
public static SkipOperation skip(int elementsToSkip) {
|
||||
return new SkipOperation(elementsToSkip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SkipOperation} skipping the given number of elements.
|
||||
*
|
||||
* @param elementsToSkip must not be less than zero.
|
||||
* @return
|
||||
*/
|
||||
public static SkipOperation skip(long elementsToSkip) {
|
||||
return new SkipOperation(elementsToSkip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
|
||||
*
|
||||
*
|
||||
* @param maxElements must not be less than zero.
|
||||
* @return
|
||||
*/
|
||||
@@ -261,7 +386,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link MatchOperation} using the given {@link Criteria}.
|
||||
*
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -269,12 +394,142 @@ public class Aggregation {
|
||||
return new MatchOperation(criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MatchOperation} using the given {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static MatchOperation match(CriteriaDefinition criteria) {
|
||||
return new MatchOperation(criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation in
|
||||
* the pipeline.
|
||||
*
|
||||
* @param outCollectionName collection name to export aggregation results. The {@link OutOperation} creates a new
|
||||
* collection in the current database if one does not already exist. The collection is not visible until the
|
||||
* aggregation completes. If the aggregation fails, MongoDB does not create the collection. Must not be
|
||||
* {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static OutOperation out(String outCollectionName) {
|
||||
return new OutOperation(outCollectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperation} given {@literal groupByField}.
|
||||
*
|
||||
* @param groupByField must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static BucketOperation bucket(String groupByField) {
|
||||
return new BucketOperation(field(groupByField));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperation} given {@link AggregationExpression group-by expression}.
|
||||
*
|
||||
* @param groupByExpression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static BucketOperation bucket(AggregationExpression groupByExpression) {
|
||||
return new BucketOperation(groupByExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketAutoOperation} given {@literal groupByField}.
|
||||
*
|
||||
* @param groupByField must not be {@literal null} or empty.
|
||||
* @param buckets number of buckets, must be a positive integer.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static BucketAutoOperation bucketAuto(String groupByField, int buckets) {
|
||||
return new BucketAutoOperation(field(groupByField), buckets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketAutoOperation} given {@link AggregationExpression group-by expression}.
|
||||
*
|
||||
* @param groupByExpression must not be {@literal null}.
|
||||
* @param buckets number of buckets, must be a positive integer.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static BucketAutoOperation bucketAuto(AggregationExpression groupByExpression, int buckets) {
|
||||
return new BucketAutoOperation(groupByExpression, buckets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FacetOperation}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static FacetOperation facet() {
|
||||
return FacetOperation.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FacetOperationBuilder} given {@link Aggregation}.
|
||||
*
|
||||
* @param aggregationOperations the sub-pipeline, must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static FacetOperationBuilder facet(AggregationOperation... aggregationOperations) {
|
||||
return facet().and(aggregationOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LookupOperation}.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param localField must not be {@literal null}.
|
||||
* @param foreignField must not be {@literal null}.
|
||||
* @param as must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static LookupOperation lookup(String from, String localField, String foreignField, String as) {
|
||||
return lookup(field(from), field(localField), field(foreignField), field(as));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LookupOperation} for the given {@link Fields}.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param localField must not be {@literal null}.
|
||||
* @param foreignField must not be {@literal null}.
|
||||
* @param as must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static LookupOperation lookup(Field from, Field localField, Field foreignField, Field as) {
|
||||
return new LookupOperation(from, localField, foreignField, as);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CountOperationBuilder}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static CountOperationBuilder count() {
|
||||
return new CountOperationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Fields} instance for the given field names.
|
||||
*
|
||||
* @see Fields#fields(String...)
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
* @see Fields#fields(String...)
|
||||
*/
|
||||
public static Fields fields(String... fields) {
|
||||
return Fields.fields(fields);
|
||||
@@ -282,7 +537,7 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Fields} instance from the given field name and target reference.
|
||||
*
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param target must not be {@literal null} or empty.
|
||||
* @return
|
||||
@@ -291,9 +546,22 @@ public class Aggregation {
|
||||
return Fields.from(field(name, target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the{@code distanceField}. The
|
||||
* {@code distanceField} defines output field that contains the calculated distance.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param distanceField must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
|
||||
return new GeoNearOperation(query, distanceField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link AggregationOptions.Builder}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @since 1.6
|
||||
*/
|
||||
@@ -303,24 +571,13 @@ public class Aggregation {
|
||||
|
||||
/**
|
||||
* Converts this {@link Aggregation} specification to a {@link DBObject}.
|
||||
*
|
||||
*
|
||||
* @param inputCollectionName the name of the input collection
|
||||
* @return the {@code DBObject} representing this aggregation
|
||||
*/
|
||||
public DBObject toDbObject(String inputCollectionName, AggregationOperationContext rootContext) {
|
||||
|
||||
AggregationOperationContext context = rootContext;
|
||||
List<DBObject> operationDocuments = new ArrayList<DBObject>(operations.size());
|
||||
|
||||
for (AggregationOperation operation : operations) {
|
||||
|
||||
operationDocuments.add(operation.toDBObject(context));
|
||||
|
||||
if (operation instanceof FieldsExposingAggregationOperation) {
|
||||
FieldsExposingAggregationOperation exposedFieldsOperation = (FieldsExposingAggregationOperation) operation;
|
||||
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), rootContext);
|
||||
}
|
||||
}
|
||||
List<DBObject> operationDocuments = AggregationOperationRenderer.toDBObject(operations, rootContext);
|
||||
|
||||
DBObject command = new BasicDBObject("aggregate", inputCollectionName);
|
||||
command.put("pipeline", operationDocuments);
|
||||
@@ -336,48 +593,12 @@ public class Aggregation {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return SerializationUtils
|
||||
.serializeToJsonSafely(toDbObject("__collection__", new NoOpAggregationOperationContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple {@link AggregationOperationContext} that just returns {@link FieldReference}s as is.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class NoOpAggregationOperationContext implements AggregationOperationContext {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public DBObject getMappedObject(DBObject dbObject) {
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(Field field) {
|
||||
return new FieldReference(new ExposedField(field, true));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(String name) {
|
||||
return new FieldReference(new ExposedField(new AggregationField(name), true));
|
||||
}
|
||||
return SerializationUtils.serializeToJsonSafely(toDbObject("__collection__", DEFAULT_CONTEXT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the system variables available in MongoDB aggregation framework pipeline expressions.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation-variables
|
||||
*/
|
||||
@@ -390,7 +611,7 @@ public class Aggregation {
|
||||
/**
|
||||
* Return {@literal true} if the given {@code fieldRef} denotes a well-known system variable, {@literal false}
|
||||
* otherwise.
|
||||
*
|
||||
*
|
||||
* @param fieldRef may be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@@ -412,7 +633,7 @@ public class Aggregation {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Enum#toString()
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2015-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.aggregation;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* An {@link AggregationExpression} can be used with field expressions in aggregation pipeline stages like
|
||||
* {@code project} and {@code group}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface AggregationExpression {
|
||||
|
||||
/**
|
||||
* Turns the {@link AggregationExpression} into a {@link DBObject} within the given
|
||||
* {@link AggregationOperationContext}.
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
DBObject toDbObject(AggregationOperationContext context);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2015-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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* An enum of supported {@link AggregationExpression}s in aggregation pipeline stages.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
* @deprecated since 1.10. Please use {@link ArithmeticOperators} and {@link ComparisonOperators} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum AggregationFunctionExpressions {
|
||||
|
||||
SIZE, CMP, EQ, GT, GTE, LT, LTE, NE, SUBTRACT, ADD, MULTIPLY;
|
||||
|
||||
/**
|
||||
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.
|
||||
*
|
||||
* @param parameters must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public AggregationExpression of(Object... parameters) {
|
||||
|
||||
Assert.notNull(parameters, "Parameters must not be null!");
|
||||
return new FunctionExpression(name().toLowerCase(), parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AggregationExpression} representing a function call.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @since 1.7
|
||||
*/
|
||||
static class FunctionExpression implements AggregationExpression {
|
||||
|
||||
private final String name;
|
||||
private final List<Object> values;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FunctionExpression} for the given name and values.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
public FunctionExpression(String name, Object[] values) {
|
||||
|
||||
Assert.hasText(name, "Name must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
this.name = name;
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.Expression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>(values.size());
|
||||
|
||||
for (Object value : values) {
|
||||
args.add(unpack(value, context));
|
||||
}
|
||||
|
||||
return new BasicDBObject("$" + name, args);
|
||||
}
|
||||
|
||||
private static Object unpack(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Rendering support for {@link AggregationOperation} into a {@link List} of {@link com.mongodb.DBObject}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
class AggregationOperationRenderer {
|
||||
|
||||
static final AggregationOperationContext DEFAULT_CONTEXT = new NoOpAggregationOperationContext();
|
||||
|
||||
/**
|
||||
* Render a {@link List} of {@link AggregationOperation} given {@link AggregationOperationContext} into their
|
||||
* {@link DBObject} representation.
|
||||
*
|
||||
* @param operations must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @return the {@link List} of {@link DBObject}.
|
||||
*/
|
||||
static List<DBObject> toDBObject(List<AggregationOperation> operations, AggregationOperationContext rootContext) {
|
||||
|
||||
List<DBObject> operationDocuments = new ArrayList<DBObject>(operations.size());
|
||||
|
||||
AggregationOperationContext contextToUse = rootContext;
|
||||
|
||||
for (AggregationOperation operation : operations) {
|
||||
|
||||
operationDocuments.add(operation.toDBObject(contextToUse));
|
||||
|
||||
if (operation instanceof FieldsExposingAggregationOperation) {
|
||||
|
||||
FieldsExposingAggregationOperation exposedFieldsOperation = (FieldsExposingAggregationOperation) operation;
|
||||
ExposedFields fields = exposedFieldsOperation.getFields();
|
||||
|
||||
if (operation instanceof InheritsFieldsAggregationOperation) {
|
||||
contextToUse = new InheritingExposedFieldsAggregationOperationContext(fields, contextToUse);
|
||||
} else {
|
||||
contextToUse = fields.exposesNoFields() ? DEFAULT_CONTEXT
|
||||
: new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), contextToUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operationDocuments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple {@link AggregationOperationContext} that just returns {@link FieldReference}s as is.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class NoOpAggregationOperationContext implements AggregationOperationContext {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public DBObject getMappedObject(DBObject dbObject) {
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(Field field) {
|
||||
return new DirectFieldReference(new ExposedField(field, true));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(String name) {
|
||||
return new DirectFieldReference(new ExposedField(new AggregationField(name), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* An {@link AggregationExpression} that renders a MongoDB Aggregation Framework expression from the AST of a
|
||||
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html">SpEL
|
||||
* expression</a>. <br />
|
||||
* <br />
|
||||
* <strong>Samples:</strong> <br />
|
||||
* <code>
|
||||
* <pre>
|
||||
* // { $and: [ { $gt: [ "$qty", 100 ] }, { $lt: [ "$qty", 250 ] } ] }
|
||||
* expressionOf("qty > 100 && qty < 250);
|
||||
*
|
||||
* // { $cond : { if : { $gte : [ "$a", 42 ]}, then : "answer", else : "no-answer" } }
|
||||
* expressionOf("cond(a >= 42, 'answer', 'no-answer')");
|
||||
* </pre>
|
||||
* </code>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @see SpelExpressionTransformer
|
||||
* @since 1.10
|
||||
*/
|
||||
public class AggregationSpELExpression implements AggregationExpression {
|
||||
|
||||
private static final SpelExpressionTransformer TRANSFORMER = new SpelExpressionTransformer();
|
||||
private final String rawExpression;
|
||||
private final Object[] parameters;
|
||||
|
||||
private AggregationSpELExpression(String rawExpression, Object[] parameters) {
|
||||
|
||||
this.rawExpression = rawExpression;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationSpELExpression} for the given {@literal expressionString} and {@literal parameters}.
|
||||
*
|
||||
* @param expressionString must not be {@literal null}.
|
||||
* @param parameters can be empty.
|
||||
* @return
|
||||
*/
|
||||
public static AggregationSpELExpression expressionOf(String expressionString, Object... parameters) {
|
||||
|
||||
Assert.notNull(expressionString, "ExpressionString must not be null!");
|
||||
return new AggregationSpELExpression(expressionString, parameters);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return (DBObject) TRANSFORMER.transform(rawExpression, context, parameters);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal boolean expressions} that evaluate their argument expressions as booleans and return a boolean
|
||||
* as the result.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class BooleanOperators {
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static BooleanOperatorFactory valueOf(String fieldReference) {
|
||||
return new BooleanOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting of the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static BooleanOperatorFactory valueOf(AggregationExpression fieldReference) {
|
||||
return new BooleanOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates the boolean value of the referenced field and returns the
|
||||
* opposite boolean value.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Not not(String fieldReference) {
|
||||
return Not.not(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates the boolean value of {@link AggregationExpression} result
|
||||
* and returns the opposite boolean value.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Not not(AggregationExpression expression) {
|
||||
return Not.not(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class BooleanOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public BooleanOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link BooleanOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public BooleanOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates one or more expressions and returns {@literal true} if
|
||||
* all of the expressions are {@literal true}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public And and(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return createAnd().andExpression(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates one or more expressions and returns {@literal true} if
|
||||
* all of the expressions are {@literal true}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public And and(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return createAnd().andField(fieldReference);
|
||||
}
|
||||
|
||||
private And createAnd() {
|
||||
return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates one or more expressions and returns {@literal true} if
|
||||
* any of the expressions are {@literal true}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Or or(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return createOr().orExpression(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates one or more expressions and returns {@literal true} if
|
||||
* any of the expressions are {@literal true}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Or or(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return createOr().orField(fieldReference);
|
||||
}
|
||||
|
||||
private Or createOr() {
|
||||
return usesFieldRef() ? Or.or(Fields.field(fieldReference)) : Or.or(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a boolean and returns the opposite boolean value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Not not() {
|
||||
return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return this.fieldReference != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $and}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class And extends AbstractAggregationExpression {
|
||||
|
||||
private And(List<?> values) {
|
||||
super(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$and";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link And} that evaluates one or more expressions and returns {@literal true} if all of the
|
||||
* expressions are {@literal true}.
|
||||
*
|
||||
* @param expressions
|
||||
* @return
|
||||
*/
|
||||
public static And and(Object... expressions) {
|
||||
return new And(Arrays.asList(expressions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link And} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public And andExpression(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new And(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link And} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public And andField(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new And(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link And} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public And andValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new And(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $or}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Or extends AbstractAggregationExpression {
|
||||
|
||||
private Or(List<?> values) {
|
||||
super(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$or";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Or} that evaluates one or more expressions and returns {@literal true} if any of the
|
||||
* expressions are {@literal true}.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Or or(Object... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "Expressions must not be null!");
|
||||
return new Or(Arrays.asList(expressions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Or} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Or orExpression(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Or(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Or} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Or orField(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Or(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Or} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Or orValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Or(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $not}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Not extends AbstractAggregationExpression {
|
||||
|
||||
private Not(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$not";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Not} that evaluates the boolean value of the referenced field and returns the opposite boolean
|
||||
* value.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Not not(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Not(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Not} that evaluates the resulting boolean value of the given {@link AggregationExpression} and
|
||||
* returns the opposite boolean value.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Not not(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Not(Collections.singletonList(expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.BucketAutoOperationOutputBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $bucketAuto}-operation. <br />
|
||||
* Bucket stage is typically used with {@link Aggregation} and {@code $facet}. Categorizes incoming documents into a
|
||||
* specific number of groups, called buckets, based on a specified expression. Bucket boundaries are automatically
|
||||
* determined in an attempt to evenly distribute the documents into the specified number of buckets. <br />
|
||||
* We recommend to use the static factory method {@link Aggregation#bucketAuto(String, int)} instead of creating
|
||||
* instances of this class directly.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://docs.mongodb.org/manual/reference/aggregation/bucketAuto/">http://docs.mongodb.org/manual/reference/aggregation/bucketAuto/</a>
|
||||
* @see BucketOperationSupport
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class BucketAutoOperation extends BucketOperationSupport<BucketAutoOperation, BucketAutoOperationOutputBuilder>
|
||||
implements FieldsExposingAggregationOperation {
|
||||
|
||||
private final int buckets;
|
||||
private final String granularity;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketAutoOperation} given a {@link Field group-by field}.
|
||||
*
|
||||
* @param groupByField must not be {@literal null}.
|
||||
* @param buckets number of buckets, must be a positive integer.
|
||||
*/
|
||||
public BucketAutoOperation(Field groupByField, int buckets) {
|
||||
|
||||
super(groupByField);
|
||||
|
||||
Assert.isTrue(buckets > 0, "Number of buckets must be greater 0!");
|
||||
|
||||
this.buckets = buckets;
|
||||
this.granularity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketAutoOperation} given a {@link AggregationExpression group-by expression}.
|
||||
*
|
||||
* @param groupByExpression must not be {@literal null}.
|
||||
* @param buckets number of buckets, must be a positive integer.
|
||||
*/
|
||||
public BucketAutoOperation(AggregationExpression groupByExpression, int buckets) {
|
||||
|
||||
super(groupByExpression);
|
||||
|
||||
Assert.isTrue(buckets > 0, "Number of buckets must be greater 0!");
|
||||
|
||||
this.buckets = buckets;
|
||||
this.granularity = null;
|
||||
}
|
||||
|
||||
private BucketAutoOperation(BucketAutoOperation bucketOperation, Outputs outputs) {
|
||||
|
||||
super(bucketOperation, outputs);
|
||||
|
||||
this.buckets = bucketOperation.buckets;
|
||||
this.granularity = bucketOperation.granularity;
|
||||
}
|
||||
|
||||
private BucketAutoOperation(BucketAutoOperation bucketOperation, int buckets, String granularity) {
|
||||
|
||||
super(bucketOperation);
|
||||
|
||||
this.buckets = buckets;
|
||||
this.granularity = granularity;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject options = new BasicDBObject();
|
||||
|
||||
options.put("buckets", buckets);
|
||||
options.putAll(super.toDBObject(context));
|
||||
|
||||
if (granularity != null) {
|
||||
options.put("granularity", granularity);
|
||||
}
|
||||
|
||||
return new BasicDBObject("$bucketAuto", options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a number of bucket {@literal buckets} and return a new {@link BucketAutoOperation}.
|
||||
*
|
||||
* @param buckets must be a positive number.
|
||||
* @return
|
||||
*/
|
||||
public BucketAutoOperation withBuckets(int buckets) {
|
||||
|
||||
Assert.isTrue(buckets > 0, "Number of buckets must be greater 0!");
|
||||
return new BucketAutoOperation(this, buckets, granularity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures {@link Granularity granularity} that specifies the preferred number series to use to ensure that the
|
||||
* calculated boundary edges end on preferred round numbers or their powers of 10 and return a new
|
||||
* {@link BucketAutoOperation}. <br />
|
||||
* Use either predefined {@link Granularities} or provide a own one.
|
||||
*
|
||||
* @param granularity must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public BucketAutoOperation withGranularity(Granularity granularity) {
|
||||
|
||||
Assert.notNull(granularity, "Granularity must not be null!");
|
||||
|
||||
return new BucketAutoOperation(this, buckets, granularity.getMongoRepresentation());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#newBucketOperation(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Outputs)
|
||||
*/
|
||||
@Override
|
||||
protected BucketAutoOperation newBucketOperation(Outputs outputs) {
|
||||
return new BucketAutoOperation(this, outputs);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutputExpression(java.lang.String, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public ExpressionBucketAutoOperationBuilder andOutputExpression(String expression, Object... params) {
|
||||
return new ExpressionBucketAutoOperationBuilder(expression, this, params);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public BucketAutoOperationOutputBuilder andOutput(AggregationExpression expression) {
|
||||
return new BucketAutoOperationOutputBuilder(expression, this);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public BucketAutoOperationOutputBuilder andOutput(String fieldName) {
|
||||
return new BucketAutoOperationOutputBuilder(Fields.field(fieldName), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OutputBuilder} implementation for {@link BucketAutoOperation}.
|
||||
*/
|
||||
public static class BucketAutoOperationOutputBuilder
|
||||
extends OutputBuilder<BucketAutoOperationOutputBuilder, BucketAutoOperation> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketAutoOperationOutputBuilder} fot the given value and {@link BucketAutoOperation}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
*/
|
||||
protected BucketAutoOperationOutputBuilder(Object value, BucketAutoOperation operation) {
|
||||
super(value, operation);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
|
||||
*/
|
||||
@Override
|
||||
protected BucketAutoOperationOutputBuilder apply(OperationOutput operationOutput) {
|
||||
return new BucketAutoOperationOutputBuilder(operationOutput, this.operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExpressionBucketOperationBuilderSupport} implementation for {@link BucketAutoOperation} using SpEL
|
||||
* expression based {@link Output}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class ExpressionBucketAutoOperationBuilder
|
||||
extends ExpressionBucketOperationBuilderSupport<BucketAutoOperationOutputBuilder, BucketAutoOperation> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionBucketAutoOperationBuilder} for the given value, {@link BucketAutoOperation} and
|
||||
* parameters.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
* @param parameters
|
||||
*/
|
||||
protected ExpressionBucketAutoOperationBuilder(String expression, BucketAutoOperation operation,
|
||||
Object[] parameters) {
|
||||
super(expression, operation, parameters);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
|
||||
*/
|
||||
@Override
|
||||
protected BucketAutoOperationOutputBuilder apply(OperationOutput operationOutput) {
|
||||
return new BucketAutoOperationOutputBuilder(operationOutput, this.operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface Granularity {
|
||||
|
||||
/**
|
||||
* @return a String that represents a MongoDB granularity to be used with {@link BucketAutoOperation}. Never
|
||||
* {@literal null}.
|
||||
*/
|
||||
String getMongoRepresentation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported MongoDB granularities.
|
||||
*
|
||||
* @see <a
|
||||
* href="https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/#granularity>https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/#granularity</a>
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public enum Granularities implements Granularity {
|
||||
|
||||
R5, R10, R20, R40, R80, //
|
||||
|
||||
SERIES_1_2_5("1-2-5"), //
|
||||
|
||||
E6, E12, E24, E48, E96, E192, //
|
||||
|
||||
POWERSOF2;
|
||||
|
||||
private final String granularity;
|
||||
|
||||
Granularities() {
|
||||
this.granularity = name();
|
||||
}
|
||||
|
||||
Granularities(String granularity) {
|
||||
this.granularity = granularity;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GranularitytoMongoGranularity()
|
||||
*/
|
||||
@Override
|
||||
public String getMongoRepresentation() {
|
||||
return granularity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.BucketOperation.BucketOperationOutputBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $bucket}-operation. <br />
|
||||
*
|
||||
* Bucket stage is typically used with {@link Aggregation} and {@code $facet}. Categorizes incoming documents into
|
||||
* groups, called buckets, based on a specified expression and bucket boundaries. <br />
|
||||
*
|
||||
* We recommend to use the static factory method {@link Aggregation#bucket(String)} instead of creating instances of
|
||||
* this class directly.
|
||||
*
|
||||
* @see <a href="http://docs.mongodb.org/manual/reference/aggregation/bucket/">http://docs.mongodb.org/manual/reference/aggregation/bucket/</a>
|
||||
* @see BucketOperationSupport
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public class BucketOperation extends BucketOperationSupport<BucketOperation, BucketOperationOutputBuilder>
|
||||
implements FieldsExposingAggregationOperation {
|
||||
|
||||
private final List<Object> boundaries;
|
||||
private final Object defaultBucket;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperation} given a {@link Field group-by field}.
|
||||
*
|
||||
* @param groupByField must not be {@literal null}.
|
||||
*/
|
||||
public BucketOperation(Field groupByField) {
|
||||
|
||||
super(groupByField);
|
||||
|
||||
this.boundaries = Collections.emptyList();
|
||||
this.defaultBucket = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperation} given a {@link AggregationExpression group-by expression}.
|
||||
*
|
||||
* @param groupByExpression must not be {@literal null}.
|
||||
*/
|
||||
public BucketOperation(AggregationExpression groupByExpression) {
|
||||
|
||||
super(groupByExpression);
|
||||
|
||||
this.boundaries = Collections.emptyList();
|
||||
this.defaultBucket = null;
|
||||
}
|
||||
|
||||
private BucketOperation(BucketOperation bucketOperation, Outputs outputs) {
|
||||
|
||||
super(bucketOperation, outputs);
|
||||
|
||||
this.boundaries = bucketOperation.boundaries;
|
||||
this.defaultBucket = bucketOperation.defaultBucket;
|
||||
}
|
||||
|
||||
private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, Object defaultBucket) {
|
||||
|
||||
super(bucketOperation);
|
||||
|
||||
this.boundaries = new ArrayList<Object>(boundaries);
|
||||
this.defaultBucket = defaultBucket;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject options = new BasicDBObject();
|
||||
|
||||
options.put("boundaries", context.getMappedObject(new BasicDBObject("$set", boundaries)).get("$set"));
|
||||
|
||||
if (defaultBucket != null) {
|
||||
options.put("default", context.getMappedObject(new BasicDBObject("$set", defaultBucket)).get("$set"));
|
||||
}
|
||||
|
||||
options.putAll(super.toDBObject(context));
|
||||
|
||||
return new BasicDBObject("$bucket", options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a default bucket {@literal literal} and return a new {@link BucketOperation}.
|
||||
*
|
||||
* @param literal must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public BucketOperation withDefaultBucket(Object literal) {
|
||||
|
||||
Assert.notNull(literal, "Default bucket literal must not be null!");
|
||||
return new BucketOperation(this, boundaries, literal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures {@literal boundaries} and return a new {@link BucketOperation}. Existing {@literal boundaries} are
|
||||
* preserved and the new {@literal boundaries} are appended.
|
||||
*
|
||||
* @param boundaries must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public BucketOperation withBoundaries(Object... boundaries) {
|
||||
|
||||
Assert.notNull(boundaries, "Boundaries must not be null!");
|
||||
Assert.noNullElements(boundaries, "Boundaries must not contain null values!");
|
||||
|
||||
List<Object> newBoundaries = new ArrayList<Object>(this.boundaries.size() + boundaries.length);
|
||||
newBoundaries.addAll(this.boundaries);
|
||||
newBoundaries.addAll(Arrays.asList(boundaries));
|
||||
|
||||
return new BucketOperation(this, newBoundaries, defaultBucket);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#newBucketOperation(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Outputs)
|
||||
*/
|
||||
@Override
|
||||
protected BucketOperation newBucketOperation(Outputs outputs) {
|
||||
return new BucketOperation(this, outputs);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutputExpression(java.lang.String, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public ExpressionBucketOperationBuilder andOutputExpression(String expression, Object... params) {
|
||||
return new ExpressionBucketOperationBuilder(expression, this, params);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public BucketOperationOutputBuilder andOutput(AggregationExpression expression) {
|
||||
return new BucketOperationOutputBuilder(expression, this);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public BucketOperationOutputBuilder andOutput(String fieldName) {
|
||||
return new BucketOperationOutputBuilder(Fields.field(fieldName), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OutputBuilder} implementation for {@link BucketOperation}.
|
||||
*/
|
||||
public static class BucketOperationOutputBuilder
|
||||
extends BucketOperationSupport.OutputBuilder<BucketOperationOutputBuilder, BucketOperation> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationOutputBuilder} fot the given value and {@link BucketOperation}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
*/
|
||||
protected BucketOperationOutputBuilder(Object value, BucketOperation operation) {
|
||||
super(value, operation);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
|
||||
*/
|
||||
@Override
|
||||
protected BucketOperationOutputBuilder apply(OperationOutput operationOutput) {
|
||||
return new BucketOperationOutputBuilder(operationOutput, this.operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExpressionBucketOperationBuilderSupport} implementation for {@link BucketOperation} using SpEL expression
|
||||
* based {@link Output}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class ExpressionBucketOperationBuilder
|
||||
extends ExpressionBucketOperationBuilderSupport<BucketOperationOutputBuilder, BucketOperation> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionBucketOperationBuilderSupport} for the given value, {@link BucketOperation}
|
||||
* and parameters.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
* @param parameters
|
||||
*/
|
||||
protected ExpressionBucketOperationBuilder(String expression, BucketOperation operation, Object[] parameters) {
|
||||
super(expression, operation, parameters);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
|
||||
*/
|
||||
@Override
|
||||
protected BucketOperationOutputBuilder apply(OperationOutput operationOutput) {
|
||||
return new BucketOperationOutputBuilder(operationOutput, this.operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
|
||||
import org.springframework.expression.spel.ast.Projection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Base class for bucket operations that support output expressions the aggregation framework. <br />
|
||||
* Bucket stages collect documents into buckets and can contribute output fields. <br />
|
||||
* Implementing classes are required to provide an {@link OutputBuilder}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract class BucketOperationSupport<T extends BucketOperationSupport<T, B>, B extends OutputBuilder<B, T>>
|
||||
implements FieldsExposingAggregationOperation {
|
||||
|
||||
private final Field groupByField;
|
||||
private final AggregationExpression groupByExpression;
|
||||
private final Outputs outputs;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationSupport} given a {@link Field group-by field}.
|
||||
*
|
||||
* @param groupByField must not be {@literal null}.
|
||||
*/
|
||||
protected BucketOperationSupport(Field groupByField) {
|
||||
|
||||
Assert.notNull(groupByField, "Group by field must not be null!");
|
||||
|
||||
this.groupByField = groupByField;
|
||||
this.groupByExpression = null;
|
||||
this.outputs = Outputs.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationSupport} given a {@link AggregationExpression group-by expression}.
|
||||
*
|
||||
* @param groupByExpression must not be {@literal null}.
|
||||
*/
|
||||
protected BucketOperationSupport(AggregationExpression groupByExpression) {
|
||||
|
||||
Assert.notNull(groupByExpression, "Group by AggregationExpression must not be null!");
|
||||
|
||||
this.groupByExpression = groupByExpression;
|
||||
this.groupByField = null;
|
||||
this.outputs = Outputs.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of {@link BucketOperationSupport}.
|
||||
*
|
||||
* @param operationSupport must not be {@literal null}.
|
||||
*/
|
||||
protected BucketOperationSupport(BucketOperationSupport<?, ?> operationSupport) {
|
||||
this(operationSupport, operationSupport.outputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of {@link BucketOperationSupport} and applies the new {@link Outputs}.
|
||||
*
|
||||
* @param operationSupport must not be {@literal null}.
|
||||
* @param outputs must not be {@literal null}.
|
||||
*/
|
||||
protected BucketOperationSupport(BucketOperationSupport<?, ?> operationSupport, Outputs outputs) {
|
||||
|
||||
Assert.notNull(operationSupport, "BucketOperationSupport must not be null!");
|
||||
Assert.notNull(outputs, "Outputs must not be null!");
|
||||
|
||||
this.groupByField = operationSupport.groupByField;
|
||||
this.groupByExpression = operationSupport.groupByExpression;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionBucketOperationBuilderSupport} given a SpEL {@literal expression} and optional
|
||||
* {@literal params} to add an output field to the resulting bucket documents.
|
||||
*
|
||||
* @param expression the SpEL expression, must not be {@literal null} or empty.
|
||||
* @param params must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public abstract ExpressionBucketOperationBuilderSupport<B, T> andOutputExpression(String expression,
|
||||
Object... params);
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationSupport} given an {@link AggregationExpression} to add an output field to the
|
||||
* resulting bucket documents.
|
||||
*
|
||||
* @param expression the SpEL expression, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public abstract B andOutput(AggregationExpression expression);
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationSupport} given {@literal fieldName} to add an output field to the resulting
|
||||
* bucket documents. {@link BucketOperationSupport} exposes accumulation operations that can be applied to
|
||||
* {@literal fieldName}.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public abstract B andOutput(String fieldName);
|
||||
|
||||
/**
|
||||
* Creates a new {@link BucketOperationSupport} given to add a count field to the resulting bucket documents.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B andOutputCount() {
|
||||
return andOutput(new AggregationExpression() {
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$sum", 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
|
||||
dbObject.put("groupBy", groupByExpression == null ? context.getReference(groupByField).toString()
|
||||
: groupByExpression.toDbObject(context));
|
||||
|
||||
if (!outputs.isEmpty()) {
|
||||
dbObject.put("output", outputs.toDbObject(context));
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return outputs.asExposedFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation hook to create a new bucket operation.
|
||||
*
|
||||
* @param outputs the outputs
|
||||
* @return the new bucket operation.
|
||||
*/
|
||||
protected abstract T newBucketOperation(Outputs outputs);
|
||||
|
||||
protected T andOutput(Output output) {
|
||||
return newBucketOperation(outputs.and(output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for SpEL expression-based {@link Output}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract static class ExpressionBucketOperationBuilderSupport<B extends OutputBuilder<B, T>, T extends BucketOperationSupport<T, B>>
|
||||
extends OutputBuilder<B, T> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionBucketOperationBuilderSupport} for the given value, {@link BucketOperationSupport}
|
||||
* and parameters.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
* @param parameters
|
||||
*/
|
||||
protected ExpressionBucketOperationBuilderSupport(String expression, T operation, Object[] parameters) {
|
||||
super(new SpelExpressionOutput(expression, parameters), operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for {@link Output} builders that result in a {@link BucketOperationSupport} providing the built
|
||||
* {@link Output}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract static class OutputBuilder<B extends OutputBuilder<B, T>, T extends BucketOperationSupport<T, B>> {
|
||||
|
||||
protected final Object value;
|
||||
protected final T operation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link OutputBuilder} for the given value and {@link BucketOperationSupport}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @param operation must not be {@literal null}.
|
||||
*/
|
||||
protected OutputBuilder(Object value, T operation) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null or empty!");
|
||||
Assert.notNull(operation, "ProjectionOperation must not be null!");
|
||||
|
||||
this.value = value;
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for a {@code $sum}-expression. <br />
|
||||
* Count expressions are emulated via {@code $sum: 1}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B count() {
|
||||
return sum(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for a {@code $sum}-expression for the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B sum() {
|
||||
return apply(Accumulators.SUM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for a {@code $sum}-expression for the given {@literal value}.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public B sum(Number value) {
|
||||
return apply(new OperationOutput(Accumulators.SUM.getMongoOperator(), Collections.singleton(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $last}-expression for the current value..
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B last() {
|
||||
return apply(Accumulators.LAST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for a {@code $first}-expression the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B first() {
|
||||
return apply(Accumulators.FIRST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $avg}-expression for the current value.
|
||||
*
|
||||
* @param reference
|
||||
* @return
|
||||
*/
|
||||
public B avg() {
|
||||
return apply(Accumulators.AVG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $min}-expression for the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B min() {
|
||||
return apply(Accumulators.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $max}-expression for the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B max() {
|
||||
return apply(Accumulators.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $push}-expression for the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B push() {
|
||||
return apply(Accumulators.PUSH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a builder for an {@code $addToSet}-expression for the current value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public B addToSet() {
|
||||
return apply(Accumulators.ADDTOSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an operator to the current value.
|
||||
*
|
||||
* @param operation the operation name, must not be {@literal null} or empty.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public B apply(String operation, Object... values) {
|
||||
|
||||
Assert.hasText(operation, "Operation must not be empty or null!");
|
||||
Assert.notNull(value, "Values must not be null!");
|
||||
|
||||
List<Object> objects = new ArrayList<Object>(values.length + 1);
|
||||
objects.add(value);
|
||||
objects.addAll(Arrays.asList(values));
|
||||
return apply(new OperationOutput(operation, objects));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an {@link OperationOutput} to this output.
|
||||
*
|
||||
* @param operationOutput must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected abstract B apply(OperationOutput operationOutput);
|
||||
|
||||
private B apply(Accumulators operation) {
|
||||
return this.apply(operation.getMongoOperator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the finally to be applied {@link BucketOperation} with the given alias.
|
||||
*
|
||||
* @param alias will never be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public T as(String alias) {
|
||||
|
||||
if (value instanceof OperationOutput) {
|
||||
return this.operation.andOutput(((OperationOutput) this.value).withAlias(alias));
|
||||
}
|
||||
|
||||
if (value instanceof Field) {
|
||||
throw new IllegalStateException("Cannot add a field as top-level output. Use accumulator expressions.");
|
||||
}
|
||||
|
||||
return this.operation
|
||||
.andOutput(new AggregationExpressionOutput(Fields.field(alias), (AggregationExpression) value));
|
||||
}
|
||||
}
|
||||
|
||||
private enum Accumulators {
|
||||
|
||||
SUM("$sum"), AVG("$avg"), FIRST("$first"), LAST("$last"), MAX("$max"), MIN("$min"), PUSH("$push"), ADDTOSET(
|
||||
"$addToSet");
|
||||
|
||||
private String mongoOperator;
|
||||
|
||||
Accumulators(String mongoOperator) {
|
||||
this.mongoOperator = mongoOperator;
|
||||
}
|
||||
|
||||
public String getMongoOperator() {
|
||||
return mongoOperator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates {@link Output}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
protected static class Outputs implements AggregationExpression {
|
||||
|
||||
protected static final Outputs EMPTY = new Outputs();
|
||||
|
||||
private List<Output> outputs;
|
||||
|
||||
/**
|
||||
* Creates a new, empty {@link Outputs}.
|
||||
*/
|
||||
private Outputs() {
|
||||
this.outputs = new ArrayList<Output>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Outputs} containing all given {@link Output}s.
|
||||
*
|
||||
* @param current
|
||||
* @param output
|
||||
*/
|
||||
private Outputs(Collection<Output> current, Output output) {
|
||||
|
||||
this.outputs = new ArrayList<Output>(current.size() + 1);
|
||||
this.outputs.addAll(current);
|
||||
this.outputs.add(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ExposedFields} derived from {@link Output}.
|
||||
*/
|
||||
protected ExposedFields asExposedFields() {
|
||||
|
||||
// The count field is included by default when the output is not specified.
|
||||
if (isEmpty()) {
|
||||
return ExposedFields.from(new ExposedField("count", true));
|
||||
}
|
||||
|
||||
ExposedFields fields = ExposedFields.from();
|
||||
|
||||
for (Output output : outputs) {
|
||||
fields = fields.and(output.getExposedField());
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Outputs} that contains the new {@link Output}.
|
||||
*
|
||||
* @param output must not be {@literal null}.
|
||||
* @return the new {@link Outputs} that contains the new {@link Output}
|
||||
*/
|
||||
protected Outputs and(Output output) {
|
||||
|
||||
Assert.notNull(output, "BucketOutput must not be null!");
|
||||
return new Outputs(this.outputs, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if {@link Outputs} contains no {@link Output}.
|
||||
*/
|
||||
protected boolean isEmpty() {
|
||||
return outputs.isEmpty();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
|
||||
for (Output output : outputs) {
|
||||
dbObject.put(output.getExposedField().getName(), output.toDbObject(context));
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates an output field in a bucket aggregation stage. <br />
|
||||
* Output fields can be either top-level fields that define a valid field name or nested output fields using
|
||||
* operators.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
protected abstract static class Output implements AggregationExpression {
|
||||
|
||||
private final ExposedField field;
|
||||
|
||||
/**
|
||||
* Creates new {@link Projection} for the given {@link Field}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
*/
|
||||
protected Output(Field field) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
this.field = new ExposedField(field, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field exposed by the {@link Output}.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
protected ExposedField getExposedField() {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output field that uses a Mongo operation (expression object) to generate an output field value. <br />
|
||||
* {@link OperationOutput} is used either with a regular field name or an operation keyword (e.g.
|
||||
* {@literal $sum, $count}).
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
protected static class OperationOutput extends Output {
|
||||
|
||||
private final String operation;
|
||||
private final List<Object> values;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output} for the given field.
|
||||
*
|
||||
* @param operation the actual operation key, must not be {@literal null} or empty.
|
||||
* @param values the values to pass into the operation, must not be {@literal null}.
|
||||
*/
|
||||
public OperationOutput(String operation, Collection<? extends Object> values) {
|
||||
|
||||
super(Fields.field(operation));
|
||||
|
||||
Assert.hasText(operation, "Operation must not be null or empty!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
this.operation = operation;
|
||||
this.values = new ArrayList<Object>(values);
|
||||
}
|
||||
|
||||
private OperationOutput(Field field, OperationOutput operationOutput) {
|
||||
|
||||
super(field);
|
||||
|
||||
this.operation = operationOutput.operation;
|
||||
this.values = operationOutput.values;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
List<Object> operationArguments = getOperationArguments(context);
|
||||
return new BasicDBObject(operation,
|
||||
operationArguments.size() == 1 ? operationArguments.get(0) : operationArguments);
|
||||
}
|
||||
|
||||
protected List<Object> getOperationArguments(AggregationOperationContext context) {
|
||||
|
||||
List<Object> result = new ArrayList<Object>(values != null ? values.size() : 1);
|
||||
|
||||
for (Object element : values) {
|
||||
|
||||
if (element instanceof Field) {
|
||||
result.add(context.getReference((Field) element).toString());
|
||||
} else if (element instanceof Fields) {
|
||||
for (Field field : (Fields) element) {
|
||||
result.add(context.getReference(field).toString());
|
||||
}
|
||||
} else if (element instanceof AggregationExpression) {
|
||||
result.add(((AggregationExpression) element).toDbObject(context));
|
||||
} else {
|
||||
result.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field that holds the {@link ProjectionOperationBuilder.OperationProjection}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected Field getField() {
|
||||
return getExposedField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this {@link OperationOutput} with the given alias.
|
||||
*
|
||||
* @param alias the alias to set
|
||||
* @return
|
||||
*/
|
||||
public OperationOutput withAlias(String alias) {
|
||||
|
||||
final Field aliasedField = Fields.field(alias);
|
||||
return new OperationOutput(aliasedField, this) {
|
||||
|
||||
@Override
|
||||
protected Field getField() {
|
||||
return aliasedField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> getOperationArguments(AggregationOperationContext context) {
|
||||
|
||||
// We have to make sure that we use the arguments from the "previous" OperationOutput that we replace
|
||||
// with this new instance.
|
||||
return OperationOutput.this.getOperationArguments(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Output} based on a SpEL expression.
|
||||
*/
|
||||
private static class SpelExpressionOutput extends Output {
|
||||
|
||||
private static final SpelExpressionTransformer TRANSFORMER = new SpelExpressionTransformer();
|
||||
|
||||
private final String expression;
|
||||
private final Object[] params;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpelExpressionOutput} for the given field, SpEL expression and parameters.
|
||||
*
|
||||
* @param expression must not be {@literal null} or empty.
|
||||
* @param parameters must not be {@literal null}.
|
||||
*/
|
||||
public SpelExpressionOutput(String expression, Object[] parameters) {
|
||||
|
||||
super(Fields.field(expression));
|
||||
|
||||
Assert.hasText(expression, "Expression must not be null!");
|
||||
Assert.notNull(parameters, "Parameters must not be null!");
|
||||
|
||||
this.expression = expression;
|
||||
this.params = parameters.clone();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Output#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return (DBObject) TRANSFORMER.transform(expression, context, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class AggregationExpressionOutput extends Output {
|
||||
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AggregationExpressionOutput}.
|
||||
*
|
||||
* @param field
|
||||
* @param expression
|
||||
*/
|
||||
protected AggregationExpressionOutput(Field field, AggregationExpression expression) {
|
||||
|
||||
super(field);
|
||||
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Output#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return expression.toDbObject(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal comparison expressions}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class ComparisonOperators {
|
||||
|
||||
/**
|
||||
* Take the field referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ComparisonOperatorFactory valueOf(String fieldReference) {
|
||||
return new ComparisonOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting from the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ComparisonOperatorFactory valueOf(AggregationExpression expression) {
|
||||
return new ComparisonOperatorFactory(expression);
|
||||
}
|
||||
|
||||
public static class ComparisonOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public ComparisonOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public ComparisonOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareTo(String fieldReference) {
|
||||
return createCmp().compareTo(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareTo(AggregationExpression expression) {
|
||||
return createCmp().compareTo(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareToValue(Object value) {
|
||||
return createCmp().compareToValue(value);
|
||||
}
|
||||
|
||||
private Cmp createCmp() {
|
||||
return usesFieldRef() ? Cmp.valueOf(fieldReference) : Cmp.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is equal to the value of the referenced field.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalTo(String fieldReference) {
|
||||
return createEq().equalTo(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is equal to the expression result.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalTo(AggregationExpression expression) {
|
||||
return createEq().equalTo(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is equal to the given value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalToValue(Object value) {
|
||||
return createEq().equalToValue(value);
|
||||
}
|
||||
|
||||
private Eq createEq() {
|
||||
return usesFieldRef() ? Eq.valueOf(fieldReference) : Eq.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than the value of the referenced field.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThan(String fieldReference) {
|
||||
return createGt().greaterThan(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than the expression result.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThan(AggregationExpression expression) {
|
||||
return createGt().greaterThan(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than the given value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThanValue(Object value) {
|
||||
return createGt().greaterThanValue(value);
|
||||
}
|
||||
|
||||
private Gt createGt() {
|
||||
return usesFieldRef() ? Gt.valueOf(fieldReference) : Gt.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than or equivalent to the value of the referenced field.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualTo(String fieldReference) {
|
||||
return createGte().greaterThanEqualTo(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than or equivalent to the expression result.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualTo(AggregationExpression expression) {
|
||||
return createGte().greaterThanEqualTo(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is greater than or equivalent to the given value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualToValue(Object value) {
|
||||
return createGte().greaterThanEqualToValue(value);
|
||||
}
|
||||
|
||||
private Gte createGte() {
|
||||
return usesFieldRef() ? Gte.valueOf(fieldReference) : Gte.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than the value of the referenced field.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThan(String fieldReference) {
|
||||
return createLt().lessThan(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than the expression result.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThan(AggregationExpression expression) {
|
||||
return createLt().lessThan(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than to the given value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThanValue(Object value) {
|
||||
return createLt().lessThanValue(value);
|
||||
}
|
||||
|
||||
private Lt createLt() {
|
||||
return usesFieldRef() ? Lt.valueOf(fieldReference) : Lt.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than or equivalent to the value of the referenced field.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualTo(String fieldReference) {
|
||||
return createLte().lessThanEqualTo(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than or equivalent to the expression result.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualTo(AggregationExpression expression) {
|
||||
return createLte().lessThanEqualTo(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the first
|
||||
* value is less than or equivalent to the given value.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualToValue(Object value) {
|
||||
return createLte().lessThanEqualToValue(value);
|
||||
}
|
||||
|
||||
private Lte createLte() {
|
||||
return usesFieldRef() ? Lte.valueOf(fieldReference) : Lte.valueOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the values
|
||||
* are not equivalent.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualTo(String fieldReference) {
|
||||
return createNe().notEqualTo(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the values
|
||||
* are not equivalent.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualTo(AggregationExpression expression) {
|
||||
return createNe().notEqualTo(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares two values and returns {@literal true} when the values
|
||||
* are not equivalent.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualToValue(Object value) {
|
||||
return createNe().notEqualToValue(value);
|
||||
}
|
||||
|
||||
private Ne createNe() {
|
||||
return usesFieldRef() ? Ne.valueOf(fieldReference) : Ne.valueOf(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return fieldReference != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $cmp}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Cmp extends AbstractAggregationExpression {
|
||||
|
||||
private Cmp(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$cmp";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Cmp}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Cmp valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Cmp(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Cmp}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Cmp valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Cmp(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Cmp} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareTo(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Cmp(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Cmp} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Cmp(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Cmp} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Cmp compareToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Cmp(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $eq}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Eq extends AbstractAggregationExpression {
|
||||
|
||||
private Eq(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$eq";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Eq valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Eq(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Eq valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Eq(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalTo(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Eq(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Eq(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Eq equalToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Eq(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $gt}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Gt extends AbstractAggregationExpression {
|
||||
|
||||
private Gt(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$gt";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gt}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Gt valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Gt(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gt}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Gt valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Gt(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThan(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Gt(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThan(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Gt(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gt greaterThanValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Gt(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $lt}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Lt extends AbstractAggregationExpression {
|
||||
|
||||
private Lt(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$lt";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lt}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Lt valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Lt(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lt}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Lt valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Lt(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThan(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Lt(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThan(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Lt(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lt} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lt lessThanValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Lt(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $gte}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Gte extends AbstractAggregationExpression {
|
||||
|
||||
private Gte(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$gte";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gte}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Gte valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Gte(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gte}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Gte valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Gte(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualTo(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Gte(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Gte(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Gte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Gte greaterThanEqualToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Gte(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $lte}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Lte extends AbstractAggregationExpression {
|
||||
|
||||
private Lte(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$lte";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lte}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Lte valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Lte(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lte}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Lte valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Lte(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualTo(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Lte(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Lte(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Lte} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Lte lessThanEqualToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Lte(append(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $ne}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Ne extends AbstractAggregationExpression {
|
||||
|
||||
private Ne(List<?> value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$ne";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Ne}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Ne valueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Ne(asFields(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Ne}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Ne valueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Ne(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Ne} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualTo(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Ne(append(Fields.field(fieldReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Ne} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Ne(append(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Eq} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Ne notEqualToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Ne(append(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal conditional expressions} that evaluate their argument expressions as booleans to a value.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public class ConditionalOperators {
|
||||
|
||||
/**
|
||||
* Take the field referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(String fieldReference) {
|
||||
return new ConditionalOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting from the given {@literal expression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(AggregationExpression expression) {
|
||||
return new ConditionalOperatorFactory(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting from the given {@literal criteriaDefinition}.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(CriteriaDefinition criteriaDefinition) {
|
||||
return new ConditionalOperatorFactory(criteriaDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates an expression and returns the value of the expression if
|
||||
* the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, including
|
||||
* instances of undefined values or missing fields, returns the value of the replacement expression.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IfNull.ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return IfNull.ifNull(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates an expression and returns the value of the expression if
|
||||
* the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, including
|
||||
* instances of undefined values or missing fields, returns the value of the replacement expression.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IfNull.ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return IfNull.ifNull(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it
|
||||
* finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and breaks
|
||||
* out of the control flow.
|
||||
*
|
||||
* @param conditions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Switch switchCases(CaseOperator... conditions) {
|
||||
return Switch.switchCases(conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it
|
||||
* finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and breaks
|
||||
* out of the control flow.
|
||||
*
|
||||
* @param conditions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Switch switchCases(List<CaseOperator> conditions) {
|
||||
return Switch.switchCases(conditions);
|
||||
}
|
||||
|
||||
public static class ConditionalOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
private final CriteriaDefinition criteriaDefinition;
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
this.criteriaDefinition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
this.criteriaDefinition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(CriteriaDefinition criteriaDefinition) {
|
||||
|
||||
Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null!");
|
||||
|
||||
this.fieldReference = null;
|
||||
this.expression = null;
|
||||
this.criteriaDefinition = criteriaDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two specified
|
||||
* return expressions.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder then(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return createThenBuilder().then(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two specified
|
||||
* return expressions.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return createThenBuilder().then(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two specified
|
||||
* return expressions.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return createThenBuilder().then(fieldReference);
|
||||
}
|
||||
|
||||
private ThenBuilder createThenBuilder() {
|
||||
|
||||
if (usesFieldRef()) {
|
||||
return Cond.newBuilder().when(fieldReference);
|
||||
}
|
||||
|
||||
return usesCriteriaDefinition() ? Cond.newBuilder().when(criteriaDefinition) : Cond.newBuilder().when(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return this.fieldReference != null;
|
||||
}
|
||||
|
||||
private boolean usesCriteriaDefinition() {
|
||||
return this.criteriaDefinition != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field
|
||||
* field references}, {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be
|
||||
* converted to a simple MongoDB type.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/">http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/</a>
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class IfNull implements AggregationExpression {
|
||||
|
||||
private final Object condition;
|
||||
private final Object value;
|
||||
|
||||
private IfNull(Object condition, Object value) {
|
||||
|
||||
this.condition = condition;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IfNull}.
|
||||
*
|
||||
* @param fieldReference the field to check for a {@literal null} value, field reference must not be {@literal null}
|
||||
* .
|
||||
* @return
|
||||
*/
|
||||
public static ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IfNullOperatorBuilder().ifNull(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IfNull}.
|
||||
*
|
||||
* @param expression the expression to check for a {@literal null} value, field reference must not be
|
||||
* {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IfNullOperatorBuilder().ifNull(expression);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
|
||||
if (condition instanceof Field) {
|
||||
list.add(context.getReference((Field) condition).toString());
|
||||
} else if (condition instanceof AggregationExpression) {
|
||||
list.add(((AggregationExpression) condition).toDbObject(context));
|
||||
} else {
|
||||
list.add(condition);
|
||||
}
|
||||
|
||||
list.add(resolve(value, context));
|
||||
|
||||
return new BasicDBObject("$ifNull", list);
|
||||
}
|
||||
|
||||
private Object resolve(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
} else if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
} else if (value instanceof DBObject) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface IfNullBuilder {
|
||||
|
||||
/**
|
||||
* @param fieldReference the field to check for a {@literal null} value, field reference must not be
|
||||
* {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression the expression to check for a {@literal null} value, field name must not be {@literal null}
|
||||
* or empty.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the {@code $ifNull} condition evaluates {@literal true}. Can be a
|
||||
* {@link DBObject}, a value that is supported by MongoDB or a value that can be converted to a MongoDB
|
||||
* representation but must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
IfNull then(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference the field holding the replacement value, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
IfNull thenValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression the expression yielding to the replacement value, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
IfNull thenValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link IfNull} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder {
|
||||
|
||||
private Object condition;
|
||||
|
||||
private IfNullOperatorBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link IfNull}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static IfNullOperatorBuilder newBuilder() {
|
||||
return new IfNullOperatorBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull.IfNullBuilder#ifNull(java.lang.String)
|
||||
*/
|
||||
public ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.hasText(fieldReference, "FieldReference name must not be null or empty!");
|
||||
this.condition = Fields.field(fieldReference);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression name must not be null or empty!");
|
||||
this.condition = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull.ThenBuilder#then(java.lang.Object)
|
||||
*/
|
||||
public IfNull then(Object value) {
|
||||
return new IfNull(condition, value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull.ThenBuilder#thenValueOf(java.lang.String)
|
||||
*/
|
||||
public IfNull thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IfNull(condition, Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
public IfNull thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IfNull(condition, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $switch}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Switch extends AbstractAggregationExpression {
|
||||
|
||||
private Switch(java.util.Map<String, Object> values) {
|
||||
super(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$switch";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Switch}.
|
||||
*
|
||||
* @param conditions must not be {@literal null}.
|
||||
*/
|
||||
public static Switch switchCases(CaseOperator... conditions) {
|
||||
|
||||
Assert.notNull(conditions, "Conditions must not be null!");
|
||||
return switchCases(Arrays.asList(conditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Switch}.
|
||||
*
|
||||
* @param conditions must not be {@literal null}.
|
||||
*/
|
||||
public static Switch switchCases(List<CaseOperator> conditions) {
|
||||
|
||||
Assert.notNull(conditions, "Conditions must not be null!");
|
||||
return new Switch(Collections.<String, Object> singletonMap("branches", new ArrayList<CaseOperator>(conditions)));
|
||||
}
|
||||
|
||||
public Switch defaultTo(Object value) {
|
||||
return new Switch(append("default", value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework case document inside a {@code $switch}-operation.
|
||||
*/
|
||||
public static class CaseOperator implements AggregationExpression {
|
||||
|
||||
private final AggregationExpression when;
|
||||
private final Object then;
|
||||
|
||||
private CaseOperator(AggregationExpression when, Object then) {
|
||||
|
||||
this.when = when;
|
||||
this.then = then;
|
||||
}
|
||||
|
||||
public static ThenBuilder when(final AggregationExpression condition) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
|
||||
return new ThenBuilder() {
|
||||
|
||||
@Override
|
||||
public CaseOperator then(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new CaseOperator(condition, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject dbo = new BasicDBObject("case", when.toDbObject(context));
|
||||
|
||||
if (then instanceof AggregationExpression) {
|
||||
dbo.put("then", ((AggregationExpression) then).toDbObject(context));
|
||||
} else if (then instanceof Field) {
|
||||
dbo.put("then", context.getReference((Field) then).toString());
|
||||
} else {
|
||||
dbo.put("then", then);
|
||||
}
|
||||
|
||||
return dbo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* Set the then {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
CaseOperator then(Object value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $cond} operator. A {@link Cond} allows nested conditions
|
||||
* {@code if-then[if-then-else]-else} using {@link Field}, {@link CriteriaDefinition}, {@link AggregationExpression}
|
||||
* or a {@link DBObject custom} condition. Replacement values can be either {@link Field field references},
|
||||
* {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be converted to a
|
||||
* simple MongoDB type.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://docs.mongodb.com/manual/reference/operator/aggregation/cond/">http://docs.mongodb.com/manual/reference/operator/aggregation/cond/</a>
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Cond implements AggregationExpression {
|
||||
|
||||
private final Object condition;
|
||||
private final Object thenValue;
|
||||
private final Object otherwiseValue;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Cond} for a given {@link Field} and {@code then}/{@code otherwise} values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
private Cond(Field condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Cond} for a given {@link CriteriaDefinition} and {@code then}/{@code otherwise} values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
private Cond(CriteriaDefinition condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
private Cond(Object condition, Object thenValue, Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
Assert.notNull(thenValue, "Then value must not be null!");
|
||||
Assert.notNull(otherwiseValue, "Otherwise value must not be null!");
|
||||
|
||||
assertNotBuilder(condition, "Condition");
|
||||
assertNotBuilder(thenValue, "Then value");
|
||||
assertNotBuilder(otherwiseValue, "Otherwise value");
|
||||
|
||||
this.condition = condition;
|
||||
this.thenValue = thenValue;
|
||||
this.otherwiseValue = otherwiseValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject condObject = new BasicDBObject();
|
||||
|
||||
condObject.append("if", resolveCriteria(context, condition));
|
||||
condObject.append("then", resolveValue(context, thenValue));
|
||||
condObject.append("else", resolveValue(context, otherwiseValue));
|
||||
|
||||
return new BasicDBObject("$cond", condObject);
|
||||
}
|
||||
|
||||
private Object resolveValue(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
private Object resolveCriteria(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
if (value instanceof CriteriaDefinition) {
|
||||
|
||||
DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject());
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
clauses.addAll(getClauses(context, mappedObject));
|
||||
|
||||
return clauses.size() == 1 ? clauses.get(0) : clauses;
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value));
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, DBObject mappedObject) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
for (String key : mappedObject.keySet()) {
|
||||
|
||||
Object predicate = mappedObject.get(key);
|
||||
clauses.addAll(getClauses(context, key, predicate));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, String key, Object predicate) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
if (predicate instanceof List) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
for (Object clause : (List<?>) predicate) {
|
||||
if (clause instanceof DBObject) {
|
||||
args.addAll(getClauses(context, (DBObject) clause));
|
||||
}
|
||||
}
|
||||
|
||||
clauses.add(new BasicDBObject(key, args));
|
||||
|
||||
} else if (predicate instanceof DBObject) {
|
||||
|
||||
DBObject nested = (DBObject) predicate;
|
||||
|
||||
for (String s : nested.keySet()) {
|
||||
|
||||
if (!isKeyword(s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(nested.get(s));
|
||||
clauses.add(new BasicDBObject(s, args));
|
||||
}
|
||||
|
||||
} else if (!isKeyword(key)) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(predicate);
|
||||
clauses.add(new BasicDBObject("$eq", args));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link String} is a MongoDB keyword.
|
||||
*
|
||||
* @param candidate
|
||||
* @return
|
||||
*/
|
||||
private boolean isKeyword(String candidate) {
|
||||
return candidate.startsWith("$");
|
||||
}
|
||||
|
||||
private Object resolve(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
return context.getMappedObject((DBObject) value);
|
||||
}
|
||||
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
private void assertNotBuilder(Object toCheck, String name) {
|
||||
Assert.isTrue(!ClassUtils.isAssignableValue(ConditionalExpressionBuilder.class, toCheck),
|
||||
String.format("%s must not be of type %s", name, ConditionalExpressionBuilder.class.getSimpleName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows fluent creation of {@link Cond}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static WhenBuilder newBuilder() {
|
||||
return ConditionalExpressionBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Cond} by providing the boolean expression used in {@code if}.
|
||||
*
|
||||
* @param booleanExpression must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ThenBuilder when(DBObject booleanExpression) {
|
||||
return ConditionalExpressionBuilder.newBuilder().when(booleanExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Cond} by providing the {@link AggregationExpression} used in {@code if}.
|
||||
*
|
||||
* @param expression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ThenBuilder when(AggregationExpression expression) {
|
||||
return ConditionalExpressionBuilder.newBuilder().when(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Cond} by providing the field reference used in {@code if}.
|
||||
*
|
||||
* @param booleanField name of a field holding a boolean value, must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ThenBuilder when(String booleanField) {
|
||||
return ConditionalExpressionBuilder.newBuilder().when(booleanField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Cond} by providing the {@link CriteriaDefinition} used in {@code if}.
|
||||
*
|
||||
* @param criteria criteria to evaluate, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
public static ThenBuilder when(CriteriaDefinition criteria) {
|
||||
return ConditionalExpressionBuilder.newBuilder().when(criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface WhenBuilder {
|
||||
|
||||
/**
|
||||
* @param booleanExpression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(DBObject booleanExpression);
|
||||
|
||||
/**
|
||||
* @param expression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(AggregationExpression expression);
|
||||
|
||||
/**
|
||||
* @param booleanField name of a field holding a boolean value, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(String booleanField);
|
||||
|
||||
/**
|
||||
* @param criteria criteria to evaluate, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal true}. Can be a {@link DBObject}, a
|
||||
* value that is supported by MongoDB or a value that can be converted to a MongoDB representation but
|
||||
* must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder then(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder thenValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder thenValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface OtherwiseBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal false}. Can be a {@link DBObject}, a
|
||||
* value that is supported by MongoDB or a value that can be converted to a MongoDB representation but
|
||||
* must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwise(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwiseValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwiseValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link Cond} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ConditionalExpressionBuilder implements WhenBuilder, ThenBuilder, OtherwiseBuilder {
|
||||
|
||||
private Object condition;
|
||||
private Object thenValue;
|
||||
|
||||
private ConditionalExpressionBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link Cond}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ConditionalExpressionBuilder newBuilder() {
|
||||
return new ConditionalExpressionBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.WhenBuilder#when(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public ConditionalExpressionBuilder when(DBObject booleanExpression) {
|
||||
|
||||
Assert.notNull(booleanExpression, "'Boolean expression' must not be null!");
|
||||
|
||||
this.condition = booleanExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(CriteriaDefinition criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
this.condition = criteria;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression field must not be null!");
|
||||
this.condition = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.WhenBuilder#when(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(String booleanField) {
|
||||
|
||||
Assert.hasText(booleanField, "Boolean field name must not be null or empty!");
|
||||
this.condition = Fields.field(booleanField);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder#then(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder then(Object thenValue) {
|
||||
|
||||
Assert.notNull(thenValue, "Then-value must not be null!");
|
||||
this.thenValue = thenValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder#thenValueOf(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.thenValue = Fields.field(fieldReference);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
this.thenValue = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder#otherwise(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwise(Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(otherwiseValue, "Value must not be null!");
|
||||
return new Cond(condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder#otherwiseValueOf(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwiseValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Cond(condition, thenValue, Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder#otherwiseValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwiseValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
return new Cond(condition, thenValue, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $count}-operation. <br />
|
||||
* We recommend to use the static factory method {@link Aggregation#count()} instead of creating instances of this class
|
||||
* directly.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/reference/operator/aggregation/count/#pipe._S_count">https://docs.mongodb.com/manual/reference/operator/aggregation/count/</a>
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public class CountOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CountOperation} given the {@link fieldName} field name.
|
||||
*
|
||||
* @param asFieldName must not be {@literal null} or empty.
|
||||
*/
|
||||
public CountOperation(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "Field name must not be null or empty!");
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$count", fieldName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return ExposedFields.from(new ExposedField(fieldName, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link CountOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class CountOperationBuilder {
|
||||
|
||||
/**
|
||||
* Returns the finally to be applied {@link CountOperation} with the given alias.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public CountOperation as(String fieldName) {
|
||||
return new CountOperation(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal data type} expressions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @soundtrack Clawfinger - Catch Me
|
||||
*/
|
||||
public class DataTypeOperators {
|
||||
|
||||
/**
|
||||
* Return the BSON data type of the given {@literal field}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Type typeOf(String fieldReference) {
|
||||
return Type.typeOf(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $type}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Type extends AbstractAggregationExpression {
|
||||
|
||||
private Type(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$type";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Type}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Type typeOf(String field) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
return new Type(Fields.field(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators.ArithmeticOperatorFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal Date} aggregation operations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class DateOperators {
|
||||
|
||||
/**
|
||||
* Take the date referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DateOperatorFactory dateOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new DateOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the date resulting from the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DateOperatorFactory dateOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new DateOperatorFactory(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class DateOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public DateOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ArithmeticOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public DateOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and
|
||||
* 366.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public DayOfYear dayOfYear() {
|
||||
return usesFieldRef() ? DayOfYear.dayOfYear(fieldReference) : DayOfYear.dayOfYear(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the day of the month for a date as a number between 1 and
|
||||
* 31.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public DayOfMonth dayOfMonth() {
|
||||
return usesFieldRef() ? DayOfMonth.dayOfMonth(fieldReference) : DayOfMonth.dayOfMonth(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the day of the week for a date as a number between 1
|
||||
* (Sunday) and 7 (Saturday).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public DayOfWeek dayOfWeek() {
|
||||
return usesFieldRef() ? DayOfWeek.dayOfWeek(fieldReference) : DayOfWeek.dayOfWeek(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the year portion of a date.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Year year() {
|
||||
return usesFieldRef() ? Year.yearOf(fieldReference) : Year.yearOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the month of a date as a number between 1 and 12.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Month month() {
|
||||
return usesFieldRef() ? Month.monthOf(fieldReference) : Month.monthOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the week of the year for a date as a number between 0 and
|
||||
* 53.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Week week() {
|
||||
return usesFieldRef() ? Week.weekOf(fieldReference) : Week.weekOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the hour portion of a date as a number between 0 and 23.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Hour hour() {
|
||||
return usesFieldRef() ? Hour.hourOf(fieldReference) : Hour.hourOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the minute portion of a date as a number between 0 and 59.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Minute minute() {
|
||||
return usesFieldRef() ? Minute.minuteOf(fieldReference) : Minute.minuteOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the second portion of a date as a number between 0 and 59,
|
||||
* but can be 60 to account for leap seconds.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Second second() {
|
||||
return usesFieldRef() ? Second.secondOf(fieldReference) : Second.secondOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the millisecond portion of a date as an integer between 0
|
||||
* and 999.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Millisecond millisecond() {
|
||||
return usesFieldRef() ? Millisecond.millisecondOf(fieldReference) : Millisecond.millisecondOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that converts a date object to a string according to a user-specified
|
||||
* {@literal format}.
|
||||
*
|
||||
* @param format must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public DateToString toString(String format) {
|
||||
return (usesFieldRef() ? DateToString.dateOf(fieldReference) : DateToString.dateOf(expression)).toString(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the weekday number in ISO 8601 format, ranging from 1 (for
|
||||
* Monday) to 7 (for Sunday).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IsoDayOfWeek isoDayOfWeek() {
|
||||
return usesFieldRef() ? IsoDayOfWeek.isoDayOfWeek(fieldReference) : IsoDayOfWeek.isoDayOfWeek(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the week number in ISO 8601 format, ranging from 1 to 53.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IsoWeek isoWeek() {
|
||||
return usesFieldRef() ? IsoWeek.isoWeekOf(fieldReference) : IsoWeek.isoWeekOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that returns the year number in ISO 8601 format.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IsoWeekYear isoWeekYear() {
|
||||
return usesFieldRef() ? IsoWeekYear.isoWeekYearOf(fieldReference) : IsoWeekYear.isoWeekYearOf(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return fieldReference != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $dayOfYear}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class DayOfYear extends AbstractAggregationExpression {
|
||||
|
||||
private DayOfYear(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$dayOfYear";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfYear}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfYear dayOfYear(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new DayOfYear(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfYear}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfYear dayOfYear(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new DayOfYear(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $dayOfMonth}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class DayOfMonth extends AbstractAggregationExpression {
|
||||
|
||||
private DayOfMonth(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$dayOfMonth";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfMonth}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfMonth dayOfMonth(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new DayOfMonth(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfMonth}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfMonth dayOfMonth(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new DayOfMonth(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $dayOfWeek}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class DayOfWeek extends AbstractAggregationExpression {
|
||||
|
||||
private DayOfWeek(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$dayOfWeek";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfWeek}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfWeek dayOfWeek(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new DayOfWeek(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link DayOfWeek}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static DayOfWeek dayOfWeek(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new DayOfWeek(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $year}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Year extends AbstractAggregationExpression {
|
||||
|
||||
private Year(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$year";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Year}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Year yearOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Year(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Year}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Year yearOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Year(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $month}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Month extends AbstractAggregationExpression {
|
||||
|
||||
private Month(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$month";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Month}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Month monthOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Month(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Month}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Month monthOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Month(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $week}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Week extends AbstractAggregationExpression {
|
||||
|
||||
private Week(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$week";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Week}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Week weekOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Week(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Week}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Week weekOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Week(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $hour}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Hour extends AbstractAggregationExpression {
|
||||
|
||||
private Hour(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$hour";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Hour}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Hour hourOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Hour(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Hour}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Hour hourOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Hour(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $minute}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Minute extends AbstractAggregationExpression {
|
||||
|
||||
private Minute(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$minute";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Minute}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Minute minuteOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Minute(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Minute}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Minute minuteOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Minute(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $second}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Second extends AbstractAggregationExpression {
|
||||
|
||||
private Second(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$second";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Second}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Second secondOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Second(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Second}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Second secondOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Second(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $millisecond}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Millisecond extends AbstractAggregationExpression {
|
||||
|
||||
private Millisecond(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$millisecond";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Millisecond}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Millisecond millisecondOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Millisecond(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Millisecond}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Millisecond millisecondOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Millisecond(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $dateToString}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class DateToString extends AbstractAggregationExpression {
|
||||
|
||||
private DateToString(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$dateToString";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link FormatBuilder} allowing to define the date format to apply.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static FormatBuilder dateOf(final String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
|
||||
return new FormatBuilder() {
|
||||
|
||||
@Override
|
||||
public DateToString toString(String format) {
|
||||
|
||||
Assert.notNull(format, "Format must not be null!");
|
||||
return new DateToString(argumentMap(Fields.field(fieldReference), format));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link FormatBuilder} allowing to define the date format to apply.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static FormatBuilder dateOf(final AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
|
||||
return new FormatBuilder() {
|
||||
|
||||
@Override
|
||||
public DateToString toString(String format) {
|
||||
|
||||
Assert.notNull(format, "Format must not be null!");
|
||||
return new DateToString(argumentMap(expression, format));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static java.util.Map<String, Object> argumentMap(Object date, String format) {
|
||||
|
||||
java.util.Map<String, Object> args = new LinkedHashMap<String, Object>(2);
|
||||
args.put("format", format);
|
||||
args.put("date", date);
|
||||
return args;
|
||||
}
|
||||
|
||||
public interface FormatBuilder {
|
||||
|
||||
/**
|
||||
* Creates new {@link DateToString} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param format must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
DateToString toString(String format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $isoDayOfWeek}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class IsoDayOfWeek extends AbstractAggregationExpression {
|
||||
|
||||
private IsoDayOfWeek(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$isoDayOfWeek";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IsoDayOfWeek}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoDayOfWeek isoDayOfWeek(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IsoDayOfWeek(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IsoDayOfWeek}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoDayOfWeek isoDayOfWeek(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IsoDayOfWeek(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $isoWeek}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class IsoWeek extends AbstractAggregationExpression {
|
||||
|
||||
private IsoWeek(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$isoWeek";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IsoWeek}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoWeek isoWeekOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IsoWeek(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IsoWeek}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoWeek isoWeekOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IsoWeek(expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $isoWeekYear}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class IsoWeekYear extends AbstractAggregationExpression {
|
||||
|
||||
private IsoWeekYear(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$isoWeekYear";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IsoWeekYear}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoWeekYear isoWeekYearOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IsoWeekYear(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Millisecond}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IsoWeekYear isoWeekYearOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IsoWeekYear(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -22,14 +22,17 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CompositeIterator;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Value object to capture the fields exposed by an {@link AggregationOperation}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
* @since 1.3
|
||||
*/
|
||||
public final class ExposedFields implements Iterable<ExposedField> {
|
||||
@@ -88,7 +91,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFields} instance for the given fields in either sythetic or non-synthetic way.
|
||||
* Creates a new {@link ExposedFields} instance for the given fields in either synthetic or non-synthetic way.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @param synthetic
|
||||
@@ -103,11 +106,11 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
result.add(new ExposedField(field, synthetic));
|
||||
}
|
||||
|
||||
return ExposedFields.from(result);
|
||||
return from(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFields} with the given orignals and synthetics.
|
||||
* Creates a new {@link ExposedFields} with the given originals and synthetics.
|
||||
*
|
||||
* @param originals must not be {@literal null}.
|
||||
* @param synthetic must not be {@literal null}.
|
||||
@@ -203,8 +206,13 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
public Iterator<ExposedField> iterator() {
|
||||
|
||||
CompositeIterator<ExposedField> iterator = new CompositeIterator<ExposedField>();
|
||||
iterator.add(syntheticFields.iterator());
|
||||
iterator.add(originalFields.iterator());
|
||||
if (!syntheticFields.isEmpty()) {
|
||||
iterator.add(syntheticFields.iterator());
|
||||
}
|
||||
|
||||
if (!originalFields.isEmpty()) {
|
||||
iterator.add(originalFields.iterator());
|
||||
}
|
||||
|
||||
return iterator;
|
||||
}
|
||||
@@ -330,12 +338,36 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to an {@link ExposedField}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
interface FieldReference {
|
||||
|
||||
/**
|
||||
* Returns the raw, unqualified reference, i.e. the field reference without a {@literal $} prefix.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getRaw();
|
||||
|
||||
/**
|
||||
* Returns the reference value for the given field reference. Will return 1 for a synthetic, unaliased field or the
|
||||
* raw rendering of the reference otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object getReferenceValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to an {@link ExposedField}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class FieldReference {
|
||||
static class DirectFieldReference implements FieldReference {
|
||||
|
||||
private final ExposedField field;
|
||||
|
||||
@@ -344,17 +376,16 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
*/
|
||||
public FieldReference(ExposedField field) {
|
||||
public DirectFieldReference(ExposedField field) {
|
||||
|
||||
Assert.notNull(field, "ExposedField must not be null!");
|
||||
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw, unqualified reference, i.e. the field reference without a {@literal $} prefix.
|
||||
*
|
||||
* @return
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference#getRaw()
|
||||
*/
|
||||
public String getRaw() {
|
||||
|
||||
@@ -362,11 +393,9 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
return field.synthetic ? target : String.format("%s.%s", Fields.UNDERSCORE_ID, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the referenve value for the given field reference. Will return 1 for a synthetic, unaliased field or the
|
||||
* raw rendering of the reference otherwise.
|
||||
*
|
||||
* @return
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference#getReferenceValue()
|
||||
*/
|
||||
public Object getReferenceValue() {
|
||||
return field.synthetic && !field.isAliased() ? 1 : toString();
|
||||
@@ -378,6 +407,11 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
if(getRaw().startsWith("$")) {
|
||||
return getRaw();
|
||||
}
|
||||
|
||||
return String.format("$%s", getRaw());
|
||||
}
|
||||
|
||||
@@ -392,11 +426,11 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof FieldReference)) {
|
||||
if (!(obj instanceof DirectFieldReference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FieldReference that = (FieldReference) obj;
|
||||
DirectFieldReference that = (DirectFieldReference) obj;
|
||||
|
||||
return this.field.equals(that.field);
|
||||
}
|
||||
@@ -410,4 +444,78 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
return field.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link FieldReference} to a {@link Field} used within a nested {@link AggregationExpression}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
static class ExpressionFieldReference implements FieldReference {
|
||||
|
||||
private FieldReference delegate;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FieldReference} for the given {@link ExposedField}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
*/
|
||||
public ExpressionFieldReference(FieldReference field) {
|
||||
delegate = field;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference#getRaw()
|
||||
*/
|
||||
@Override
|
||||
public String getRaw() {
|
||||
return delegate.getRaw();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference#getReferenceValue()
|
||||
*/
|
||||
@Override
|
||||
public Object getReferenceValue() {
|
||||
return delegate.getReferenceValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String fieldRef = delegate.toString();
|
||||
|
||||
if (fieldRef.startsWith("$$")) {
|
||||
return fieldRef;
|
||||
}
|
||||
|
||||
if (fieldRef.startsWith("$")) {
|
||||
return "$" + fieldRef;
|
||||
}
|
||||
|
||||
return fieldRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof ExpressionFieldReference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpressionFieldReference that = (ExpressionFieldReference) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -24,9 +25,10 @@ import com.mongodb.DBObject;
|
||||
/**
|
||||
* {@link AggregationOperationContext} that combines the available field references from a given
|
||||
* {@code AggregationOperationContext} and an {@link FieldsExposingAggregationOperation}.
|
||||
*
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @since 1.4
|
||||
*/
|
||||
class ExposedFieldsAggregationOperationContext implements AggregationOperationContext {
|
||||
@@ -37,11 +39,12 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
/**
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
|
||||
* {@link AggregationOperationContext} to perform a mapping to mongo types if necessary.
|
||||
*
|
||||
*
|
||||
* @param exposedFields must not be {@literal null}.
|
||||
* @param rootContext must not be {@literal null}.
|
||||
*/
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields, AggregationOperationContext rootContext) {
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields,
|
||||
AggregationOperationContext rootContext) {
|
||||
|
||||
Assert.notNull(exposedFields, "ExposedFields must not be null!");
|
||||
Assert.notNull(rootContext, "RootContext must not be null!");
|
||||
@@ -79,7 +82,7 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
|
||||
/**
|
||||
* Returns a {@link FieldReference} to the given {@link Field} with the given {@code name}.
|
||||
*
|
||||
*
|
||||
* @param field may be {@literal null}
|
||||
* @param name must not be {@literal null}
|
||||
* @return
|
||||
@@ -88,16 +91,32 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
|
||||
FieldReference exposedField = resolveExposedField(field, name);
|
||||
if (exposedField != null) {
|
||||
return exposedField;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a {@link field}/{@link name} for a {@link FieldReference} if possible.
|
||||
*
|
||||
* @param field may be {@literal null}
|
||||
* @param name must not be {@literal null}
|
||||
* @return the resolved reference or {@literal null}
|
||||
*/
|
||||
protected FieldReference resolveExposedField(Field field, String name) {
|
||||
ExposedField exposedField = exposedFields.getField(name);
|
||||
|
||||
if (exposedField != null) {
|
||||
|
||||
if (field != null) {
|
||||
// we return a FieldReference to the given field directly to make sure that we reference the proper alias here.
|
||||
return new FieldReference(new ExposedField(field, exposedField.isSynthetic()));
|
||||
return new DirectFieldReference(new ExposedField(field, exposedField.isSynthetic()));
|
||||
}
|
||||
|
||||
return new FieldReference(exposedField);
|
||||
return new DirectFieldReference(exposedField);
|
||||
}
|
||||
|
||||
if (name.contains(".")) {
|
||||
@@ -108,10 +127,9 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
if (rootField != null) {
|
||||
|
||||
// We have to synthetic to true, in order to render the field-name as is.
|
||||
return new FieldReference(new ExposedField(name, true));
|
||||
return new DirectFieldReference(new ExposedField(name, true));
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Output;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $facet}-operation. <br />
|
||||
* Facet of {@link AggregationOperation}s to be used in an {@link Aggregation}. Processes multiple
|
||||
* {@link AggregationOperation} pipelines within a single stage on the same set of input documents. Each sub-pipeline
|
||||
* has its own field in the output document where its results are stored as an array of documents.
|
||||
* {@link FacetOperation} enables various aggregations on the same set of input documents, without needing to retrieve
|
||||
* the input documents multiple times. <br />
|
||||
* As of MongoDB 3.4, {@link FacetOperation} cannot be used with nested pipelines containing {@link GeoNearOperation},
|
||||
* {@link OutOperation} and {@link FacetOperation}. <br />
|
||||
* We recommend to use the static factory method {@link Aggregation#facet()} instead of creating instances of this class
|
||||
* directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/facet/
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class FacetOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
/**
|
||||
* Empty (initial) {@link FacetOperation}.
|
||||
*/
|
||||
public static final FacetOperation EMPTY = new FacetOperation();
|
||||
|
||||
private final Facets facets;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FacetOperation}.
|
||||
*/
|
||||
public FacetOperation() {
|
||||
this(Facets.EMPTY);
|
||||
}
|
||||
|
||||
private FacetOperation(Facets facets) {
|
||||
this.facets = facets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FacetOperationBuilder} to append a new facet using {@literal operations}. <br />
|
||||
* {@link FacetOperationBuilder} takes a pipeline of {@link AggregationOperation} to categorize documents into a
|
||||
* single facet.
|
||||
*
|
||||
* @param operations must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public FacetOperationBuilder and(AggregationOperation... operations) {
|
||||
|
||||
Assert.notNull(operations, "AggregationOperations must not be null!");
|
||||
Assert.notEmpty(operations, "AggregationOperations must not be empty!");
|
||||
|
||||
return new FacetOperationBuilder(facets, Arrays.asList(operations));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$facet", facets.toDBObject(context));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return facets.asExposedFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link FacetOperation} by adding existing and the new pipeline of {@link AggregationOperation} to the
|
||||
* new {@link FacetOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class FacetOperationBuilder {
|
||||
|
||||
private final Facets current;
|
||||
private final List<AggregationOperation> operations;
|
||||
|
||||
private FacetOperationBuilder(Facets current, List<AggregationOperation> operations) {
|
||||
this.current = current;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FacetOperation} that contains the configured pipeline of {@link AggregationOperation}
|
||||
* exposed as {@literal fieldName} in the resulting facet document.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public FacetOperation as(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "FieldName must not be null or empty!");
|
||||
|
||||
return new FacetOperation(current.and(fieldName, operations));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates multiple {@link Facet}s
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class Facets {
|
||||
|
||||
private static final Facets EMPTY = new Facets(Collections.<Facet> emptyList());
|
||||
|
||||
private List<Facet> facets;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Facets} given {@link List} of {@link Facet}.
|
||||
*
|
||||
* @param facets
|
||||
*/
|
||||
private Facets(List<Facet> facets) {
|
||||
this.facets = facets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ExposedFields} derived from {@link Output}.
|
||||
*/
|
||||
ExposedFields asExposedFields() {
|
||||
|
||||
ExposedFields fields = ExposedFields.from();
|
||||
|
||||
for (Facet facet : facets) {
|
||||
fields = fields.and(facet.getExposedField());
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject dbObject = new BasicDBObject(facets.size());
|
||||
|
||||
for (Facet facet : facets) {
|
||||
dbObject.put(facet.getExposedField().getName(), facet.toDBObjects(context));
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a facet to this {@link Facets}.
|
||||
*
|
||||
* @param fieldName must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
* @return the new {@link Facets}.
|
||||
*/
|
||||
Facets and(String fieldName, List<AggregationOperation> operations) {
|
||||
|
||||
Assert.hasText(fieldName, "FieldName must not be null or empty!");
|
||||
Assert.notNull(operations, "AggregationOperations must not be null!");
|
||||
|
||||
List<Facet> facets = new ArrayList<Facet>(this.facets.size() + 1);
|
||||
facets.addAll(this.facets);
|
||||
facets.add(new Facet(new ExposedField(fieldName, true), operations));
|
||||
|
||||
return new Facets(facets);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single facet with a {@link ExposedField} and its {@link AggregationOperation} pipeline.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class Facet {
|
||||
|
||||
private final ExposedField exposedField;
|
||||
private final List<AggregationOperation> operations;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Facet} given {@link ExposedField} and {@link AggregationOperation} pipeline.
|
||||
*
|
||||
* @param exposedField must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
*/
|
||||
Facet(ExposedField exposedField, List<AggregationOperation> operations) {
|
||||
|
||||
Assert.notNull(exposedField, "ExposedField must not be null!");
|
||||
Assert.notNull(operations, "AggregationOperations must not be null!");
|
||||
|
||||
this.exposedField = exposedField;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
ExposedField getExposedField() {
|
||||
return exposedField;
|
||||
}
|
||||
|
||||
List<DBObject> toDBObjects(AggregationOperationContext context) {
|
||||
return AggregationOperationRenderer.toDBObject(operations, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -84,6 +85,15 @@ public final class Fields implements Iterable<Field> {
|
||||
return new AggregationField(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Field} with the given {@code name} and {@code target}.
|
||||
* <p>
|
||||
* The {@code target} is the name of the backing document field that will be aliased with {@code name}.
|
||||
*
|
||||
* @param name
|
||||
* @param target must not be {@literal null} or empty
|
||||
* @return
|
||||
*/
|
||||
public static Field field(String name, String target) {
|
||||
Assert.hasText(target, "Target must not be null or empty!");
|
||||
return new AggregationField(name, target);
|
||||
@@ -176,6 +186,14 @@ public final class Fields implements Iterable<Field> {
|
||||
return fields.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public List<Field> asList() {
|
||||
return Collections.unmodifiableList(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object to encapsulate a field in an aggregation operation.
|
||||
*
|
||||
@@ -183,21 +201,32 @@ public final class Fields implements Iterable<Field> {
|
||||
*/
|
||||
static class AggregationField implements Field {
|
||||
|
||||
private final String raw;
|
||||
private final String name;
|
||||
private final String target;
|
||||
|
||||
/**
|
||||
* Creates an aggregation field with the given name. As no target is set explicitly, the name will be used as target
|
||||
* as well.
|
||||
* Creates an aggregation field with the given {@code name}.
|
||||
*
|
||||
* @param key
|
||||
* @see AggregationField#AggregationField(String, String).
|
||||
* @param name must not be {@literal null} or empty
|
||||
*/
|
||||
public AggregationField(String key) {
|
||||
this(key, null);
|
||||
public AggregationField(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an aggregation field with the given {@code name} and {@code target}.
|
||||
* <p>
|
||||
* The {@code name} serves as an alias for the actual backing document field denoted by {@code target}. If no target
|
||||
* is set explicitly, the name will be used as target.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty
|
||||
* @param target
|
||||
*/
|
||||
public AggregationField(String name, String target) {
|
||||
|
||||
raw = name;
|
||||
String nameToSet = cleanUp(name);
|
||||
String targetToSet = cleanUp(target);
|
||||
|
||||
@@ -239,6 +268,11 @@ public final class Fields implements Iterable<Field> {
|
||||
* @see org.springframework.data.mongodb.core.aggregation.Field#getAlias()
|
||||
*/
|
||||
public String getTarget() {
|
||||
|
||||
if (isLocalVar()) {
|
||||
return this.getRaw();
|
||||
}
|
||||
|
||||
return StringUtils.hasText(this.target) ? this.target : this.name;
|
||||
}
|
||||
|
||||
@@ -251,6 +285,22 @@ public final class Fields implements Iterable<Field> {
|
||||
return !getName().equals(getTarget());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} in case the field name starts with {@code $$}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public boolean isLocalVar() {
|
||||
return raw.startsWith("$$") && !raw.startsWith("$$$");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public String getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -16,17 +16,28 @@
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
/**
|
||||
* {@link AggregationOperation} that exposes new {@link ExposedFields} that can be used for later aggregation pipeline
|
||||
* {@code AggregationOperation}s.
|
||||
*
|
||||
* {@link AggregationOperation} that exposes {@link ExposedFields} that can be used for later aggregation pipeline
|
||||
* {@code AggregationOperation}s. A {@link FieldsExposingAggregationOperation} implementing the
|
||||
* {@link InheritsFieldsAggregationOperation} will expose fields from its parent operations. Not implementing
|
||||
* {@link InheritsFieldsAggregationOperation} will replace existing exposed fields.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface FieldsExposingAggregationOperation extends AggregationOperation {
|
||||
|
||||
/**
|
||||
* Returns the fields exposed by the {@link AggregationOperation}.
|
||||
*
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
ExposedFields getFields();
|
||||
|
||||
/**
|
||||
* Marker interface for {@link AggregationOperation} that inherits fields from previous operations.
|
||||
*/
|
||||
static interface InheritsFieldsAggregationOperation extends FieldsExposingAggregationOperation {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,17 +22,33 @@ import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Represents a {@code geoNear} aggregation operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#geoNear(NearQuery, String)} instead of creating
|
||||
* instances of this class directly.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @since 1.3
|
||||
*/
|
||||
public class GeoNearOperation implements AggregationOperation {
|
||||
|
||||
private final NearQuery nearQuery;
|
||||
private final String distanceField;
|
||||
|
||||
public GeoNearOperation(NearQuery nearQuery) {
|
||||
/**
|
||||
* Creates a new {@link GeoNearOperation} from the given {@link NearQuery} and the given distance field. The
|
||||
* {@code distanceField} defines output field that contains the calculated distance.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param distanceField must not be {@literal null}.
|
||||
*/
|
||||
public GeoNearOperation(NearQuery nearQuery, String distanceField) {
|
||||
|
||||
Assert.notNull(nearQuery, "NearQuery must not be null.");
|
||||
Assert.hasLength(distanceField, "Distance field must not be null or empty.");
|
||||
|
||||
Assert.notNull(nearQuery);
|
||||
this.nearQuery = nearQuery;
|
||||
this.distanceField = distanceField;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -41,6 +57,10 @@ public class GeoNearOperation implements AggregationOperation {
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$geoNear", context.getMappedObject(nearQuery.toDBObject()));
|
||||
|
||||
BasicDBObject command = (BasicDBObject) context.getMappedObject(nearQuery.toDBObject());
|
||||
command.put("distanceField", distanceField);
|
||||
|
||||
return new BasicDBObject("$geoNear", command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $graphLookup}-operation. <br />
|
||||
* Performs a recursive search on a collection, with options for restricting the search by recursion depth and query
|
||||
* filter. <br />
|
||||
* We recommend to use the static factory method {@link Aggregation#graphLookup(String)} instead of creating instances
|
||||
* of this class directly.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://docs.mongodb.org/manual/reference/aggregation/graphLookup/">http://docs.mongodb.org/manual/reference/aggregation/graphLookup/</a>
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class GraphLookupOperation implements InheritsFieldsAggregationOperation {
|
||||
|
||||
private static final Set<Class<?>> ALLOWED_START_TYPES = new HashSet<Class<?>>(
|
||||
Arrays.<Class<?>> asList(AggregationExpression.class, String.class, Field.class, DBObject.class));
|
||||
|
||||
private final String from;
|
||||
private final List<Object> startWith;
|
||||
private final Field connectFrom;
|
||||
private final Field connectTo;
|
||||
private final Field as;
|
||||
private final Long maxDepth;
|
||||
private final Field depthField;
|
||||
private final CriteriaDefinition restrictSearchWithMatch;
|
||||
|
||||
private GraphLookupOperation(String from, List<Object> startWith, Field connectFrom, Field connectTo, Field as,
|
||||
Long maxDepth, Field depthField, CriteriaDefinition restrictSearchWithMatch) {
|
||||
|
||||
this.from = from;
|
||||
this.startWith = startWith;
|
||||
this.connectFrom = connectFrom;
|
||||
this.connectTo = connectTo;
|
||||
this.as = as;
|
||||
this.maxDepth = maxDepth;
|
||||
this.depthField = depthField;
|
||||
this.restrictSearchWithMatch = restrictSearchWithMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FromBuilder} to build {@link GraphLookupOperation}.
|
||||
*
|
||||
* @return a new {@link FromBuilder}.
|
||||
*/
|
||||
public static FromBuilder builder() {
|
||||
return new GraphLookupOperationFromBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
DBObject graphLookup = new BasicDBObject();
|
||||
|
||||
graphLookup.put("from", from);
|
||||
|
||||
List<Object> mappedStartWith = new ArrayList<Object>(startWith.size());
|
||||
|
||||
for (Object startWithElement : startWith) {
|
||||
|
||||
if (startWithElement instanceof AggregationExpression) {
|
||||
mappedStartWith.add(((AggregationExpression) startWithElement).toDbObject(context));
|
||||
} else if (startWithElement instanceof Field) {
|
||||
mappedStartWith.add(context.getReference((Field) startWithElement).toString());
|
||||
} else {
|
||||
mappedStartWith.add(startWithElement);
|
||||
}
|
||||
}
|
||||
|
||||
graphLookup.put("startWith", mappedStartWith.size() == 1 ? mappedStartWith.iterator().next() : mappedStartWith);
|
||||
|
||||
graphLookup.put("connectFromField", connectFrom.getName());
|
||||
graphLookup.put("connectToField", connectTo.getName());
|
||||
graphLookup.put("as", as.getName());
|
||||
|
||||
if (maxDepth != null) {
|
||||
graphLookup.put("maxDepth", maxDepth);
|
||||
}
|
||||
|
||||
if (depthField != null) {
|
||||
graphLookup.put("depthField", depthField.getName());
|
||||
}
|
||||
|
||||
if (restrictSearchWithMatch != null) {
|
||||
graphLookup.put("restrictSearchWithMatch", context.getMappedObject(restrictSearchWithMatch.getCriteriaObject()));
|
||||
}
|
||||
|
||||
return new BasicDBObject("$graphLookup", graphLookup);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return ExposedFields.from(new ExposedField(as, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface FromBuilder {
|
||||
|
||||
/**
|
||||
* Set the {@literal collectionName} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
StartWithBuilder from(String collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface StartWithBuilder {
|
||||
|
||||
/**
|
||||
* Set the startWith {@literal fieldReferences} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param fieldReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
ConnectFromBuilder startWith(String... fieldReferences);
|
||||
|
||||
/**
|
||||
* Set the startWith {@literal expressions} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
ConnectFromBuilder startWith(AggregationExpression... expressions);
|
||||
|
||||
/**
|
||||
* Set the startWith as either {@literal fieldReferences}, {@link Fields}, {@link DBObject} or
|
||||
* {@link AggregationExpression} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
ConnectFromBuilder startWith(Object... expressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ConnectFromBuilder {
|
||||
|
||||
/**
|
||||
* Set the connectFrom {@literal fieldName} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
ConnectToBuilder connectFrom(String fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ConnectToBuilder {
|
||||
|
||||
/**
|
||||
* Set the connectTo {@literal fieldName} to apply the {@code $graphLookup} to.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
GraphLookupOperationBuilder connectTo(String fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to build the initial {@link GraphLookupOperationBuilder} that configures the initial mandatory set of
|
||||
* {@link GraphLookupOperation} properties.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static final class GraphLookupOperationFromBuilder
|
||||
implements FromBuilder, StartWithBuilder, ConnectFromBuilder, ConnectToBuilder {
|
||||
|
||||
private String from;
|
||||
private List<? extends Object> startWith;
|
||||
private String connectFrom;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.FromBuilder#from(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public StartWithBuilder from(String collectionName) {
|
||||
|
||||
Assert.hasText(collectionName, "CollectionName must not be null or empty!");
|
||||
|
||||
this.from = collectionName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder#startWith(java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public ConnectFromBuilder startWith(String... fieldReferences) {
|
||||
|
||||
Assert.notNull(fieldReferences, "FieldReferences must not be null!");
|
||||
Assert.noNullElements(fieldReferences, "FieldReferences must not contain null elements!");
|
||||
|
||||
List<Object> fields = new ArrayList<Object>(fieldReferences.length);
|
||||
|
||||
for (String fieldReference : fieldReferences) {
|
||||
fields.add(Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
this.startWith = fields;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder#startWith(org.springframework.data.mongodb.core.aggregation.AggregationExpression[])
|
||||
*/
|
||||
@Override
|
||||
public ConnectFromBuilder startWith(AggregationExpression... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "AggregationExpressions must not be null!");
|
||||
Assert.noNullElements(expressions, "AggregationExpressions must not contain null elements!");
|
||||
|
||||
this.startWith = Arrays.asList(expressions);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectFromBuilder startWith(Object... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "Expressions must not be null!");
|
||||
Assert.noNullElements(expressions, "Expressions must not contain null elements!");
|
||||
|
||||
this.startWith = verifyAndPotentiallyTransformStartsWithTypes(expressions);
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<Object> verifyAndPotentiallyTransformStartsWithTypes(Object... expressions) {
|
||||
|
||||
List<Object> expressionsToUse = new ArrayList<Object>(expressions.length);
|
||||
|
||||
for (Object expression : expressions) {
|
||||
|
||||
assertStartWithType(expression);
|
||||
|
||||
if (expression instanceof String) {
|
||||
expressionsToUse.add(Fields.field((String) expression));
|
||||
} else {
|
||||
expressionsToUse.add(expression);
|
||||
}
|
||||
|
||||
}
|
||||
return expressionsToUse;
|
||||
}
|
||||
|
||||
private void assertStartWithType(Object expression) {
|
||||
|
||||
for (Class<?> type : ALLOWED_START_TYPES) {
|
||||
|
||||
if (ClassUtils.isAssignable(type, expression.getClass())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Expression must be any of %s but was %s", ALLOWED_START_TYPES, expression.getClass()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.ConnectFromBuilder#connectFrom(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ConnectToBuilder connectFrom(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "ConnectFrom must not be null or empty!");
|
||||
|
||||
this.connectFrom = fieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.ConnectToBuilder#connectTo(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public GraphLookupOperationBuilder connectTo(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "ConnectTo must not be null or empty!");
|
||||
|
||||
return new GraphLookupOperationBuilder(from, startWith, connectFrom, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static final class GraphLookupOperationBuilder {
|
||||
|
||||
private final String from;
|
||||
private final List<Object> startWith;
|
||||
private final Field connectFrom;
|
||||
private final Field connectTo;
|
||||
private Long maxDepth;
|
||||
private Field depthField;
|
||||
private CriteriaDefinition restrictSearchWithMatch;
|
||||
|
||||
protected GraphLookupOperationBuilder(String from, List<? extends Object> startWith, String connectFrom,
|
||||
String connectTo) {
|
||||
|
||||
this.from = from;
|
||||
this.startWith = new ArrayList<Object>(startWith);
|
||||
this.connectFrom = Fields.field(connectFrom);
|
||||
this.connectTo = Fields.field(connectTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally limit the number of recursions.
|
||||
*
|
||||
* @param numberOfRecursions must be greater or equal to zero.
|
||||
* @return
|
||||
*/
|
||||
public GraphLookupOperationBuilder maxDepth(long numberOfRecursions) {
|
||||
|
||||
Assert.isTrue(numberOfRecursions >= 0, "Max depth must be >= 0!");
|
||||
|
||||
this.maxDepth = numberOfRecursions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally add a depth field {@literal fieldName} to each traversed document in the search path.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public GraphLookupOperationBuilder depthField(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "Depth field name must not be null or empty!");
|
||||
|
||||
this.depthField = Fields.field(fieldName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally add a query specifying conditions to the recursive search.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public GraphLookupOperationBuilder restrict(CriteriaDefinition criteriaDefinition) {
|
||||
|
||||
Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null!");
|
||||
|
||||
this.restrictSearchWithMatch = criteriaDefinition;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the array field added to each output document and return the final {@link GraphLookupOperation}.
|
||||
* Contains the documents traversed in the {@literal $graphLookup} stage to reach the document.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return the final {@link GraphLookupOperation}.
|
||||
*/
|
||||
public GraphLookupOperation as(String fieldName) {
|
||||
|
||||
Assert.hasText(fieldName, "As field name must not be null or empty!");
|
||||
|
||||
return new GraphLookupOperation(from, startWith, connectFrom, connectTo, Fields.field(fieldName), maxDepth,
|
||||
depthField, restrictSearchWithMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -31,11 +31,16 @@ import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $group}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#group(Fields)} instead of creating instances of this
|
||||
* class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/group/#stage._S_group
|
||||
* @author Sebastian Herold
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Gustavo de Geus
|
||||
* @author Christoph Strobl
|
||||
* @since 1.3
|
||||
*/
|
||||
public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
@@ -190,6 +195,16 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return newBuilder(GroupOps.LAST, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $last}-expression for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr
|
||||
* @return
|
||||
*/
|
||||
public GroupOperationBuilder last(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.LAST, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for a {@code $first}-expression for the given field-reference.
|
||||
*
|
||||
@@ -200,6 +215,16 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return newBuilder(GroupOps.FIRST, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for a {@code $first}-expression for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr
|
||||
* @return
|
||||
*/
|
||||
public GroupOperationBuilder first(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.FIRST, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $avg}-expression for the given field-reference.
|
||||
*
|
||||
@@ -210,6 +235,16 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return newBuilder(GroupOps.AVG, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $avg}-expression for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr
|
||||
* @return
|
||||
*/
|
||||
public GroupOperationBuilder avg(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.AVG, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $push}-expression for the given field-reference.
|
||||
*
|
||||
@@ -244,6 +279,16 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return newBuilder(GroupOps.MIN, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $min}-expression that for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr
|
||||
* @return
|
||||
*/
|
||||
public GroupOperationBuilder min(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.MIN, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $max}-expression that for the given field-reference.
|
||||
*
|
||||
@@ -254,6 +299,61 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
return newBuilder(GroupOps.MAX, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $max}-expression that for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr
|
||||
* @return
|
||||
*/
|
||||
public GroupOperationBuilder max(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.MAX, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevSamp}-expression that for the given
|
||||
* field-reference.
|
||||
*
|
||||
* @param reference must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public GroupOperationBuilder stdDevSamp(String reference) {
|
||||
return newBuilder(GroupOps.STD_DEV_SAMP, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevSamp}-expression that for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public GroupOperationBuilder stdDevSamp(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.STD_DEV_SAMP, null, expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevPop}-expression that for the given field-reference.
|
||||
*
|
||||
* @param reference must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public GroupOperationBuilder stdDevPop(String reference) {
|
||||
return newBuilder(GroupOps.STD_DEV_POP, reference, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link GroupOperationBuilder} for an {@code $stdDevPop}-expression that for the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expr must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public GroupOperationBuilder stdDevPop(AggregationExpression expr) {
|
||||
return newBuilder(GroupOps.STD_DEV_POP, null, expr);
|
||||
}
|
||||
|
||||
private GroupOperationBuilder newBuilder(Keyword keyword, String reference, Object value) {
|
||||
return new GroupOperationBuilder(this, new Operation(keyword, null, reference, value));
|
||||
}
|
||||
@@ -318,21 +418,18 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private static enum GroupOps implements Keyword {
|
||||
|
||||
SUM, LAST, FIRST, PUSH, AVG, MIN, MAX, ADD_TO_SET, COUNT;
|
||||
SUM("$sum"), LAST("$last"), FIRST("$first"), PUSH("$push"), AVG("$avg"), MIN("$min"), MAX("$max"), ADD_TO_SET("$addToSet"), STD_DEV_POP("$stdDevPop"), STD_DEV_SAMP("$stdDevSamp");
|
||||
|
||||
private String mongoOperator;
|
||||
|
||||
GroupOps(String mongoOperator) {
|
||||
this.mongoOperator = mongoOperator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String[] parts = name().split("_");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (String part : parts) {
|
||||
String lowerCase = part.toLowerCase(Locale.US);
|
||||
builder.append(builder.length() == 0 ? lowerCase : StringUtils.capitalize(lowerCase));
|
||||
}
|
||||
|
||||
return "$" + builder.toString();
|
||||
return mongoOperator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +463,11 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
|
||||
public Object getValue(AggregationOperationContext context) {
|
||||
|
||||
if (reference == null) {
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
|
||||
/**
|
||||
* {@link ExposedFieldsAggregationOperationContext} that inherits fields from its parent
|
||||
* {@link AggregationOperationContext}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.9
|
||||
*/
|
||||
class InheritingExposedFieldsAggregationOperationContext extends ExposedFieldsAggregationOperationContext {
|
||||
|
||||
private final AggregationOperationContext previousContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
|
||||
* {@link AggregationOperationContext} to perform a mapping to mongo types if necessary.
|
||||
*
|
||||
* @param exposedFields must not be {@literal null}.
|
||||
* @param previousContext must not be {@literal null}.
|
||||
*/
|
||||
public InheritingExposedFieldsAggregationOperationContext(ExposedFields exposedFields,
|
||||
AggregationOperationContext previousContext) {
|
||||
|
||||
super(exposedFields, previousContext);
|
||||
|
||||
this.previousContext = previousContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext#resolveExposedField(org.springframework.data.mongodb.core.aggregation.Field, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected FieldReference resolveExposedField(Field field, String name) {
|
||||
|
||||
FieldReference fieldReference = super.resolveExposedField(field, name);
|
||||
if (fieldReference != null) {
|
||||
return fieldReference;
|
||||
}
|
||||
|
||||
if (field != null) {
|
||||
return previousContext.getReference(field);
|
||||
}
|
||||
|
||||
return previousContext.getReference(name);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,10 @@ import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the {@code $limit}-operation
|
||||
* Encapsulates the {@code $limit}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#limit(long)} instead of creating instances of this
|
||||
* class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/limit/
|
||||
* @author Thomas Darimont
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal literal} aggregation operations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class LiteralOperators {
|
||||
|
||||
/**
|
||||
* Take the value referenced by given {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LiteralOperatorFactory valueOf(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new LiteralOperatorFactory(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class LiteralOperatorFactory {
|
||||
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Creates new {@link LiteralOperatorFactory} for given {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
public LiteralOperatorFactory(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Literal} that returns the associated value without parsing.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Literal asLiteral() {
|
||||
return Literal.asLiteral(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $literal}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class Literal extends AbstractAggregationExpression {
|
||||
|
||||
private Literal(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$literal";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Literal}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Literal asLiteral(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Literal(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $lookup}-operation. We recommend to use the static factory method
|
||||
* {@link Aggregation#lookup(String, String, String, String)} instead of creating instances of this class directly.
|
||||
*
|
||||
* @author Alessio Fachechi
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/lookup/#stage._S_lookup
|
||||
* @since 1.9
|
||||
*/
|
||||
public class LookupOperation implements FieldsExposingAggregationOperation, InheritsFieldsAggregationOperation {
|
||||
|
||||
private Field from;
|
||||
private Field localField;
|
||||
private Field foreignField;
|
||||
private ExposedField as;
|
||||
|
||||
/**
|
||||
* Creates a new {@link LookupOperation} for the given {@link Field}s.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param localField must not be {@literal null}.
|
||||
* @param foreignField must not be {@literal null}.
|
||||
* @param as must not be {@literal null}.
|
||||
*/
|
||||
public LookupOperation(Field from, Field localField, Field foreignField, Field as) {
|
||||
|
||||
Assert.notNull(from, "From must not be null!");
|
||||
Assert.notNull(localField, "LocalField must not be null!");
|
||||
Assert.notNull(foreignField, "ForeignField must not be null!");
|
||||
Assert.notNull(as, "As must not be null!");
|
||||
|
||||
this.from = from;
|
||||
this.localField = localField;
|
||||
this.foreignField = foreignField;
|
||||
this.as = new ExposedField(as, true);
|
||||
}
|
||||
|
||||
private LookupOperation() {
|
||||
// used by builder
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return ExposedFields.from(as);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject lookupObject = new BasicDBObject();
|
||||
|
||||
lookupObject.append("from", from.getTarget());
|
||||
lookupObject.append("localField", localField.getTarget());
|
||||
lookupObject.append("foreignField", foreignField.getTarget());
|
||||
lookupObject.append("as", as.getTarget());
|
||||
|
||||
return new BasicDBObject("$lookup", lookupObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows creation of {@link LookupOperation}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static FromBuilder newLookup() {
|
||||
return new LookupOperationBuilder();
|
||||
}
|
||||
|
||||
public static interface FromBuilder {
|
||||
|
||||
/**
|
||||
* @param name the collection in the same database to perform the join with, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
LocalFieldBuilder from(String name);
|
||||
}
|
||||
|
||||
public static interface LocalFieldBuilder {
|
||||
|
||||
/**
|
||||
* @param name the field from the documents input to the {@code $lookup} stage, must not be {@literal null} or
|
||||
* empty.
|
||||
* @return
|
||||
*/
|
||||
ForeignFieldBuilder localField(String name);
|
||||
}
|
||||
|
||||
public static interface ForeignFieldBuilder {
|
||||
|
||||
/**
|
||||
* @param name the field from the documents in the {@code from} collection, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
AsBuilder foreignField(String name);
|
||||
}
|
||||
|
||||
public static interface AsBuilder {
|
||||
|
||||
/**
|
||||
* @param name the name of the new array field to add to the input documents, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
LookupOperation as(String name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link LookupOperation} creation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.9
|
||||
*/
|
||||
public static final class LookupOperationBuilder
|
||||
implements FromBuilder, LocalFieldBuilder, ForeignFieldBuilder, AsBuilder {
|
||||
|
||||
private final LookupOperation lookupOperation;
|
||||
|
||||
private LookupOperationBuilder() {
|
||||
this.lookupOperation = new LookupOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new builder for {@link LookupOperation}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static FromBuilder newBuilder() {
|
||||
return new LookupOperationBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalFieldBuilder from(String name) {
|
||||
|
||||
Assert.hasText(name, "'From' must not be null or empty!");
|
||||
lookupOperation.from = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupOperation as(String name) {
|
||||
|
||||
Assert.hasText(name, "'As' must not be null or empty!");
|
||||
lookupOperation.as = new ExposedField(Fields.field(name), true);
|
||||
return new LookupOperation(lookupOperation.from, lookupOperation.localField, lookupOperation.foreignField,
|
||||
lookupOperation.as);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsBuilder foreignField(String name) {
|
||||
|
||||
Assert.hasText(name, "'ForeignField' must not be null or empty!");
|
||||
lookupOperation.foreignField = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForeignFieldBuilder localField(String name) {
|
||||
|
||||
Assert.hasText(name, "'LocalField' must not be null or empty!");
|
||||
lookupOperation.localField = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,11 @@ import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the {@code $match}-operation
|
||||
* Encapsulates the {@code $match}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method
|
||||
* {@link Aggregation#match(org.springframework.data.mongodb.core.query.Criteria)} instead of creating instances of this
|
||||
* class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/match/
|
||||
* @author Sebastian Herold
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExpressionFieldReference;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* {@link AggregationOperationContext} that delegates {@link FieldReference} resolution and mapping to a parent one, but
|
||||
* assures {@link FieldReference} get converted into {@link ExpressionFieldReference} using {@code $$} to ref an inner
|
||||
* variable.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
class NestedDelegatingExpressionAggregationOperationContext implements AggregationOperationContext {
|
||||
|
||||
private final AggregationOperationContext delegate;
|
||||
|
||||
/**
|
||||
* Creates new {@link NestedDelegatingExpressionAggregationOperationContext}.
|
||||
*
|
||||
* @param referenceContext must not be {@literal null}.
|
||||
*/
|
||||
public NestedDelegatingExpressionAggregationOperationContext(AggregationOperationContext referenceContext) {
|
||||
|
||||
Assert.notNull(referenceContext, "Reference context must not be null!");
|
||||
this.delegate = referenceContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public DBObject getMappedObject(DBObject dbObject) {
|
||||
return delegate.getMappedObject(dbObject);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.Field)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(Field field) {
|
||||
return new ExpressionFieldReference(delegate.getReference(field));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(String name) {
|
||||
return new ExpressionFieldReference(delegate.getReference(name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Encapsulates the {@code $out}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#out(String)} instead of creating instances of this
|
||||
* class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/out/
|
||||
* @author Nikolay Bogdanov
|
||||
*/
|
||||
public class OutOperation implements AggregationOperation {
|
||||
|
||||
private final String collectionName;
|
||||
|
||||
/**
|
||||
* @param outCollectionName Collection name to export the results. Must not be {@literal null}.
|
||||
*/
|
||||
public OutOperation(String outCollectionName) {
|
||||
Assert.notNull(outCollectionName, "Collection name must not be null!");
|
||||
this.collectionName = outCollectionName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$out", collectionName);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -17,10 +17,15 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -28,15 +33,20 @@ import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $project}-operation. Projection of field to be used in an
|
||||
* {@link Aggregation}. A projection is similar to a {@link Field} inclusion/exclusion but more powerful. It can
|
||||
* generate new fields, change values of given field etc.
|
||||
* Encapsulates the aggregation framework {@code $project}-operation.
|
||||
* <p>
|
||||
* Projection of field to be used in an {@link Aggregation}. A projection is similar to a {@link Field}
|
||||
* inclusion/exclusion but more powerful. It can generate new fields, change values of given field etc.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#project(Fields)} instead of creating instances of
|
||||
* this class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/project/
|
||||
* @author Tobias Trelle
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.3
|
||||
*/
|
||||
public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
@@ -99,8 +109,8 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
*/
|
||||
private ProjectionOperation andReplaceLastOneWith(Projection projection) {
|
||||
|
||||
List<Projection> projections = this.projections.isEmpty() ? Collections.<Projection> emptyList() : this.projections
|
||||
.subList(0, this.projections.size() - 1);
|
||||
List<Projection> projections = this.projections.isEmpty() ? Collections.<Projection> emptyList()
|
||||
: this.projections.subList(0, this.projections.size() - 1);
|
||||
return new ProjectionOperation(projections, Arrays.asList(projection));
|
||||
}
|
||||
|
||||
@@ -118,6 +128,10 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return new ExpressionProjectionOperationBuilder(expression, this, params);
|
||||
}
|
||||
|
||||
public ProjectionOperationBuilder and(AggregationExpression expression) {
|
||||
return new ProjectionOperationBuilder(expression, this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes the given fields from the projection.
|
||||
*
|
||||
@@ -231,6 +245,24 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
* @return
|
||||
*/
|
||||
public abstract ProjectionOperation as(String alias);
|
||||
|
||||
/**
|
||||
* Apply a conditional projection using {@link Cond}.
|
||||
*
|
||||
* @param cond must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract ProjectionOperation applyCondition(Cond cond);
|
||||
|
||||
/**
|
||||
* Apply a conditional value replacement for {@literal null} values using {@link IfNull}.
|
||||
*
|
||||
* @param ifNull must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract ProjectionOperation applyCondition(IfNull ifNull);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +364,8 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return new BasicDBObject(getExposedField().getName(), toMongoExpression(context, expression, params));
|
||||
}
|
||||
|
||||
protected static Object toMongoExpression(AggregationOperationContext context, String expression, Object[] params) {
|
||||
protected static Object toMongoExpression(AggregationOperationContext context, String expression,
|
||||
Object[] params) {
|
||||
return TRANSFORMER.transform(expression, context, params);
|
||||
}
|
||||
}
|
||||
@@ -343,6 +376,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class ProjectionOperationBuilder extends AbstractProjectionOperationBuilder {
|
||||
|
||||
@@ -360,7 +394,8 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
* @param operation must not be {@literal null}.
|
||||
* @param previousProjection the previous operation projection, may be {@literal null}.
|
||||
*/
|
||||
public ProjectionOperationBuilder(String name, ProjectionOperation operation, OperationProjection previousProjection) {
|
||||
public ProjectionOperationBuilder(String name, ProjectionOperation operation,
|
||||
OperationProjection previousProjection) {
|
||||
super(name, operation);
|
||||
|
||||
this.name = name;
|
||||
@@ -409,7 +444,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
/**
|
||||
* Allows to specify an alias for the previous projection operation.
|
||||
*
|
||||
* @param string
|
||||
* @param alias
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@@ -417,9 +452,35 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
if (this.previousProjection != null) {
|
||||
return this.operation.andReplaceLastOneWith(this.previousProjection.withAlias(alias));
|
||||
} else {
|
||||
return this.operation.and(new FieldProjection(Fields.field(alias, name), null));
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(alias), (AggregationExpression) value));
|
||||
}
|
||||
|
||||
return this.operation.and(new FieldProjection(Fields.field(alias, name), null));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.ConditionalOperator)
|
||||
*/
|
||||
@Override
|
||||
public ProjectionOperation applyCondition(Cond cond) {
|
||||
|
||||
Assert.notNull(cond, "ConditionalOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), cond));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.IfNullOperator)
|
||||
*/
|
||||
@Override
|
||||
public ProjectionOperation applyCondition(IfNull ifNull) {
|
||||
|
||||
Assert.notNull(ifNull, "IfNullOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), ifNull));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,6 +532,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return project("subtract", Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $subtract} expression that subtracts the result of the given {@link AggregationExpression}
|
||||
* from the previously mentioned field.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder minus(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return project("subtract", expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $multiply} expression that multiplies the given number with the previously mentioned field.
|
||||
*
|
||||
@@ -496,6 +571,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return project("multiply", Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $multiply} expression that multiplies the previously with the result of the
|
||||
* {@link AggregationExpression}. mentioned field.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder multiply(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return project("multiply", expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $divide} expression that divides the previously mentioned field by the given number.
|
||||
*
|
||||
@@ -522,6 +611,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return project("divide", Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $divide} expression that divides the value of the previously mentioned by the result of the
|
||||
* {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder divide(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return project("divide", expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $mod} expression that divides the previously mentioned field by the given number and returns
|
||||
* the remainder.
|
||||
@@ -539,7 +642,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
/**
|
||||
* Generates an {@code $mod} expression that divides the value of the given field by the previously mentioned field
|
||||
* and returns the remainder.
|
||||
*
|
||||
*
|
||||
* @param fieldReference
|
||||
* @return
|
||||
*/
|
||||
@@ -549,7 +652,580 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return project("mod", Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Generates an {@code $mod} expression that divides the value of the previously mentioned field by the result of
|
||||
* the {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder mod(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return project("mod", expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $size} expression that returns the size of the array held by the given field. <br />
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.7
|
||||
*/
|
||||
public ProjectionOperationBuilder size() {
|
||||
return project("size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $cmp} expression (compare to) that compares the value of the field to a given value or field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder cmp(Object compareValue) {
|
||||
return project("cmp", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $eq} expression (equal) that compares the value of the field to a given value or field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder eq(Object compareValue) {
|
||||
return project("eq", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $gt} expression (greater than) that compares the value of the field to a given value or field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder gt(Object compareValue) {
|
||||
return project("gt", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $gte} expression (greater than equal) that compares the value of the field to a given value or
|
||||
* field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder gte(Object compareValue) {
|
||||
return project("gte", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $lt} expression (less than) that compares the value of the field to a given value or field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder lt(Object compareValue) {
|
||||
return project("lt", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $lte} expression (less than equal) that compares the value of the field to a given value or
|
||||
* field.
|
||||
*
|
||||
* @param compareValue the compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder lte(Object compareValue) {
|
||||
return project("lte", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $ne} expression (not equal) that compares the value of the field to a given value or field.
|
||||
*
|
||||
* @param compareValue compare value or a {@link Field} object.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder ne(Object compareValue) {
|
||||
return project("ne", compareValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $slice} expression that returns a subset of the array held by the given field. <br />
|
||||
* If {@literal n} is positive, $slice returns up to the first n elements in the array. <br />
|
||||
* If {@literal n} is negative, $slice returns up to the last n elements in the array.
|
||||
*
|
||||
* @param count max number of elements.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder slice(int count) {
|
||||
return project("slice", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $slice} expression that returns a subset of the array held by the given field. <br />
|
||||
*
|
||||
* @param count max number of elements. Must not be negative.
|
||||
* @param offset the offset within the array to start from.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder slice(int count, int offset) {
|
||||
return project("slice", offset, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $filter} expression that returns a subset of the array held by the given field.
|
||||
*
|
||||
* @param as The variable name for the element in the input array. Must not be {@literal null}.
|
||||
* @param condition The {@link AggregationExpression} that determines whether to include the element in the
|
||||
* resulting array. Must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder filter(String as, AggregationExpression condition) {
|
||||
return this.operation.and(ArrayOperators.Filter.filter(name).as(as).by(condition));
|
||||
}
|
||||
|
||||
// SET OPERATORS
|
||||
|
||||
/**
|
||||
* Generates a {@code $setEquals} expression that compares the previously mentioned field to one or more arrays and
|
||||
* returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
|
||||
*
|
||||
* @param arrays must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder equalsArrays(String... arrays) {
|
||||
|
||||
Assert.notEmpty(arrays, "Arrays must not be null or empty!");
|
||||
return project("setEquals", Fields.fields(arrays));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $setIntersection} expression that takes array of the previously mentioned field and one or
|
||||
* more arrays and returns an array that contains the elements that appear in every of those.
|
||||
*
|
||||
* @param arrays must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder intersectsArrays(String... arrays) {
|
||||
|
||||
Assert.notEmpty(arrays, "Arrays must not be null or empty!");
|
||||
return project("setIntersection", Fields.fields(arrays));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $setUnion} expression that takes array of the previously mentioned field and one or more
|
||||
* arrays and returns an array that contains the elements that appear in any of those.
|
||||
*
|
||||
* @param arrays must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder unionArrays(String... arrays) {
|
||||
|
||||
Assert.notEmpty(arrays, "Arrays must not be null or empty!");
|
||||
return project("setUnion", Fields.fields(arrays));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $setDifference} expression that takes array of the previously mentioned field and returns an
|
||||
* array containing the elements that do not exist in the given {@literal array}.
|
||||
*
|
||||
* @param array must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder differenceToArray(String array) {
|
||||
|
||||
Assert.hasText(array, "Array must not be null or empty!");
|
||||
return project("setDifference", Fields.fields(array));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $setIsSubset} expression that takes array of the previously mentioned field and returns
|
||||
* {@literal true} if it is a subset of the given {@literal array}.
|
||||
*
|
||||
* @param array must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder subsetOfArray(String array) {
|
||||
|
||||
Assert.hasText(array, "Array must not be null or empty!");
|
||||
return project("setIsSubset", Fields.fields(array));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $anyElementTrue} expression that Takes array of the previously mentioned field and returns
|
||||
* {@literal true} if any of the elements are {@literal true} and {@literal false} otherwise.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder anyElementInArrayTrue() {
|
||||
return project("anyElementTrue");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@code $allElementsTrue} expression that takes array of the previously mentioned field and returns
|
||||
* {@literal true} if no elements is {@literal false}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder allElementsInArrayTrue() {
|
||||
return project("allElementsTrue");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $abs} expression that takes the number of the previously mentioned field and returns the
|
||||
* absolute value of it.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder absoluteValue() {
|
||||
return this.operation.and(ArithmeticOperators.Abs.absoluteValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $ceil} expression that takes the number of the previously mentioned field and returns the
|
||||
* smallest integer greater than or equal to the specified number.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder ceil() {
|
||||
return this.operation.and(ArithmeticOperators.Ceil.ceilValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $exp} expression that takes the number of the previously mentioned field and raises Euler’s
|
||||
* number (i.e. e ) on it.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder exp() {
|
||||
return this.operation.and(ArithmeticOperators.Exp.expValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $floor} expression that takes the number of the previously mentioned field and returns the
|
||||
* largest integer less than or equal to it.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder floor() {
|
||||
return this.operation.and(ArithmeticOperators.Floor.floorValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $ln} expression that takes the number of the previously mentioned field and calculates the
|
||||
* natural logarithm ln (i.e loge) of it.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder ln() {
|
||||
return this.operation.and(ArithmeticOperators.Ln.lnValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
|
||||
* log of the associated number in the specified base.
|
||||
*
|
||||
* @param baseFieldRef must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder log(String baseFieldRef) {
|
||||
return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(baseFieldRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
|
||||
* log of the associated number in the specified base.
|
||||
*
|
||||
* @param base must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder log(Number base) {
|
||||
return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
|
||||
* log of the associated number in the specified base.
|
||||
*
|
||||
* @param base must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder log(AggregationExpression base) {
|
||||
return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(base));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $log10} expression that takes the number of the previously mentioned field and calculates the
|
||||
* log base 10.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder log10() {
|
||||
return this.operation.and(ArithmeticOperators.Log10.log10ValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $pow} expression that takes the number of the previously mentioned field and raises it by the
|
||||
* specified exponent.
|
||||
*
|
||||
* @param exponentFieldRef must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder pow(String exponentFieldRef) {
|
||||
return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponentFieldRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $pow} expression that takes the number of the previously mentioned field and raises it by the
|
||||
* specified exponent.
|
||||
*
|
||||
* @param exponent must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder pow(Number exponent) {
|
||||
return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $pow} expression that Takes the number of the previously mentioned field and raises it by the
|
||||
* specified exponent.
|
||||
*
|
||||
* @param exponentExpression must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder pow(AggregationExpression exponentExpression) {
|
||||
return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponentExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $sqrt} expression that takes the number of the previously mentioned field and calculates the
|
||||
* square root.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder sqrt() {
|
||||
return this.operation.and(ArithmeticOperators.Sqrt.sqrtOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the number of the previously mentioned field and truncates it to its integer value.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder trunc() {
|
||||
return this.operation.and(ArithmeticOperators.Trunc.truncValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $concat} expression that takes the string representation of the previously mentioned field and
|
||||
* concats given values to it.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder concat(Object... values) {
|
||||
return project("concat", values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $substr} expression that Takes the string representation of the previously mentioned field and
|
||||
* returns a substring starting at a specified index position.
|
||||
*
|
||||
* @param start
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder substring(int start) {
|
||||
return substring(start, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $substr} expression that takes the string representation of the previously mentioned field and
|
||||
* returns a substring starting at a specified index position including the specified number of characters.
|
||||
*
|
||||
* @param start
|
||||
* @param nrOfChars
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder substring(int start, int nrOfChars) {
|
||||
return project("substr", start, nrOfChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $toLower} expression that takes the string representation of the previously mentioned field
|
||||
* and lowers it.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder toLower() {
|
||||
return this.operation.and(StringOperators.ToLower.lowerValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $toUpper} expression that takes the string representation of the previously mentioned field
|
||||
* and uppers it.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder toUpper() {
|
||||
return this.operation.and(StringOperators.ToUpper.upperValueOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
|
||||
* and performs case-insensitive comparison to the given {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder strCaseCmp(String value) {
|
||||
return project("strcasecmp", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
|
||||
* and performs case-insensitive comparison to the referenced {@literal fieldRef}.
|
||||
*
|
||||
* @param fieldRef must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder strCaseCmpValueOf(String fieldRef) {
|
||||
return project("strcasecmp", fieldRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
|
||||
* and performs case-insensitive comparison to the result of the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder strCaseCmp(AggregationExpression expression) {
|
||||
return project("strcasecmp", expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $arrayElemAt} expression that takes the string representation of the previously mentioned
|
||||
* field and returns the element at the specified array {@literal position}.
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder arrayElementAt(int position) {
|
||||
return project("arrayElemAt", position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $concatArrays} expression that takes the string representation of the previously mentioned
|
||||
* field and concats it with the arrays from the referenced {@literal fields}.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder concatArrays(String... fields) {
|
||||
return project("concatArrays", Fields.fields(fields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $isArray} expression that takes the string representation of the previously mentioned field
|
||||
* and checks if its an array.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder isArray() {
|
||||
return this.operation.and(ArrayOperators.IsArray.isArray(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $literal} expression that Takes the value previously and uses it as literal.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder asLiteral() {
|
||||
return this.operation.and(LiteralOperators.Literal.asLiteral(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $dateToString} expression that takes the date representation of the previously mentioned field
|
||||
* and applies given {@literal format} to it.
|
||||
*
|
||||
* @param format must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder dateAsFormattedString(String format) {
|
||||
return this.operation.and(DateOperators.DateToString.dateOf(name).toString(format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $let} expression that binds variables for use in the specified expression, and returns the
|
||||
* result of the expression.
|
||||
*
|
||||
* @param valueExpression The {@link AggregationExpression} bound to {@literal variableName}.
|
||||
* @param variableName The variable name to be used in the {@literal in} {@link AggregationExpression}.
|
||||
* @param in The {@link AggregationExpression} to evaluate.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder let(AggregationExpression valueExpression, String variableName,
|
||||
AggregationExpression in) {
|
||||
return this.operation.and(VariableOperators.Let.define(ExpressionVariable.newVariable(variableName).forExpression(valueExpression)).andApply(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a {@code $let} expression that binds variables for use in the specified expression, and returns the
|
||||
* result of the expression.
|
||||
*
|
||||
* @param variables The bound {@link ExpressionVariable}s.
|
||||
* @param in The {@link AggregationExpression} to evaluate.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder let(Collection<ExpressionVariable> variables, AggregationExpression in) {
|
||||
return this.operation.and(VariableOperators.Let.define(variables).andApply(in));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@@ -715,7 +1391,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@@ -733,7 +1409,18 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
result.add(context.getReference(getField().getName()).toString());
|
||||
|
||||
for (Object element : values) {
|
||||
result.add(element instanceof Field ? context.getReference((Field) element).toString() : element);
|
||||
|
||||
if (element instanceof Field) {
|
||||
result.add(context.getReference((Field) element).toString());
|
||||
} else if (element instanceof Fields) {
|
||||
for (Field field : (Fields) element) {
|
||||
result.add(context.getReference(field).toString());
|
||||
}
|
||||
} else if (element instanceof AggregationExpression) {
|
||||
result.add(((AggregationExpression) element).toDbObject(context));
|
||||
} else {
|
||||
result.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -748,6 +1435,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return field;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#getExposedField()
|
||||
*/
|
||||
@Override
|
||||
public ExposedField getExposedField() {
|
||||
|
||||
if (!getField().isAliased()) {
|
||||
return super.getExposedField();
|
||||
}
|
||||
|
||||
return new ExposedField(new AggregationField(getField().getName()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this {@link OperationProjection} with the given alias.
|
||||
*
|
||||
@@ -937,4 +1638,31 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
*/
|
||||
public abstract DBObject toDBObject(AggregationOperationContext context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
static class ExpressionProjection extends Projection {
|
||||
|
||||
private final AggregationExpression expression;
|
||||
private final Field field;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionProjection}.
|
||||
*
|
||||
* @param field
|
||||
* @param expression
|
||||
*/
|
||||
public ExpressionProjection(Field field, AggregationExpression expression) {
|
||||
|
||||
super(field);
|
||||
this.field = field;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject(field.getName(), expression.toDbObject(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.expression.spel.ast.Projection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $replaceRoot}-operation. <br />
|
||||
* We recommend to use the static factory method {@link Aggregation#replaceRoot(String)} instead of creating instances
|
||||
* of this class directly.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/">https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/</a>
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class ReplaceRootOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private final Replacement replacement;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootOperation} given the {@link Field} field name.
|
||||
*
|
||||
* @param field must not be {@literal null} or empty.
|
||||
*/
|
||||
public ReplaceRootOperation(Field field) {
|
||||
this(new FieldReplacement(field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootOperation} given the {@link AggregationExpression} pointing to a document.
|
||||
*
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
*/
|
||||
public ReplaceRootOperation(AggregationExpression aggregationExpression) {
|
||||
this(new AggregationExpressionReplacement(aggregationExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootOperation} given the {@link Replacement}.
|
||||
*
|
||||
* @param replacement must not be {@literal null}.
|
||||
*/
|
||||
public ReplaceRootOperation(Replacement replacement) {
|
||||
|
||||
Assert.notNull(replacement, "Replacement must not be null!");
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootDocumentOperationBuilder}.
|
||||
*
|
||||
* @return a new {@link ReplaceRootDocumentOperationBuilder}.
|
||||
*/
|
||||
public static ReplaceRootOperationBuilder builder() {
|
||||
return new ReplaceRootOperationBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$replaceRoot", new BasicDBObject("newRoot", replacement.toDocumentExpression(context)));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return ExposedFields.from();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link ReplaceRootOperation}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class ReplaceRootOperationBuilder {
|
||||
|
||||
/**
|
||||
* Defines a root document replacement based on a {@literal fieldName} that resolves to a document.
|
||||
*
|
||||
* @param fieldName must not be {@literal null} or empty.
|
||||
* @return the final {@link ReplaceRootOperation}.
|
||||
*/
|
||||
public ReplaceRootOperation withValueOf(String fieldName) {
|
||||
return new ReplaceRootOperation(Fields.field(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a root document replacement based on a {@link AggregationExpression} that resolves to a document.
|
||||
*
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
* @return the final {@link ReplaceRootOperation}.
|
||||
*/
|
||||
public ReplaceRootOperation withValueOf(AggregationExpression aggregationExpression) {
|
||||
return new ReplaceRootOperation(aggregationExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a root document replacement based on a composable document that is empty initially. <br />
|
||||
* {@link ReplaceRootOperation} can be populated with individual entries and derive its values from other, existing
|
||||
* documents.
|
||||
*
|
||||
* @return the {@link ReplaceRootDocumentOperation}.
|
||||
*/
|
||||
public ReplaceRootDocumentOperation withDocument() {
|
||||
return new ReplaceRootDocumentOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a root document replacement based on a composable document given {@literal dbObject}. <br />
|
||||
* {@link ReplaceRootOperation} can be populated with individual entries and derive its values from other, existing
|
||||
* documents.
|
||||
*
|
||||
* @param dbObject must not be {@literal null}.
|
||||
* @return the final {@link ReplaceRootOperation}.
|
||||
*/
|
||||
public ReplaceRootOperation withDocument(DBObject dbObject) {
|
||||
|
||||
Assert.notNull(dbObject, "DBObject must not be null!");
|
||||
|
||||
return new ReplaceRootDocumentOperation().andValuesOf(dbObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $replaceRoot}-operation to result in a composable replacement
|
||||
* document. <br />
|
||||
* Instances of {@link ReplaceRootDocumentOperation} yield empty upon construction and can be populated with single
|
||||
* values and documents.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ReplaceRootDocumentOperation extends ReplaceRootOperation {
|
||||
|
||||
private final static ReplacementDocument EMPTY = new ReplacementDocument();
|
||||
private final ReplacementDocument current;
|
||||
|
||||
/**
|
||||
* Creates an empty {@link ReplaceRootDocumentOperation}.
|
||||
*/
|
||||
public ReplaceRootDocumentOperation() {
|
||||
this(EMPTY);
|
||||
}
|
||||
|
||||
private ReplaceRootDocumentOperation(ReplacementDocument replacementDocument) {
|
||||
super(replacementDocument);
|
||||
current = replacementDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an extended {@link ReplaceRootDocumentOperation} that combines {@link ReplacementDocument}s from the
|
||||
* {@literal currentOperation} and {@literal extension} operation.
|
||||
*
|
||||
* @param currentOperation must not be {@literal null}.
|
||||
* @param extension must not be {@literal null}.
|
||||
*/
|
||||
protected ReplaceRootDocumentOperation(ReplaceRootDocumentOperation currentOperation,
|
||||
ReplacementDocument extension) {
|
||||
this(currentOperation.current.extendWith(extension));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootDocumentOperationBuilder} to define a field for the
|
||||
* {@link AggregationExpression}.
|
||||
*
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
* @return the {@link ReplaceRootDocumentOperationBuilder}.
|
||||
*/
|
||||
public ReplaceRootDocumentOperationBuilder and(AggregationExpression aggregationExpression) {
|
||||
return new ReplaceRootDocumentOperationBuilder(this, aggregationExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootDocumentOperationBuilder} to define a field for the {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return the {@link ReplaceRootDocumentOperationBuilder}.
|
||||
*/
|
||||
public ReplaceRootDocumentOperationBuilder andValue(Object value) {
|
||||
return new ReplaceRootDocumentOperationBuilder(this, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReplaceRootDocumentOperation} that merges all existing replacement values with values from
|
||||
* {@literal value}. Existing replacement values are overwritten.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return the {@link ReplaceRootDocumentOperation}.
|
||||
*/
|
||||
public ReplaceRootDocumentOperation andValuesOf(Object value) {
|
||||
return new ReplaceRootDocumentOperation(this, ReplacementDocument.valueOf(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link ReplaceRootDocumentOperation} to populate {@link ReplacementDocument}
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static class ReplaceRootDocumentOperationBuilder {
|
||||
|
||||
private final ReplaceRootDocumentOperation currentOperation;
|
||||
private final Object value;
|
||||
|
||||
protected ReplaceRootDocumentOperationBuilder(ReplaceRootDocumentOperation currentOperation, Object value) {
|
||||
|
||||
Assert.notNull(currentOperation, "Current ReplaceRootDocumentOperation must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
this.currentOperation = currentOperation;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ReplaceRootDocumentOperation as(String fieldName) {
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return new ReplaceRootDocumentOperation(currentOperation,
|
||||
ReplacementDocument.forExpression(fieldName, (AggregationExpression) value));
|
||||
}
|
||||
|
||||
return new ReplaceRootDocumentOperation(currentOperation, ReplacementDocument.forSingleValue(fieldName, value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement object that results in a replacement document or an expression that results in a document.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface Replacement {
|
||||
|
||||
/**
|
||||
* Renders the current {@link Replacement} into a its MongoDB representation based on the given
|
||||
* {@link AggregationOperationContext}.
|
||||
*
|
||||
* @param context will never be {@literal null}.
|
||||
* @return a replacement document or an expression that results in a document.
|
||||
*/
|
||||
Object toDocumentExpression(AggregationOperationContext context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Replacement} that uses a {@link AggregationExpression} that results in a replacement document.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class AggregationExpressionReplacement implements Replacement {
|
||||
|
||||
private final AggregationExpression aggregationExpression;
|
||||
|
||||
protected AggregationExpressionReplacement(AggregationExpression aggregationExpression) {
|
||||
|
||||
Assert.notNull(aggregationExpression, "AggregationExpression must not be null!");
|
||||
this.aggregationExpression = aggregationExpression;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDocumentExpression(AggregationOperationContext context) {
|
||||
return aggregationExpression.toDbObject(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Replacement that references a {@link Field} inside the current aggregation pipeline.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class FieldReplacement implements Replacement {
|
||||
|
||||
private final Field field;
|
||||
|
||||
/**
|
||||
* Creates {@link FieldReplacement} given {@link Field}.
|
||||
*/
|
||||
protected FieldReplacement(Field field) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public Object toDocumentExpression(AggregationOperationContext context) {
|
||||
return context.getReference(field).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement document consisting of multiple {@link ReplacementContributor}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class ReplacementDocument implements Replacement {
|
||||
|
||||
private final Collection<ReplacementContributor> replacements;
|
||||
|
||||
/**
|
||||
* Creates an empty {@link ReplacementDocument}.
|
||||
*/
|
||||
protected ReplacementDocument() {
|
||||
replacements = new ArrayList<ReplacementContributor>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReplacementDocument} given {@link ReplacementContributor}.
|
||||
*
|
||||
* @param contributor
|
||||
*/
|
||||
protected ReplacementDocument(ReplacementContributor contributor) {
|
||||
|
||||
Assert.notNull(contributor, "ReplacementContributor must not be null!");
|
||||
replacements = Collections.singleton(contributor);
|
||||
}
|
||||
|
||||
private ReplacementDocument(Collection<ReplacementContributor> replacements) {
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReplacementDocument} given a {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ReplacementDocument valueOf(Object value) {
|
||||
return new ReplacementDocument(new DocumentContributor(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReplacementDocument} given a single {@literal field} and {@link AggregationExpression}.
|
||||
*
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ReplacementDocument forExpression(String field, AggregationExpression aggregationExpression) {
|
||||
return new ReplacementDocument(new ExpressionFieldContributor(Fields.field(field), aggregationExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReplacementDocument} given a single {@literal field} and {@literal value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ReplacementDocument forSingleValue(String field, Object value) {
|
||||
return new ReplacementDocument(new ValueFieldContributor(Fields.field(field), value));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.Replacement#toObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDocumentExpression(AggregationOperationContext context) {
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
|
||||
for (ReplacementContributor replacement : replacements) {
|
||||
dbObject.putAll(replacement.toDbObject(context));
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend a replacement document that merges {@code this} and {@literal replacement} {@link ReplacementContributor}s
|
||||
* in a new {@link ReplacementDocument}.
|
||||
*
|
||||
* @param extension must not be {@literal null}.
|
||||
* @return the new, extended {@link ReplacementDocument}
|
||||
*/
|
||||
public ReplacementDocument extendWith(ReplacementDocument extension) {
|
||||
|
||||
Assert.notNull(extension, "ReplacementDocument must not be null");
|
||||
|
||||
ReplacementDocument replacementDocument = new ReplacementDocument();
|
||||
|
||||
List<ReplacementContributor> replacements = new ArrayList<ReplacementContributor>(
|
||||
this.replacements.size() + extension.replacements.size());
|
||||
|
||||
replacements.addAll(this.replacements);
|
||||
replacements.addAll(extension.replacements);
|
||||
|
||||
return new ReplacementDocument(replacements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial {@link DBObject} contributor for document replacement.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private interface ReplacementContributor extends AggregationExpression {
|
||||
|
||||
/**
|
||||
* Renders the current {@link ReplacementContributor} into a {@link DBObject} based on the given
|
||||
* {@link AggregationOperationContext}.
|
||||
*
|
||||
* @param context will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
DBObject toDbObject(AggregationOperationContext context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReplacementContributor} to contribute multiple fields based on the input {@literal value}. <br />
|
||||
* The value object is mapped into a MongoDB {@link DBObject}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
private static class DocumentContributor implements ReplacementContributor {
|
||||
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Creates new {@link Projection} for the given {@link Field}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
public DocumentContributor(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
return (DBObject) value;
|
||||
}
|
||||
|
||||
return (DBObject) context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for {@link ReplacementContributor} implementations to contribute a single {@literal field} Typically
|
||||
* used to construct a composite document that should contain the resulting key-value pair.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private abstract static class FieldContributorSupport implements ReplacementContributor {
|
||||
|
||||
private final ExposedField field;
|
||||
|
||||
/**
|
||||
* Creates new {@link FieldContributorSupport} for the given {@link Field}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
*/
|
||||
public FieldContributorSupport(Field field) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
this.field = new ExposedField(field, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ExposedField}.
|
||||
*/
|
||||
public ExposedField getField() {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReplacementContributor} to contribute a single {@literal field} and {@literal value}. The {@literal value}
|
||||
* is mapped to a MongoDB {@link DBObject} and can be a singular value, a list or subdocument.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class ValueFieldContributor extends FieldContributorSupport {
|
||||
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Creates new {@link Projection} for the given {@link Field}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
public ValueFieldContributor(Field field, Object value) {
|
||||
|
||||
super(field);
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
Object mappedValue = value instanceof BasicDBObject ? value
|
||||
: context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
|
||||
return new BasicDBObject(getField().getTarget(), mappedValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReplacementContributor} to contribute a single {@literal field} and value based on a
|
||||
* {@link AggregationExpression}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
private static class ExpressionFieldContributor extends FieldContributorSupport {
|
||||
|
||||
private final AggregationExpression aggregationExpression;
|
||||
|
||||
/**
|
||||
* Creates new {@link Projection} for the given {@link Field}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param aggregationExpression must not be {@literal null}.
|
||||
*/
|
||||
public ExpressionFieldContributor(Field field, AggregationExpression aggregationExpression) {
|
||||
|
||||
super(field);
|
||||
|
||||
Assert.notNull(aggregationExpression, "AggregationExpression must not be null!");
|
||||
|
||||
this.aggregationExpression = aggregationExpression;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplacementContributor#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject(getField().getTarget(), aggregationExpression.toDbObject(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal Set expressions} which perform {@literal set} operation on arrays, treating arrays as sets.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class SetOperators {
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetOperatorFactory arrayAsSet(String fieldReference) {
|
||||
return new SetOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array resulting from the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetOperatorFactory arrayAsSet(AggregationExpression expression) {
|
||||
return new SetOperatorFactory(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link SetOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public SetOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public SetOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares the previously mentioned field to one or more arrays and
|
||||
* returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetEquals isEqualTo(String... arrayReferences) {
|
||||
return createSetEquals().isEqualTo(arrayReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that compares the previously mentioned field to one or more arrays and
|
||||
* returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetEquals isEqualTo(AggregationExpression... expressions) {
|
||||
return createSetEquals().isEqualTo(expressions);
|
||||
}
|
||||
|
||||
private SetEquals createSetEquals() {
|
||||
return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and one or more
|
||||
* arrays and returns an array that contains the elements that appear in every of those.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIntersection intersects(String... arrayReferences) {
|
||||
return createSetIntersection().intersects(arrayReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and one or more
|
||||
* arrays and returns an array that contains the elements that appear in every of those.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIntersection intersects(AggregationExpression... expressions) {
|
||||
return createSetIntersection().intersects(expressions);
|
||||
}
|
||||
|
||||
private SetIntersection createSetIntersection() {
|
||||
return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and one or more
|
||||
* arrays and returns an array that contains the elements that appear in any of those.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetUnion union(String... arrayReferences) {
|
||||
return createSetUnion().union(arrayReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and one or more
|
||||
* arrays and returns an array that contains the elements that appear in any of those.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetUnion union(AggregationExpression... expressions) {
|
||||
return createSetUnion().union(expressions);
|
||||
}
|
||||
|
||||
private SetUnion createSetUnion() {
|
||||
return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and returns an array
|
||||
* containing the elements that do not exist in the given {@literal arrayReference}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetDifference differenceTo(String arrayReference) {
|
||||
return createSetDifference().differenceTo(arrayReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and returns an array
|
||||
* containing the elements that do not exist in the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetDifference differenceTo(AggregationExpression expression) {
|
||||
return createSetDifference().differenceTo(expression);
|
||||
}
|
||||
|
||||
private SetDifference createSetDifference() {
|
||||
return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and returns
|
||||
* {@literal true} if it is a subset of the given {@literal arrayReference}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIsSubset isSubsetOf(String arrayReference) {
|
||||
return createSetIsSubset().isSubsetOf(arrayReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and returns
|
||||
* {@literal true} if it is a subset of the given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIsSubset isSubsetOf(AggregationExpression expression) {
|
||||
return createSetIsSubset().isSubsetOf(expression);
|
||||
}
|
||||
|
||||
private SetIsSubset createSetIsSubset() {
|
||||
return usesFieldRef() ? SetIsSubset.arrayAsSet(fieldReference) : SetIsSubset.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that takes array of the previously mentioned field and returns
|
||||
* {@literal true} if any of the elements are {@literal true} and {@literal false} otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AnyElementTrue anyElementTrue() {
|
||||
return usesFieldRef() ? AnyElementTrue.arrayAsSet(fieldReference) : AnyElementTrue.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that tkes array of the previously mentioned field and returns
|
||||
* {@literal true} if no elements is {@literal false}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AllElementsTrue allElementsTrue() {
|
||||
return usesFieldRef() ? AllElementsTrue.arrayAsSet(fieldReference) : AllElementsTrue.arrayAsSet(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return this.fieldReference != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $setEquals}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetEquals extends AbstractAggregationExpression {
|
||||
|
||||
private SetEquals(List<?> arrays) {
|
||||
super(arrays);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$setEquals";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link SetEquals}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetEquals arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetEquals(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link SetEquals}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetEquals arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetEquals(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link java.util.Set} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetEquals isEqualTo(String... arrayReferences) {
|
||||
|
||||
Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
|
||||
return new SetEquals(append(Fields.fields(arrayReferences).asList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetEquals isEqualTo(AggregationExpression... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "Expressions must not be null!");
|
||||
return new SetEquals(append(Arrays.asList(expressions)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Sum} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param array must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetEquals isEqualTo(Object[] array) {
|
||||
|
||||
Assert.notNull(array, "Array must not be null!");
|
||||
return new SetEquals(append(array));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $setIntersection}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetIntersection extends AbstractAggregationExpression {
|
||||
|
||||
private SetIntersection(List<?> arrays) {
|
||||
super(arrays);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$setIntersection";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIntersection}
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetIntersection arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetIntersection(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIntersection}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetIntersection arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetIntersection(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIntersection} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIntersection intersects(String... arrayReferences) {
|
||||
|
||||
Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
|
||||
return new SetIntersection(append(asFields(arrayReferences)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIntersection} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIntersection intersects(AggregationExpression... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "Expressions must not be null!");
|
||||
return new SetIntersection(append(Arrays.asList(expressions)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $setUnion}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetUnion extends AbstractAggregationExpression {
|
||||
|
||||
private SetUnion(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$setUnion";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetUnion}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetUnion arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetUnion(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetUnion}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetUnion arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetUnion(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetUnion} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param arrayReferences must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetUnion union(String... arrayReferences) {
|
||||
|
||||
Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
|
||||
return new SetUnion(append(asFields(arrayReferences)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetUnion} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expressions must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetUnion union(AggregationExpression... expressions) {
|
||||
|
||||
Assert.notNull(expressions, "Expressions must not be null!");
|
||||
return new SetUnion(append(Arrays.asList(expressions)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $setDifference}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetDifference extends AbstractAggregationExpression {
|
||||
|
||||
private SetDifference(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$setDifference";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetDifference}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetDifference arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetDifference(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetDifference}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetDifference arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetDifference(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetDifference} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetDifference differenceTo(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetDifference(append(Fields.field(arrayReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetDifference} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetDifference differenceTo(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetDifference(append(expression));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $setIsSubset}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class SetIsSubset extends AbstractAggregationExpression {
|
||||
|
||||
private SetIsSubset(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$setIsSubset";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIsSubset}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetIsSubset arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetIsSubset(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIsSubset}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static SetIsSubset arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetIsSubset(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIsSubset} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIsSubset isSubsetOf(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new SetIsSubset(append(Fields.field(arrayReference)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SetIsSubset} with all previously added arguments appending the given one.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public SetIsSubset isSubsetOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new SetIsSubset(append(expression));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $anyElementTrue}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class AnyElementTrue extends AbstractAggregationExpression {
|
||||
|
||||
private AnyElementTrue(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$anyElementTrue";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AnyElementTrue}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AnyElementTrue arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new AnyElementTrue(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AnyElementTrue}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AnyElementTrue arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new AnyElementTrue(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
public AnyElementTrue anyElementTrue() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $allElementsTrue}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class AllElementsTrue extends AbstractAggregationExpression {
|
||||
|
||||
private AllElementsTrue(Object value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMongoMethod() {
|
||||
return "$allElementsTrue";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AllElementsTrue}.
|
||||
*
|
||||
* @param arrayReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AllElementsTrue arrayAsSet(String arrayReference) {
|
||||
|
||||
Assert.notNull(arrayReference, "ArrayReference must not be null!");
|
||||
return new AllElementsTrue(asFields(arrayReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AllElementsTrue}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AllElementsTrue arrayAsSet(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new AllElementsTrue(Collections.singletonList(expression));
|
||||
}
|
||||
|
||||
public AllElementsTrue allElementsTrue() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -22,6 +22,9 @@ import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $skip}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#skip(int)} instead of creating instances of this
|
||||
* class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/skip/
|
||||
* @author Thomas Darimont
|
||||
@@ -35,7 +38,7 @@ public class SkipOperation implements AggregationOperation {
|
||||
/**
|
||||
* Creates a new {@link SkipOperation} skipping the given number of elements.
|
||||
*
|
||||
* @param skipCount number of documents to skip.
|
||||
* @param skipCount number of documents to skip, must not be less than zero.
|
||||
*/
|
||||
public SkipOperation(long skipCount) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,9 @@ import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $sort}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#sort(Direction, String...)} instead of creating
|
||||
* instances of this class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/sort/#pipe._S_sort
|
||||
* @author Thomas Darimont
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -26,19 +26,26 @@ import org.springframework.data.mongodb.core.spel.ExpressionNode;
|
||||
import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
|
||||
import org.springframework.data.mongodb.core.spel.LiteralNode;
|
||||
import org.springframework.data.mongodb.core.spel.MethodReferenceNode;
|
||||
import org.springframework.data.mongodb.core.spel.MethodReferenceNode.AggregationMethodReference;
|
||||
import org.springframework.data.mongodb.core.spel.MethodReferenceNode.AggregationMethodReference.ArgumentType;
|
||||
import org.springframework.data.mongodb.core.spel.NotOperatorNode;
|
||||
import org.springframework.data.mongodb.core.spel.OperatorNode;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.ast.CompoundExpression;
|
||||
import org.springframework.expression.spel.ast.ConstructorReference;
|
||||
import org.springframework.expression.spel.ast.Indexer;
|
||||
import org.springframework.expression.spel.ast.InlineList;
|
||||
import org.springframework.expression.spel.ast.InlineMap;
|
||||
import org.springframework.expression.spel.ast.OperatorNot;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.NumberUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -48,6 +55,7 @@ import com.mongodb.DBObject;
|
||||
* Renders the AST of a SpEL expression as a MongoDB Aggregation Framework projection expression.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
|
||||
@@ -69,6 +77,8 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
conversions.add(new PropertyOrFieldReferenceNodeConversion(this));
|
||||
conversions.add(new CompoundExpressionNodeConversion(this));
|
||||
conversions.add(new MethodReferenceNodeConversion(this));
|
||||
conversions.add(new NotOperatorNodeConversion(this));
|
||||
conversions.add(new ValueRetrievingNodeConversion(this));
|
||||
|
||||
this.conversions = Collections.unmodifiableList(conversions);
|
||||
}
|
||||
@@ -131,8 +141,8 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static abstract class ExpressionNodeConversion<T extends ExpressionNode> implements
|
||||
AggregationExpressionTransformer {
|
||||
private static abstract class ExpressionNodeConversion<T extends ExpressionNode>
|
||||
implements AggregationExpressionTransformer {
|
||||
|
||||
private final AggregationExpressionTransformer transformer;
|
||||
private final Class<? extends ExpressionNode> nodeType;
|
||||
@@ -235,8 +245,17 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
protected Object convert(AggregationExpressionTransformationContext<OperatorNode> context) {
|
||||
|
||||
OperatorNode currentNode = context.getCurrentNode();
|
||||
|
||||
DBObject operationObject = createOperationObjectAndAddToPreviousArgumentsIfNecessary(context, currentNode);
|
||||
|
||||
if (currentNode.isLogicalOperator()) {
|
||||
|
||||
for (ExpressionNode expressionNode : currentNode) {
|
||||
transform(expressionNode, currentNode, operationObject, context);
|
||||
}
|
||||
|
||||
return operationObject;
|
||||
}
|
||||
|
||||
Object leftResult = transform(currentNode.getLeft(), currentNode, operationObject, context);
|
||||
|
||||
if (currentNode.isUnaryMinus()) {
|
||||
@@ -271,7 +290,8 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return nextDbObject;
|
||||
}
|
||||
|
||||
private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<OperatorNode> context, Object leftResult) {
|
||||
private Object convertUnaryMinusOp(ExpressionTransformationContextSupport<OperatorNode> context,
|
||||
Object leftResult) {
|
||||
|
||||
Object result = leftResult instanceof Number ? leftResult
|
||||
: new BasicDBObject("$multiply", dbList(-1, leftResult));
|
||||
@@ -289,7 +309,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
*/
|
||||
@Override
|
||||
protected boolean supports(ExpressionNode node) {
|
||||
return node.isMathematicalOperation();
|
||||
return node.isMathematicalOperation() || node.isLogicalOperator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,13 +482,33 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
protected Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) {
|
||||
|
||||
MethodReferenceNode node = context.getCurrentNode();
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
AggregationMethodReference methodReference = node.getMethodReference();
|
||||
|
||||
for (ExpressionNode childNode : node) {
|
||||
args.add(transform(childNode, context));
|
||||
Object args = null;
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.SINGLE)) {
|
||||
args = transform(node.getChild(0), context);
|
||||
} else if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.MAP)) {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
|
||||
int i = 0;
|
||||
for(ExpressionNode child : node) {
|
||||
dbo.put(methodReference.getArgumentMap()[i++], transform(child, context));
|
||||
}
|
||||
args = dbo;
|
||||
} else {
|
||||
|
||||
List<Object> argList = new ArrayList<Object>();
|
||||
|
||||
for (ExpressionNode childNode : node) {
|
||||
argList.add(transform(childNode, context));
|
||||
}
|
||||
|
||||
args = dbList(argList.toArray());
|
||||
}
|
||||
|
||||
return context.addToPreviousOrReturn(new BasicDBObject(node.getMethodName(), dbList(args.toArray())));
|
||||
return context.addToPreviousOrReturn(new BasicDBObject(methodReference.getMongoOperator(), args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,4 +550,81 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
return node.isOfType(CompoundExpression.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
static class NotOperatorNodeConversion extends ExpressionNodeConversion<NotOperatorNode> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionNodeConversion}.
|
||||
*
|
||||
* @param transformer must not be {@literal null}.
|
||||
*/
|
||||
public NotOperatorNodeConversion(AggregationExpressionTransformer transformer) {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@Override
|
||||
protected Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) {
|
||||
|
||||
NotOperatorNode node = context.getCurrentNode();
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
|
||||
for (ExpressionNode childNode : node) {
|
||||
args.add(transform(childNode, context));
|
||||
}
|
||||
|
||||
return context.addToPreviousOrReturn(new BasicDBObject(node.getMongoOperator(), dbList(args.toArray())));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
@Override
|
||||
protected boolean supports(ExpressionNode node) {
|
||||
return node.isOfType(OperatorNot.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
static class ValueRetrievingNodeConversion extends ExpressionNodeConversion<ExpressionNode> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExpressionNodeConversion}.
|
||||
*
|
||||
* @param transformer must not be {@literal null}.
|
||||
*/
|
||||
public ValueRetrievingNodeConversion(AggregationExpressionTransformer transformer) {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.SpelNodeWrapper#convertSpelNodeToMongoObjectExpression(org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.ExpressionConversionContext)
|
||||
*/
|
||||
@Override
|
||||
protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
|
||||
return context.getCurrentNode().getValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.SpelExpressionTransformer.NodeConversion#supports(org.springframework.data.mongodb.core.spel.ExpressionNode)
|
||||
*/
|
||||
@Override
|
||||
protected boolean supports(ExpressionNode node) {
|
||||
return node.isOfType(InlineMap.class) || node.isOfType(InlineList.class)
|
||||
|| node.isOfType(ConstructorReference.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
@@ -98,6 +99,6 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
|
||||
Field mappedField = field(propertyPath.getLeafProperty().getName(),
|
||||
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
|
||||
|
||||
return new FieldReference(new ExposedField(mappedField, true));
|
||||
return new DirectFieldReference(new ExposedField(mappedField, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -23,33 +23,245 @@ import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $unwind}-operation.
|
||||
* <p>
|
||||
* We recommend to use the static factory method {@link Aggregation#unwind(String)} instead of creating instances of
|
||||
* this class directly.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/unwind/#pipe._S_unwind
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.3
|
||||
*/
|
||||
public class UnwindOperation implements AggregationOperation {
|
||||
public class UnwindOperation
|
||||
implements AggregationOperation, FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation {
|
||||
|
||||
private final ExposedField field;
|
||||
private final ExposedField arrayIndex;
|
||||
private final boolean preserveNullAndEmptyArrays;
|
||||
|
||||
/**
|
||||
* Creates a new {@link UnwindOperation} for the given {@link Field}.
|
||||
*
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
*/
|
||||
public UnwindOperation(Field field) {
|
||||
|
||||
Assert.notNull(field);
|
||||
this.field = new ExposedField(field, true);
|
||||
this(new ExposedField(field, true), false);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Creates a new {@link UnwindOperation} using Mongo 3.2 syntax.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
|
||||
* array is empty.
|
||||
* @since 1.10
|
||||
*/
|
||||
public UnwindOperation(Field field, boolean preserveNullAndEmptyArrays) {
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
|
||||
this.field = new ExposedField(field, true);
|
||||
this.arrayIndex = null;
|
||||
this.preserveNullAndEmptyArrays = preserveNullAndEmptyArrays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link UnwindOperation} using Mongo 3.2 syntax.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param arrayIndex optional field name to expose the field array index, must not be {@literal null}.
|
||||
* @param preserveNullAndEmptyArrays {@literal true} to output the document if path is {@literal null}, missing or
|
||||
* array is empty.
|
||||
* @since 1.10
|
||||
*/
|
||||
public UnwindOperation(Field field, Field arrayIndex, boolean preserveNullAndEmptyArrays) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
Assert.notNull(arrayIndex, "ArrayIndex must not be null!");
|
||||
|
||||
this.field = new ExposedField(field, true);
|
||||
this.arrayIndex = new ExposedField(arrayIndex, true);
|
||||
this.preserveNullAndEmptyArrays = preserveNullAndEmptyArrays;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject("$unwind", context.getReference(field).toString());
|
||||
|
||||
String path = context.getReference(field).toString();
|
||||
|
||||
if (!preserveNullAndEmptyArrays && arrayIndex == null) {
|
||||
return new BasicDBObject("$unwind", path);
|
||||
}
|
||||
|
||||
DBObject unwindArgs = new BasicDBObject();
|
||||
unwindArgs.put("path", path);
|
||||
if (arrayIndex != null) {
|
||||
unwindArgs.put("includeArrayIndex", arrayIndex.getName());
|
||||
}
|
||||
unwindArgs.put("preserveNullAndEmptyArrays", preserveNullAndEmptyArrays);
|
||||
|
||||
return new BasicDBObject("$unwind", unwindArgs);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return arrayIndex != null ? ExposedFields.from(arrayIndex) : ExposedFields.from();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows creation of {@link LookupOperation}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static PathBuilder newUnwind() {
|
||||
return UnwindOperationBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface PathBuilder {
|
||||
|
||||
/**
|
||||
* @param path the path to unwind, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
IndexBuilder path(String path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface IndexBuilder {
|
||||
|
||||
/**
|
||||
* Exposes the array index as {@code field}.
|
||||
*
|
||||
* @param field field name to expose the field array index, must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
EmptyArraysBuilder arrayIndex(String field);
|
||||
|
||||
/**
|
||||
* Do not expose the array index.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
EmptyArraysBuilder noArrayIndex();
|
||||
}
|
||||
|
||||
public static interface EmptyArraysBuilder {
|
||||
|
||||
/**
|
||||
* Output documents if the array is null or empty.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UnwindOperation preserveNullAndEmptyArrays();
|
||||
|
||||
/**
|
||||
* Do not output documents if the array is null or empty.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UnwindOperation skipNullAndEmptyArrays();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link UnwindOperation} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public static final class UnwindOperationBuilder implements PathBuilder, IndexBuilder, EmptyArraysBuilder {
|
||||
|
||||
private Field field;
|
||||
private Field arrayIndex;
|
||||
|
||||
private UnwindOperationBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates new builder for {@link UnwindOperation}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static PathBuilder newBuilder() {
|
||||
return new UnwindOperationBuilder();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.UnwindOperation.EmptyArraysBuilder#preserveNullAndEmptyArrays()
|
||||
*/
|
||||
@Override
|
||||
public UnwindOperation preserveNullAndEmptyArrays() {
|
||||
|
||||
if (arrayIndex != null) {
|
||||
return new UnwindOperation(field, arrayIndex, true);
|
||||
}
|
||||
|
||||
return new UnwindOperation(field, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.UnwindOperation.EmptyArraysBuilder#skipNullAndEmptyArrays()
|
||||
*/
|
||||
@Override
|
||||
public UnwindOperation skipNullAndEmptyArrays() {
|
||||
|
||||
if (arrayIndex != null) {
|
||||
return new UnwindOperation(field, arrayIndex, false);
|
||||
}
|
||||
|
||||
return new UnwindOperation(field, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.UnwindOperation.IndexBuilder#arrayIndex(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public EmptyArraysBuilder arrayIndex(String field) {
|
||||
|
||||
Assert.hasText(field, "'ArrayIndex' must not be null or empty!");
|
||||
arrayIndex = Fields.field(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.UnwindOperation.IndexBuilder#noArrayIndex()
|
||||
*/
|
||||
@Override
|
||||
public EmptyArraysBuilder noArrayIndex() {
|
||||
|
||||
arrayIndex = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.UnwindOperation.PathBuilder#path(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public UnwindOperationBuilder path(String path) {
|
||||
|
||||
Assert.hasText(path, "'Path' must not be null or empty!");
|
||||
field = Fields.field(path);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Gateway to {@literal variable} aggregation operations.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public class VariableOperators {
|
||||
|
||||
/**
|
||||
* Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array
|
||||
* and returns an array with the applied results.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Map.AsBuilder mapItemsOf(String fieldReference) {
|
||||
return Map.itemsOf(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array
|
||||
* and returns an array with the applied results.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Map.AsBuilder mapItemsOf(AggregationExpression expression) {
|
||||
return Map.itemsOf(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a
|
||||
* nested {@link AggregationExpression}.
|
||||
*
|
||||
* @param variables must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Let.LetBuilder define(ExpressionVariable... variables) {
|
||||
return Let.define(variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a
|
||||
* nested {@link AggregationExpression}.
|
||||
*
|
||||
* @param variables must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Let.LetBuilder define(Collection<ExpressionVariable> variables) {
|
||||
return Let.define(variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $map}.
|
||||
*/
|
||||
public static class Map implements AggregationExpression {
|
||||
|
||||
private Object sourceArray;
|
||||
private String itemVariableName;
|
||||
private AggregationExpression functionToApply;
|
||||
|
||||
private Map(Object sourceArray, String itemVariableName, AggregationExpression functionToApply) {
|
||||
|
||||
Assert.notNull(sourceArray, "SourceArray must not be null!");
|
||||
Assert.notNull(itemVariableName, "ItemVariableName must not be null!");
|
||||
Assert.notNull(functionToApply, "FunctionToApply must not be null!");
|
||||
|
||||
this.sourceArray = sourceArray;
|
||||
this.itemVariableName = itemVariableName;
|
||||
this.functionToApply = functionToApply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array
|
||||
* and returns an array with the applied results.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AsBuilder itemsOf(final String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
|
||||
return new AsBuilder() {
|
||||
|
||||
@Override
|
||||
public FunctionBuilder as(final String variableName) {
|
||||
|
||||
Assert.notNull(variableName, "VariableName must not be null!");
|
||||
|
||||
return new FunctionBuilder() {
|
||||
|
||||
@Override
|
||||
public Map andApply(final AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
return new Map(Fields.field(fieldReference), variableName, expression);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array
|
||||
* and returns an array with the applied results.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static AsBuilder itemsOf(final AggregationExpression source) {
|
||||
|
||||
Assert.notNull(source, "AggregationExpression must not be null!");
|
||||
|
||||
return new AsBuilder() {
|
||||
|
||||
@Override
|
||||
public FunctionBuilder as(final String variableName) {
|
||||
|
||||
Assert.notNull(variableName, "VariableName must not be null!");
|
||||
|
||||
return new FunctionBuilder() {
|
||||
|
||||
@Override
|
||||
public Map andApply(final AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
return new Map(source, variableName, expression);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(final AggregationOperationContext context) {
|
||||
return toMap(ExposedFields.synthetic(Fields.fields(itemVariableName)), context);
|
||||
}
|
||||
|
||||
private DBObject toMap(ExposedFields exposedFields, AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject map = new BasicDBObject();
|
||||
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
|
||||
exposedFields, context);
|
||||
|
||||
BasicDBObject input;
|
||||
if (sourceArray instanceof Field) {
|
||||
input = new BasicDBObject("input", context.getReference((Field) sourceArray).toString());
|
||||
} else {
|
||||
input = new BasicDBObject("input", ((AggregationExpression) sourceArray).toDbObject(context));
|
||||
}
|
||||
|
||||
map.putAll(context.getMappedObject(input));
|
||||
map.put("as", itemVariableName);
|
||||
map.put("in",
|
||||
functionToApply.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(operationContext)));
|
||||
|
||||
return new BasicDBObject("$map", map);
|
||||
}
|
||||
|
||||
public interface AsBuilder {
|
||||
|
||||
/**
|
||||
* Define the {@literal variableName} for addressing items within the array.
|
||||
*
|
||||
* @param variableName must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
FunctionBuilder as(String variableName);
|
||||
}
|
||||
|
||||
public interface FunctionBuilder {
|
||||
|
||||
/**
|
||||
* Creates new {@link Map} that applies the given {@link AggregationExpression} to each item of the referenced
|
||||
* array and returns an array with the applied results.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Map andApply(AggregationExpression expression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AggregationExpression} for {@code $let} that binds {@link AggregationExpression} to variables for use in the
|
||||
* specified {@code in} expression, and returns the result of the expression.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public static class Let implements AggregationExpression {
|
||||
|
||||
private final List<ExpressionVariable> vars;
|
||||
private final AggregationExpression expression;
|
||||
|
||||
private Let(List<ExpressionVariable> vars, AggregationExpression expression) {
|
||||
|
||||
this.vars = vars;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Let} by defining the variables for {@code $vars}.
|
||||
*
|
||||
* @param variables must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LetBuilder define(final Collection<ExpressionVariable> variables) {
|
||||
|
||||
Assert.notNull(variables, "Variables must not be null!");
|
||||
|
||||
return new LetBuilder() {
|
||||
|
||||
@Override
|
||||
public Let andApply(final AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Let(new ArrayList<ExpressionVariable>(variables), expression);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Start creating new {@link Let} by defining the variables for {@code $vars}.
|
||||
*
|
||||
* @param variables must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static LetBuilder define(final ExpressionVariable... variables) {
|
||||
|
||||
Assert.notNull(variables, "Variables must not be null!");
|
||||
|
||||
return new LetBuilder() {
|
||||
|
||||
@Override
|
||||
public Let andApply(final AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new Let(Arrays.asList(variables), expression);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface LetBuilder {
|
||||
|
||||
/**
|
||||
* Define the {@link AggregationExpression} to evaluate.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Let andApply(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(final AggregationOperationContext context) {
|
||||
return toLet(ExposedFields.synthetic(Fields.fields(getVariableNames())), context);
|
||||
}
|
||||
|
||||
private String[] getVariableNames() {
|
||||
|
||||
String[] varNames = new String[this.vars.size()];
|
||||
for (int i = 0; i < this.vars.size(); i++) {
|
||||
varNames[i] = this.vars.get(i).variableName;
|
||||
}
|
||||
|
||||
return varNames;
|
||||
}
|
||||
|
||||
private DBObject toLet(ExposedFields exposedFields, AggregationOperationContext context) {
|
||||
|
||||
DBObject letExpression = new BasicDBObject();
|
||||
DBObject mappedVars = new BasicDBObject();
|
||||
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
|
||||
exposedFields, context);
|
||||
|
||||
for (ExpressionVariable var : this.vars) {
|
||||
mappedVars.putAll(getMappedVariable(var, context));
|
||||
}
|
||||
|
||||
letExpression.put("vars", mappedVars);
|
||||
letExpression.put("in", getMappedIn(operationContext));
|
||||
|
||||
return new BasicDBObject("$let", letExpression);
|
||||
}
|
||||
|
||||
private DBObject getMappedVariable(ExpressionVariable var, AggregationOperationContext context) {
|
||||
|
||||
return new BasicDBObject(var.variableName, var.expression instanceof AggregationExpression
|
||||
? ((AggregationExpression) var.expression).toDbObject(context) : var.expression);
|
||||
}
|
||||
|
||||
private Object getMappedIn(AggregationOperationContext context) {
|
||||
return expression.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class ExpressionVariable {
|
||||
|
||||
private final String variableName;
|
||||
private final Object expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link ExpressionVariable}.
|
||||
*
|
||||
* @param variableName can be {@literal null}.
|
||||
* @param expression can be {@literal null}.
|
||||
*/
|
||||
private ExpressionVariable(String variableName, Object expression) {
|
||||
|
||||
this.variableName = variableName;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ExpressionVariable} with given name.
|
||||
*
|
||||
* @param variableName must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ExpressionVariable newVariable(String variableName) {
|
||||
|
||||
Assert.notNull(variableName, "VariableName must not be null!");
|
||||
return new ExpressionVariable(variableName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ExpressionVariable} with current name and given {@literal expression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public ExpressionVariable forExpression(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new ExpressionVariable(variableName, expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ExpressionVariable} with current name and given {@literal expressionObject}.
|
||||
*
|
||||
* @param expressionObject must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public ExpressionVariable forExpression(DBObject expressionObject) {
|
||||
|
||||
Assert.notNull(expressionObject, "Expression must not be null!");
|
||||
return new ExpressionVariable(variableName, expressionObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
* Copyright 2011-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,7 @@ import java.math.BigInteger;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
|
||||
@@ -46,10 +46,8 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
|
||||
*
|
||||
* @param conversionService
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public AbstractMongoConverter(GenericConversionService conversionService) {
|
||||
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
|
||||
: conversionService;
|
||||
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,15 +75,13 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
|
||||
*/
|
||||
private void initializeConverters() {
|
||||
|
||||
if (!conversionService.canConvert(ObjectId.class, String.class)) {
|
||||
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(String.class, ObjectId.class)) {
|
||||
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
|
||||
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
|
||||
|
||||
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
|
||||
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
|
||||
}
|
||||
|
||||
if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) {
|
||||
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,14 +17,15 @@ package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -38,17 +39,11 @@ import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.JodaTimeConverters;
|
||||
import org.springframework.data.convert.Jsr310Converters;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.ThreeTenBackPortConverters;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.DBObjectToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToURLConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.TermToStringConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.URLToStringConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
|
||||
import org.springframework.data.util.CacheValue;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -72,10 +67,13 @@ public class CustomConversions {
|
||||
private final Set<ConvertiblePair> writingPairs;
|
||||
private final Set<Class<?>> customSimpleTypes;
|
||||
private final SimpleTypeHolder simpleTypeHolder;
|
||||
private final ConcurrentMap<ConvertiblePair, CacheValue> customReadTargetTypes;
|
||||
|
||||
private final List<Object> converters;
|
||||
|
||||
private final Map<ConvertiblePair, CacheValue<Class<?>>> customReadTargetTypes;
|
||||
private final Map<ConvertiblePair, CacheValue<Class<?>>> customWriteTargetTypes;
|
||||
private final Map<Class<?>, CacheValue<Class<?>>> rawWriteTargetTypes;
|
||||
|
||||
/**
|
||||
* Creates an empty {@link CustomConversions} object.
|
||||
*/
|
||||
@@ -95,25 +93,20 @@ public class CustomConversions {
|
||||
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
|
||||
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
|
||||
this.customSimpleTypes = new HashSet<Class<?>>();
|
||||
this.customReadTargetTypes = new ConcurrentHashMap<GenericConverter.ConvertiblePair, CacheValue>();
|
||||
this.customReadTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>();
|
||||
this.customWriteTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>();
|
||||
this.rawWriteTargetTypes = new ConcurrentHashMap<Class<?>, CacheValue<Class<?>>>();
|
||||
|
||||
List<Object> toRegister = new ArrayList<Object>();
|
||||
|
||||
// Add user provided converters to make sure they can override the defaults
|
||||
toRegister.addAll(converters);
|
||||
toRegister.add(CustomToStringConverter.INSTANCE);
|
||||
toRegister.add(BigDecimalToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToBigDecimalConverter.INSTANCE);
|
||||
toRegister.add(BigIntegerToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToBigIntegerConverter.INSTANCE);
|
||||
toRegister.add(URLToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToURLConverter.INSTANCE);
|
||||
toRegister.add(DBObjectToStringConverter.INSTANCE);
|
||||
toRegister.add(TermToStringConverter.INSTANCE);
|
||||
|
||||
toRegister.addAll(MongoConverters.getConvertersToRegister());
|
||||
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
|
||||
toRegister.addAll(GeoConverters.getConvertersToRegister());
|
||||
toRegister.addAll(Jsr310Converters.getConvertersToRegister());
|
||||
toRegister.addAll(ThreeTenBackPortConverters.getConvertersToRegister());
|
||||
|
||||
for (Object c : toRegister) {
|
||||
registerConversion(c);
|
||||
@@ -173,14 +166,15 @@ public class CustomConversions {
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!");
|
||||
throw new IllegalArgumentException(
|
||||
"Given set contains element that is neither Converter nor ConverterFactory!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a conversion for the given converter. Inspects either generics or the {@link ConvertiblePair}s returned
|
||||
* by a {@link GenericConverter}.
|
||||
* Registers a conversion for the given converter. Inspects either generics of {@link Converter} and
|
||||
* {@link ConverterFactory} or the {@link ConvertiblePair}s returned by a {@link GenericConverter}.
|
||||
*
|
||||
* @param converter
|
||||
*/
|
||||
@@ -195,6 +189,10 @@ public class CustomConversions {
|
||||
for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) {
|
||||
register(new ConverterRegistration(pair, isReading, isWriting));
|
||||
}
|
||||
} else if (converter instanceof ConverterFactory) {
|
||||
|
||||
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), ConverterFactory.class);
|
||||
register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting));
|
||||
} else if (converter instanceof Converter) {
|
||||
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
|
||||
register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting));
|
||||
@@ -240,70 +238,103 @@ public class CustomConversions {
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Class<?> getCustomWriteTarget(Class<?> sourceType) {
|
||||
return getCustomWriteTarget(sourceType, null);
|
||||
public Class<?> getCustomWriteTarget(final Class<?> sourceType) {
|
||||
|
||||
return getOrCreateAndCache(sourceType, rawWriteTargetTypes, new Producer() {
|
||||
|
||||
@Override
|
||||
public Class<?> get() {
|
||||
return getCustomTarget(sourceType, null, writingPairs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target type we can write an onject of the given source type to. The returned type might be a subclass
|
||||
* oth the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply return the
|
||||
* first target type matching or {@literal null} if no conversion can be found.
|
||||
* Returns the target type we can readTargetWriteLocl an inject of the given source type to. The returned type might
|
||||
* be a subclass of the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply
|
||||
* return the first target type matching or {@literal null} if no conversion can be found.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @param requestedTargetType
|
||||
* @return
|
||||
*/
|
||||
public Class<?> getCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
public Class<?> getCustomWriteTarget(final Class<?> sourceType, final Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
if (requestedTargetType == null) {
|
||||
return getCustomWriteTarget(sourceType);
|
||||
}
|
||||
|
||||
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
|
||||
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customWriteTargetTypes,
|
||||
new Producer() {
|
||||
|
||||
@Override
|
||||
public Class<?> get() {
|
||||
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to write into a Mongo native type. The returned type might
|
||||
* be a subclass of the given expected type though.
|
||||
* Returns whether we have a custom conversion registered to readTargetWriteLocl into a Mongo native type. The
|
||||
* returned type might be a subclass of the given expected type though.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomWriteTarget(Class<?> sourceType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
return hasCustomWriteTarget(sourceType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to write an object of the given source type into an object
|
||||
* of the given Mongo native target type.
|
||||
* Returns whether we have a custom conversion registered to readTargetWriteLocl an object of the given source type
|
||||
* into an object of the given Mongo native target type.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomWriteTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
return getCustomWriteTarget(sourceType, requestedTargetType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a custom conversion registered to read the given source into the given target type.
|
||||
* Returns whether we have a custom conversion registered to readTargetReadLock the given source into the given target
|
||||
* type.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}
|
||||
* @param requestedTargetType must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public boolean hasCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
Assert.notNull(requestedTargetType);
|
||||
|
||||
return getCustomReadTarget(sourceType, requestedTargetType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the given {@link ConvertiblePair} for ones that have a source compatible type as source. Additionally
|
||||
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
|
||||
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getCustomReadTarget(final Class<?> sourceType, final Class<?> requestedTargetType) {
|
||||
|
||||
if (requestedTargetType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customReadTargetTypes,
|
||||
new Producer() {
|
||||
|
||||
@Override
|
||||
public Class<?> get() {
|
||||
return getCustomTarget(sourceType, requestedTargetType, readingPairs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the given {@link ConvertiblePair}s for ones that have a source compatible type as source. Additionally
|
||||
* checks assignability of the target type if one is given.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}.
|
||||
@@ -312,11 +343,15 @@ public class CustomConversions {
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
|
||||
Iterable<ConvertiblePair> pairs) {
|
||||
Collection<ConvertiblePair> pairs) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
Assert.notNull(pairs);
|
||||
|
||||
if (requestedTargetType != null && pairs.contains(new ConvertiblePair(sourceType, requestedTargetType))) {
|
||||
return requestedTargetType;
|
||||
}
|
||||
|
||||
for (ConvertiblePair typePair : pairs) {
|
||||
if (typePair.getSourceType().isAssignableFrom(sourceType)) {
|
||||
Class<?> targetType = typePair.getTargetType();
|
||||
@@ -330,32 +365,31 @@ public class CustomConversions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the
|
||||
* returned {@link Class} could be an assignable type to the given {@code requestedTargetType}.
|
||||
* Will try to find a value for the given key in the given cache or produce one using the given {@link Producer} and
|
||||
* store it in the cache.
|
||||
*
|
||||
* @param sourceType must not be {@literal null}.
|
||||
* @param requestedTargetType can be {@literal null}.
|
||||
* @param key the key to lookup a potentially existing value, must not be {@literal null}.
|
||||
* @param cache the cache to find the value in, must not be {@literal null}.
|
||||
* @param producer the {@link Producer} to create values to cache, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) {
|
||||
private static <T> Class<?> getOrCreateAndCache(T key, Map<T, CacheValue<Class<?>>> cache, Producer producer) {
|
||||
|
||||
Assert.notNull(sourceType);
|
||||
CacheValue<Class<?>> cacheValue = cache.get(key);
|
||||
|
||||
if (requestedTargetType == null) {
|
||||
return null;
|
||||
if (cacheValue != null) {
|
||||
return cacheValue.getValue();
|
||||
}
|
||||
|
||||
ConvertiblePair lookupKey = new ConvertiblePair(sourceType, requestedTargetType);
|
||||
CacheValue readTargetTypeValue = customReadTargetTypes.get(lookupKey);
|
||||
Class<?> type = producer.get();
|
||||
cache.put(key, CacheValue.<Class<?>> ofNullable(type));
|
||||
|
||||
if (readTargetTypeValue != null) {
|
||||
return readTargetTypeValue.getType();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
readTargetTypeValue = CacheValue.of(getCustomTarget(sourceType, requestedTargetType, readingPairs));
|
||||
CacheValue cacheValue = customReadTargetTypes.putIfAbsent(lookupKey, readTargetTypeValue);
|
||||
private interface Producer {
|
||||
|
||||
return cacheValue != null ? cacheValue.getType() : readTargetTypeValue.getType();
|
||||
Class<?> get();
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
@@ -363,6 +397,10 @@ public class CustomConversions {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes()
|
||||
*/
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
|
||||
ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class);
|
||||
@@ -371,34 +409,12 @@ public class CustomConversions {
|
||||
return new HashSet<ConvertiblePair>(Arrays.asList(localeToString, booleanToString));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return source.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to safely store {@literal null} values in the type cache.
|
||||
*
|
||||
* @author Patryk Wasik
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class CacheValue {
|
||||
|
||||
private static final CacheValue ABSENT = new CacheValue(null);
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
public CacheValue(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
static CacheValue of(Class<?> type) {
|
||||
return type == null ? ABSENT : new CacheValue(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user