Compare commits

..

146 Commits

Author SHA1 Message Date
Mark Paluch
5b502a095f Update reference docs to use correct MongoClient.
Closes #3666
2021-06-22 14:36:49 +02:00
larsw
7552329598 Add closing quote to GeoJson javadoc.
Closes #3677
2021-06-21 13:58:56 +02:00
Mark Paluch
fa4f8bfe9f Updated changelog.
See #3629
2021-05-14 12:36:36 +02:00
Mark Paluch
58fde292d5 Updated changelog.
See #3628
2021-05-14 12:06:42 +02:00
Greg L. Turnquist
e71f8b34be Authenticate with artifactory.
See #3616.
2021-04-22 15:04:24 -05:00
Mark Paluch
152d84a200 Updated changelog.
See #3616
2021-04-14 14:39:59 +02:00
Mark Paluch
ddf77d55ff Updated changelog.
See #3617
2021-04-14 11:43:33 +02:00
Mark Paluch
2acf1bc5d4 After release cleanups.
See #3597
2021-04-14 11:16:05 +02:00
Mark Paluch
26f230036a Prepare next development iteration.
See #3597
2021-04-14 11:16:01 +02:00
Mark Paluch
3ee696dab2 Release version 3.0.9 (Neumann SR9).
See #3597
2021-04-14 11:04:45 +02:00
Mark Paluch
7ee12e9637 Prepare 3.0.9 (Neumann SR9).
See #3597
2021-04-14 11:04:07 +02:00
Mark Paluch
414d716c07 Updated changelog.
See #3597
2021-04-14 11:03:59 +02:00
Mark Paluch
5f9e25cd8a Polishing.
Fix nullability annotations for isEqual(…) parameters. Fix generics. Reformat code.

Add tests.

See #3414
Original pull request: #3615.
2021-04-13 09:44:41 +02:00
Clement Petit
caa2dd4e85 Handle nested Pattern and Document in Criteria.equals(…).
Closes #3414
Original pull request: #3615.
2021-04-13 09:44:41 +02:00
Mark Paluch
28fc1e4823 Polishing.
Use ObjectUtils for empty check.

See #3623
Original pull request: #3625.
2021-04-13 09:09:49 +02:00
Christoph Strobl
d62639d11c Fix NPE in declarative aggregation execution.
This commit fixes an issue where using a simple return type leads to NPE when the actual aggregation result does not contain any values.

Closes: #3623
Original pull request: #3625.
2021-04-13 09:09:49 +02:00
Mark Paluch
b13e47d8ac Updated changelog.
See #3598
2021-03-31 18:30:47 +02:00
Mark Paluch
3743ca56df Updated changelog.
See #3595
2021-03-31 17:26:07 +02:00
Clément Petit
0f974130d1 Return saved entity reference instead of original reference.
Make SimpleReactiveMongoRepository#saveAll(Publisher<S>) return the saved entity references instead of the original references.

Closes #3609
Original pull request: #3611.
2021-03-29 11:03:44 +02:00
Mark Paluch
020da42800 Updated changelog.
See #3558
2021-03-17 11:31:31 +01:00
Mark Paluch
4307b4cd91 Updated changelog.
See #3561
2021-03-17 11:03:42 +01:00
Mark Paluch
af874b635c After release cleanups.
See #3556
2021-03-17 10:33:57 +01:00
Mark Paluch
017e01810d Prepare next development iteration.
See #3556
2021-03-17 10:33:55 +01:00
Mark Paluch
05b46779cb Release version 3.0.8 (Neumann SR8).
See #3556
2021-03-17 10:21:33 +01:00
Mark Paluch
ba56d247c9 Prepare 3.0.8 (Neumann SR8).
See #3556
2021-03-17 10:21:08 +01:00
Mark Paluch
4484dac574 Updated changelog.
See #3556
2021-03-17 10:21:02 +01:00
Mark Paluch
af4b1788fc Polishing.
Move hasValue(…) from DocumentAccessor to BsonUtils. Fix typo in tests.

See: #3590
Original pull request: #3591.
2021-03-15 14:07:35 +01:00
Christoph Strobl
af40f15a36 Fix ShardKey lookup for nested paths.
This commit fixes the lookup of shard key values for nested paths using the dot (.) notation.

Closes: #3590
Original pull request: #3591.
2021-03-15 14:07:34 +01:00
Christoph Strobl
193b7de2d9 Upgrade MongoDB drivers to 4.0.6
Closes #3588
2021-03-15 09:24:19 +01:00
Christoph Strobl
f5204e859f Preserve class keyword as Map key during update mapping.
This commit makes sure to skip the class property ob Object when mapping maps and their keys inside an Update.

Closes #3566
Original pull request: #3577.
2021-03-02 11:37:43 +01:00
Mark Paluch
16818324d8 Polishing.
Reformat code. Reduce method visibility in JUnit 5 tests. Add Nullable annotations to address warnings.

See #3568
Original pull request: #3569.
2021-03-02 11:30:31 +01:00
Brice Vandeputte
a9269c0086 Translate MongoSocketException subclasses to DataAccessResourceFailureException.
Closes #3568
Original pull request: #3569.
2021-03-02 11:30:30 +01:00
Christoph Strobl
d3624b9af4 Remove duplicate JSON Schema section from reference documentation.
Closes: #3573
Original pull request: #3574.
2021-03-01 14:42:38 +01:00
Mark Paluch
3711ed5b50 Polishing.
Simplify assertions.

See #3552.
Original pull request: #3565.
2021-02-22 10:01:49 +01:00
Christoph Strobl
c20feabada Preserve numeric keys that exceed Long range when mapping Updates.
This commit makes sure we preserve map keys no matter what.

Closes #3552.
Original pull request: #3565.
2021-02-22 10:01:48 +01:00
Mark Paluch
f93e40fdee Polishing.
Reformat code. Add since tags.

See #3395
Original pull request: #3554.
2021-02-18 15:09:27 +01:00
Christoph Strobl
dca508f3dc Fix case insensitive derived in queries on String properties.
We now consider the IgnoreCase part of a derived query when used along with In. Strings will be quoted to avoid malicious strings from being handed over to the server as a regular expression to evaluate.

See #3395
Original pull request: #3554.
2021-02-18 15:09:26 +01:00
Christoph Strobl
742448a281 Updated changelog.
See #3560
2021-02-18 11:37:46 +01:00
Christoph Strobl
1a4749e97c Updated changelog.
See #3557
2021-02-18 11:18:29 +01:00
Christoph Strobl
a3cbe829b2 Updated changelog.
See #3537
2021-02-17 14:20:38 +01:00
Christoph Strobl
4ca6f29666 Updated changelog.
See #3536
2021-02-17 13:49:19 +01:00
Christoph Strobl
df65644d59 After release cleanups.
See #3520
2021-02-17 11:32:50 +01:00
Christoph Strobl
05a169c858 Prepare next development iteration.
See #3520
2021-02-17 11:32:48 +01:00
Christoph Strobl
e49119038d Release version 3.0.7 (Neumann SR7).
See #3520
2021-02-17 11:07:42 +01:00
Christoph Strobl
f2df84e8c4 Prepare 3.0.7 (Neumann SR7).
See #3520
2021-02-17 11:07:10 +01:00
Christoph Strobl
1504fd0529 Updated changelog.
See #3520
2021-02-17 11:07:02 +01:00
Christoph Strobl
f63610c925 Updated changelog.
See #3519
2021-02-17 10:58:22 +01:00
Christoph Strobl
06eb52cf13 Updated reference documentation regarding GeoJsonModule.
Original pull request: #3539.
Closes #3517
2021-02-02 14:50:59 +01:00
Christoph Strobl
c9a4a8a370 Fix Criteria chaining for Criteria.alike().
This commit fixes an issue where an Example probe would not be added to the criteria chain.

Closes #3544
Original pull request: #3549.
2021-02-01 09:14:56 +01:00
Christoph Strobl
8d17ddb92c Update QBE Documentation section.
This commit adds a note explaining scenarios suitable for an UntypedExampleMatcher.

Closes: #3474
Original pull request: #3538.
2021-01-26 15:04:51 +01:00
Christoph Strobl
0e96ef2d0c Fix method names in full text query documentation.
Closes #3525
2021-01-20 08:35:43 +01:00
Christoph Strobl
4d8b965176 Updated changelog.
See #3521
2021-01-13 15:49:49 +01:00
Christoph Strobl
37bb390447 Updated changelog.
See #3477
2021-01-13 15:16:25 +01:00
Christoph Strobl
5c79e1e4ef Update issue tracker references after GitHub issues migration.
See: #3529
2021-01-12 13:57:32 +01:00
Mark Paluch
9b41d70f75 Update copyright year to 2021.
Closes #3534
2021-01-12 11:50:53 +01:00
Mark Paluch
b2e52ab28f DATAMONGO-2671 - Polishing.
Fix copyright header.

Original pull request: #897.
2021-01-11 12:16:38 +01:00
Mark Paluch
fed33444e9 DATAMONGO-2671 - Polishing.
Fix copyright header. Add since tags.

Original pull request: #897.
2021-01-11 12:16:38 +01:00
Christoph Strobl
a4ad37703f DATAMONGO-2671 - Fix dateFromParts millisecond field name.
Use millisecond instead of milliseconds field name.

Related to: https://jira.mongodb.org/browse/DOCS-10652
Original pull request: #897.
2021-01-11 12:15:27 +01:00
Greg L. Turnquist
42e95e9600 DATAMONGO-2665 - Use Docker hub credentials for all CI jobs, 2020-12-15 15:24:44 -06:00
Mark Paluch
ed894aa51a DATAMONGO-2653 - Updated changelog. 2020-12-09 16:47:44 +01:00
Mark Paluch
693ff04294 DATAMONGO-2649 - Updated changelog. 2020-12-09 15:33:25 +01:00
Mark Paluch
b018b31e20 DATAMONGO-2647 - After release cleanups. 2020-12-09 12:41:25 +01:00
Mark Paluch
bacb75d098 DATAMONGO-2647 - Prepare next development iteration. 2020-12-09 12:41:18 +01:00
Mark Paluch
ec0fe6f994 DATAMONGO-2647 - Release version 3.0.6 (Neumann SR6). 2020-12-09 11:16:20 +01:00
Mark Paluch
d3d690b908 DATAMONGO-2647 - Prepare 3.0.6 (Neumann SR6). 2020-12-09 11:15:54 +01:00
Mark Paluch
b98b5125f1 DATAMONGO-2647 - Updated changelog. 2020-12-09 11:15:51 +01:00
Mark Paluch
d68bd4b44a DATAMONGO-2646 - Updated changelog. 2020-12-09 09:59:11 +01:00
Mark Paluch
a73ae9a1a5 DATAMONGO-2663 - Document Spring Data to MongoDB compatibility.
Original Pull Request: #895
2020-12-07 14:39:41 +01:00
Mark Paluch
ac3c578e93 DATAMONGO-2661 - Polishing.
Add ticket reference.

Original pull request: #894.
2020-11-26 11:48:07 +01:00
Yoann de Martino
9872f8cb07 DATAMONGO-2661 - Handle nullable types for KPropertyPath.
Original pull request: #894.
2020-11-26 11:47:03 +01:00
Mark Paluch
1b1ab2c495 DATAMONGO-2648 - Updated changelog. 2020-11-11 12:34:41 +01:00
Christoph Strobl
6554274dde DATAMONGO-2644 - ProjectOperation no longer errors on inclusion of default _id field.
Original pull request: #890.
2020-11-10 09:41:09 +01:00
Mark Paluch
34f91acbfe DATAMONGO-2639 - Updated changelog. 2020-10-28 16:28:04 +01:00
Mark Paluch
8e7d508cfe DATAMONGO-2625 - After release cleanups. 2020-10-28 14:51:05 +01:00
Mark Paluch
04dd78cb36 DATAMONGO-2625 - Prepare next development iteration. 2020-10-28 14:51:02 +01:00
Mark Paluch
52c554cfe1 DATAMONGO-2625 - Release version 3.0.5 (Neumann SR5). 2020-10-28 14:34:40 +01:00
Mark Paluch
c1bb8c4ba5 DATAMONGO-2625 - Prepare 3.0.5 (Neumann SR5). 2020-10-28 14:34:15 +01:00
Mark Paluch
ed9a367bb8 DATAMONGO-2625 - Updated changelog. 2020-10-28 14:34:04 +01:00
Mark Paluch
b88ce46239 DATAMONGO-2624 - Updated changelog. 2020-10-28 12:15:06 +01:00
Mark Paluch
42ec94d321 DATAMONGO-2641 - Updated changelog. 2020-10-28 11:32:32 +01:00
Robin Dupret
53f35e185f DATAMONGO-2638 - Fix list item rendering in reference documentation.
Original Pull Request: #885
2020-10-27 13:33:15 +01:00
LiangYong
e08bfd253c DATAMONGO-2638 - Fix aggregation input parameter syntax in reference documentation.
Original Pull Request: #881
2020-10-27 13:33:11 +01:00
Mark Paluch
caaafa275d DATAMONGO-2643 - Adopt to AssertJ API changes. 2020-10-26 10:59:30 +01:00
Christoph Strobl
1f082abc7f DATAMONGO-2626 - Updated changelog. 2020-10-14 14:51:55 +02:00
Mark Paluch
df77fcc19d DATAMONGO-2616 - Polishing.
Reformat code. Merge if-statements.

Original pull request: #889.
2020-10-07 11:35:57 +02:00
Christoph Strobl
007b965673 DATAMONGO-2616 - Short circuit id value assignment in MongoConverter.
Original pull request: #889.
2020-10-07 11:35:57 +02:00
Christoph Strobl
1b9680cece DATAMONGO-2633 - Fix json parsing of nested arrays in ParameterBindingDocumentCodec.
Original pull request: #888.
2020-10-05 15:35:07 +02:00
Mark Paluch
13f1d21919 DATAMONGO-2608 - Updated changelog. 2020-09-16 14:12:10 +02:00
Mark Paluch
4c10bf30bc DATAMONGO-2609 - After release cleanups. 2020-09-16 12:15:45 +02:00
Mark Paluch
8f78d0e0d8 DATAMONGO-2609 - Prepare next development iteration. 2020-09-16 12:15:40 +02:00
Mark Paluch
56115a263c DATAMONGO-2609 - Release version 3.0.4 (Neumann SR4). 2020-09-16 11:43:14 +02:00
Mark Paluch
66b809318a DATAMONGO-2609 - Prepare 3.0.4 (Neumann SR4). 2020-09-16 11:42:46 +02:00
Mark Paluch
bfff60d915 DATAMONGO-2609 - Updated changelog. 2020-09-16 11:42:29 +02:00
Mark Paluch
051e973226 DATAMONGO-2593 - Updated changelog. 2020-09-16 11:20:13 +02:00
Mark Paluch
eb61629f10 DATAMONGO-2592 - Updated changelog. 2020-09-16 10:39:02 +02:00
Christoph Strobl
85783e5354 DATAMONGO-2618 - Fix visibility of ReplaceRootDocumentOperation. 2020-09-14 13:44:36 +02:00
Michal Kurcius
433b012b91 DATAMONGO-2613 - Fix single element ArrayJsonSchemaObject to document mapping.
Now toDocument calls toDocument on items correctly.

Original Pull Request: #883
2020-08-20 09:05:58 +02:00
Mark Paluch
8dca0049ca DATAMONGO-2594 - After release cleanups. 2020-08-12 13:19:58 +02:00
Mark Paluch
635f3b82be DATAMONGO-2594 - Prepare next development iteration. 2020-08-12 13:19:55 +02:00
Mark Paluch
e69c7e1134 DATAMONGO-2594 - Release version 3.0.3 (Neumann SR3). 2020-08-12 13:07:46 +02:00
Mark Paluch
1f94e74b75 DATAMONGO-2594 - Prepare 3.0.3 (Neumann SR3). 2020-08-12 13:07:20 +02:00
Mark Paluch
98858e0f5f DATAMONGO-2594 - Updated changelog. 2020-08-12 13:07:08 +02:00
Mark Paluch
90f311de51 DATAMONGO-2579 - Updated changelog. 2020-08-12 12:01:26 +02:00
Mark Paluch
7f7015fd86 DATAMONGO-2601 - Suppress results for suspended query methods returning kotlin.Unit.
We now discard results for suspended query methods if the return type is kotlin.Unit.

Related ticket: DATACMNS-1779
2020-07-31 11:43:59 +02:00
Mark Paluch
ab9c5d73a0 DATAMONGO-2599 - Eagerly consider enum types as simple types.
MongoSimpleTypes now eagerly checks if a type is a simple one to avoid PersistentEntity registration for ChronoUnit.
2020-07-30 16:24:32 +02:00
Mark Paluch
13caa162db DATAMONGO-2598 - Polishing.
Original pull request: #872.
2020-07-28 15:22:02 +02:00
Jay Bryant
d5d620d777 DATAMONGO-2598 - Wording changes.
Removed the language of oppression and violence
and replaced it with more neutral language.

Note that problematic words in the code have
to remain in the docs until the code changes.

Original pull request: #872.
2020-07-28 15:22:01 +02:00
Mark Paluch
21d50f2a72 DATAMONGO-2568 - After release cleanups. 2020-07-22 10:37:08 +02:00
Mark Paluch
4912d62be6 DATAMONGO-2568 - Prepare next development iteration. 2020-07-22 10:37:05 +02:00
Mark Paluch
2bf6f226d6 DATAMONGO-2568 - Release version 3.0.2 (Neumann SR2). 2020-07-22 10:21:08 +02:00
Mark Paluch
1a6f7e371a DATAMONGO-2568 - Prepare 3.0.2 (Neumann SR2). 2020-07-22 10:20:44 +02:00
Mark Paluch
2ef5d795ce DATAMONGO-2568 - Updated changelog. 2020-07-22 10:20:31 +02:00
Mark Paluch
e3d2f16202 DATAMONGO-2567 - Updated changelog. 2020-07-22 10:08:49 +02:00
Mark Paluch
c44232ff39 DATAMONGO-2566 - Updated changelog. 2020-07-22 09:44:34 +02:00
Mark Paluch
385e911708 DATAMONGO-2589 - Upgrade to MongoDB Driver 4.0.5. 2020-07-17 10:49:56 +02:00
Mark Paluch
171d8b2b1e DATAMONGO-2536 - Polishing.
Encapsulate skipResults in AggregationOptions. Reformat code. Add override Javadoc.

Original pull request: #876.
2020-07-16 09:42:53 +02:00
Christoph Strobl
7ac7eefad6 DATAMONGO-2536 - Add option to skip reading aggregation result.
Introduce dedicated AggregationPipeline to encapsulate pipeline stages.

Original pull request: #876.
2020-07-16 09:42:53 +02:00
Mark Paluch
7a39e94e4b DATAMONGO-2571 - Polishing.
Reduce test method visibility for JUnit 5.

Original pull request: #873.
2020-07-15 15:33:48 +02:00
Christoph Strobl
25733664b3 DATAMONGO-2571 - Fix regular expression parameter binding for String-based queries.
Original pull request: #873.
2020-07-15 15:33:48 +02:00
Mark Paluch
81da10f499 DATAMONGO-2490 - Polishing.
Remove unnecessary code. Reuse session-associated collection when logging to avoid unqualified calls to MongoDbFactory.getMongoDatabase(). Create collection before transaction in test for compatibility with older MongoDB servers.

Original pull request: #875.
2020-07-15 15:14:08 +02:00
Christoph Strobl
bde114ed19 DATAMONGO-2490 - Fix dbref fetching during ongoing transaction.
Original pull request: #875.
2020-07-15 15:14:08 +02:00
Mark Paluch
a2403f58ec DATAMONGO-2544 - Updated changelog. 2020-06-25 12:00:24 +02:00
Mark Paluch
875b8eda9f DATAMONGO-2570 - Polishing.
Add assertions. Use method references where possible.

Original pull request: #871.
2020-06-22 10:40:04 +02:00
Mehran Behnam
fc0a021937 DATAMONGO-2570 - Change type of count variable to primitive.
Avoiding unintentional unboxing.

Original pull request: #871.
2020-06-22 10:40:04 +02:00
Mark Paluch
a6aa174ff5 DATAMONGO-2572 - Remove usage of Oppressive Language.
Replaced blacklist with denylist and introduce meta keyword SECONDARY_READS as we no longer use MongoDB API with the initial replication concept.

Original Pull Request: #870
2020-06-17 13:32:40 +02:00
Mark Paluch
8b36617752 DATAMONGO-2543 - After release cleanups. 2020-06-10 14:29:28 +02:00
Mark Paluch
56e8799c22 DATAMONGO-2543 - Prepare next development iteration. 2020-06-10 14:29:25 +02:00
Mark Paluch
88e60070d6 DATAMONGO-2543 - Release version 3.0.1 (Neumann SR1). 2020-06-10 14:02:26 +02:00
Mark Paluch
6c7039580f DATAMONGO-2543 - Prepare 3.0.1 (Neumann SR1). 2020-06-10 14:01:58 +02:00
Mark Paluch
fba003f215 DATAMONGO-2543 - Updated changelog. 2020-06-10 14:01:47 +02:00
Mark Paluch
1a3239554c DATAMONGO-2533 - Updated changelog. 2020-06-10 12:30:00 +02:00
Mark Paluch
37b541931d DATAMONGO-2532 - Updated changelog. 2020-06-10 11:48:56 +02:00
Mark Paluch
9038280f68 DATAMONGO-2565 - Polishing.
Add unit test to verify behavior. Cleanup code.

Original pull request: #869.
2020-06-10 10:15:12 +02:00
BraveLeeLee
75935a2bdb DATAMONGO-2565 - Evaluate correct expression when obtaining collation from MongoPersistentEntity.
Original pull request: #869.
2020-06-10 10:14:34 +02:00
Mark Paluch
d9ca3d7eb3 DATAMONGO-2562 - Polishing.
Fix typo in exception message.
2020-06-09 11:18:33 +02:00
Mark Paluch
04e77ad5ab DATAMONGO-2562 - Fix return type detection for suspended Kotlin methods.
See DATACMNS-1738 for further reference.
2020-06-09 11:18:33 +02:00
Mark Paluch
0ab39a17a7 DATAMONGO-2560 - Upgrade MongoDB drivers to 4.0.4. 2020-06-08 15:59:02 +02:00
Mark Paluch
49a6f13797 DATAMONGO-2542 - Polishing.
Fix nullable annotation.

Original pull request: #863.
2020-05-26 10:32:39 +02:00
Christoph Strobl
b0fd6f691b DATAMONGO-2542 - Shortcut PersistentPropertyPath resolution during query mapping.
By shortcutting the path resolution we avoid checking keywords like $in against a potential path expression.

Original pull request: #863.
2020-05-26 10:32:39 +02:00
Mark Paluch
b5778772d9 DATAMONGO-2545 - Polishing.
Fix warnings and typos.

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
0f55fb305d DATAMONGO-2545 - Fix full Query Document binding resulting from SpEL.
We reenabled annotated queries using a SpEL expression resulting in the actual query document.

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
5ae7547465 DATAMONGO-2545 - Fix regression in String query SpEL parameter binding.
We reenabled parameter binding within SpEL using query parameter placeholders ?0, ?1,... instead of their array index [0],[1],...

Original pull request: #864.
2020-05-26 10:14:27 +02:00
Christoph Strobl
cf4e04a30e DATAMONGO-2547 - Use target class ClassLoader instead of default CL when creating proxy instances.
Original pull request: #865.
2020-05-26 08:56:09 +02:00
Mark Paluch
89c1dc77d9 DATAMONGO-2553 - Fix reference documentation links.
Remove links to removed documentations sections.
2020-05-19 11:36:44 +02:00
Mark Paluch
a2c842b59b DATAMONGO-2534 - After release cleanups. 2020-05-12 12:56:12 +02:00
Mark Paluch
0cd0be9478 DATAMONGO-2534 - Prepare next development iteration. 2020-05-12 12:40:53 +02:00
226 changed files with 2959 additions and 10904 deletions

View File

@@ -6,6 +6,7 @@ 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. Dont 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).

View File

@@ -1,47 +0,0 @@
# GitHub Actions to automate GitHub issues for Spring Data Project Management
name: Spring Data GitHub Issues
on:
issues:
types: [opened, edited, reopened]
issue_comment:
types: [created]
pull_request_target:
types: [opened, edited, reopened]
jobs:
Inbox:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null
steps:
- name: Create or Update Issue Card
uses: peter-evans/create-or-update-project-card@v1.1.2
with:
project-name: 'Spring Data'
column-name: 'Inbox'
project-location: 'spring-projects'
token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Pull-Request:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request != null
steps:
- name: Create or Update Pull Request Card
uses: peter-evans/create-or-update-project-card@v1.1.2
with:
project-name: 'Spring Data'
column-name: 'Review pending'
project-location: 'spring-projects'
issue-number: ${{ github.event.pull_request.number }}
token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Feedback-Provided:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && github.event_name == 'issue_comment' && github.event.action == 'created' && github.actor != 'spring-projects-issues' && github.event.pull_request == null && github.event.issue.state == 'open' && contains(toJSON(github.event.issue.labels), 'waiting-for-feedback')
steps:
- name: Update Project Card
uses: peter-evans/create-or-update-project-card@v1.1.2
with:
project-name: 'Spring Data'
column-name: 'Feedback provided'
project-location: 'spring-projects'
token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}

27
CODE_OF_CONDUCT.adoc Normal file
View 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 https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].

44
Jenkinsfile vendored
View File

@@ -3,7 +3,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/2.3.x", threshold: hudson.model.Result.SUCCESS)
}
options {
@@ -46,16 +46,16 @@ pipeline {
}
}
}
stage('Publish JDK 15 + MongoDB 4.2') {
stage('Publish JDK 14 + MongoDB 4.2') {
when {
changeset "ci/openjdk15-mongodb-4.2/**"
changeset "ci/openjdk14-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk15-with-mongodb-4.2.0", "ci/openjdk15-mongodb-4.2/")
def image = docker.build("springci/spring-data-openjdk14-with-mongodb-4.2.0", "ci/openjdk14-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
@@ -68,7 +68,7 @@ pipeline {
stage("test: baseline (jdk8)") {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -76,6 +76,9 @@ pipeline {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
@@ -85,7 +88,7 @@ pipeline {
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
@@ -94,8 +97,8 @@ pipeline {
stage("Test other configurations") {
when {
allOf {
branch 'master'
anyOf {
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -105,6 +108,9 @@ pipeline {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
@@ -114,7 +120,7 @@ pipeline {
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
@@ -126,6 +132,9 @@ pipeline {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
@@ -135,18 +144,21 @@ pipeline {
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
}
}
stage("test: baseline (jdk15)") {
stage("test: baseline (jdk14)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
@@ -156,7 +168,7 @@ pipeline {
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
@@ -168,7 +180,7 @@ pipeline {
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -185,7 +197,7 @@ pipeline {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
@@ -201,7 +213,7 @@ pipeline {
stage('Publish documentation') {
when {
branch 'master'
branch '3.0.x'
}
agent {
label 'data'
@@ -216,7 +228,7 @@ pipeline {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' +
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +

202
LICENSE.txt Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -10,7 +10,7 @@ Key functional areas of Spring Data MongoDB are a POJO centric model for interac
== Code of Conduct
This project is governed by the https://github.com/spring-projects/.github/blob/e3cc2ff230d8f1dca06535aa6b5a4a23815861d4/CODE_OF_CONDUCT.md[Spring Code of Conduct]. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
This project is governed by the link:CODE_OF_CONDUCT.adoc[Spring Code of Conduct]. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
== Getting Started

View File

@@ -1,4 +1,4 @@
FROM adoptopenjdk/openjdk15:latest
FROM adoptopenjdk/openjdk14:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive

17
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-RC1</version>
<version>3.0.10.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.5.0-RC1</version>
<version>2.3.10.BUILD-SNAPSHOT</version>
</parent>
<modules>
@@ -26,8 +26,8 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.5.0-RC1</springdata.commons>
<mongo>4.2.2</mongo>
<springdata.commons>2.3.10.BUILD-SNAPSHOT</springdata.commons>
<mongo>4.0.6</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -134,8 +134,8 @@
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-libs-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>
@@ -158,11 +158,6 @@
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</pluginRepository>
<pluginRepository>
<id>bintray-plugins</id>
<name>bintray-plugins</name>
<url>https://jcenter.bintray.com</url>
</pluginRepository>
</pluginRepositories>
</project>

29
settings.xml Normal file
View File

@@ -0,0 +1,29 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>spring-plugins-release</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-snapshot</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-milestone</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-release</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
</servers>
</settings>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-RC1</version>
<version>3.0.10.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-RC1</version>
<version>3.0.10.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-RC1</version>
<version>3.0.10.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -136,13 +136,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava3}</version>
<optional>true</optional>
</dependency>
<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
<dependency>
@@ -199,14 +192,7 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b11</version>
<version>5.2.4.Final</version>
<scope>test</scope>
</dependency>

View File

@@ -1,152 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
import java.util.Arrays;
import org.bson.Document;
import org.bson.codecs.DocumentCodec;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* A {@link MongoExpression} using the {@link ParameterBindingDocumentCodec} for parsing a raw ({@literal json})
* expression. The expression will be wrapped within <code>{ ... }</code> if necessary. The actual parsing and parameter
* binding of placeholders like {@code ?0} is delayed upon first call on the the target {@link Document} via
* {@link #toDocument()}.
* <p />
*
* <pre class="code">
* $toUpper : $name -> { '$toUpper' : '$name' }
*
* { '$toUpper' : '$name' } -> { '$toUpper' : '$name' }
*
* { '$toUpper' : '?0' }, "$name" -> { '$toUpper' : '$name' }
* </pre>
*
* Some types might require a special {@link org.bson.codecs.Codec}. If so, make sure to provide a {@link CodecRegistry}
* containing the required {@link org.bson.codecs.Codec codec} via {@link #withCodecRegistry(CodecRegistry)}.
*
* @author Christoph Strobl
* @since 3.2
*/
public class BindableMongoExpression implements MongoExpression {
private final String expressionString;
private final @Nullable CodecRegistryProvider codecRegistryProvider;
private final @Nullable Object[] args;
private final Lazy<Document> target;
/**
* Create a new instance of {@link BindableMongoExpression}.
*
* @param expression must not be {@literal null}.
* @param args can be {@literal null}.
*/
public BindableMongoExpression(String expression, @Nullable Object[] args) {
this(expression, null, args);
}
/**
* Create a new instance of {@link BindableMongoExpression}.
*
* @param expression must not be {@literal null}.
* @param codecRegistryProvider can be {@literal null}.
* @param args can be {@literal null}.
*/
public BindableMongoExpression(String expression, @Nullable CodecRegistryProvider codecRegistryProvider,
@Nullable Object[] args) {
this.expressionString = expression;
this.codecRegistryProvider = codecRegistryProvider;
this.args = args;
this.target = Lazy.of(this::parse);
}
/**
* Provide the {@link CodecRegistry} used to convert expressions.
*
* @param codecRegistry must not be {@literal null}.
* @return new instance of {@link BindableMongoExpression}.
*/
public BindableMongoExpression withCodecRegistry(CodecRegistry codecRegistry) {
return new BindableMongoExpression(expressionString, () -> codecRegistry, args);
}
/**
* Provide the arguments to bind to the placeholders via their index.
*
* @param args must not be {@literal null}.
* @return new instance of {@link BindableMongoExpression}.
*/
public BindableMongoExpression bind(Object... args) {
return new BindableMongoExpression(expressionString, codecRegistryProvider, args);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoExpression#toDocument()
*/
@Override
public Document toDocument() {
return target.get();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BindableMongoExpression{" + "expressionString='" + expressionString + '\'' + ", args="
+ Arrays.toString(args) + '}';
}
private Document parse() {
String expression = wrapJsonIfNecessary(expressionString);
if (ObjectUtils.isEmpty(args)) {
if (codecRegistryProvider == null) {
return Document.parse(expression);
}
return Document.parse(expression, codecRegistryProvider.getCodecFor(Document.class)
.orElseGet(() -> new DocumentCodec(codecRegistryProvider.getCodecRegistry())));
}
ParameterBindingDocumentCodec codec = codecRegistryProvider == null ? new ParameterBindingDocumentCodec()
: new ParameterBindingDocumentCodec(codecRegistryProvider.getCodecRegistry());
return codec.decode(expression, args);
}
private static String wrapJsonIfNecessary(String json) {
if (StringUtils.hasText(json) && (json.startsWith("{") && json.endsWith("}"))) {
return json;
}
return "{" + json + "}";
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
/**
* Wrapper object for MongoDB expressions like {@code $toUpper : $name} that manifest as {@link org.bson.Document} when
* passed on to the driver.
* <p />
* A set of predefined {@link MongoExpression expressions}, including a
* {@link org.springframework.data.mongodb.core.aggregation.AggregationSpELExpression SpEL based variant} for method
* like expressions (eg. {@code toUpper(name)}) are available via the
* {@link org.springframework.data.mongodb.core.aggregation Aggregation API}.
*
* @author Christoph Strobl
* @since 3.2
* @see org.springframework.data.mongodb.core.aggregation.ArithmeticOperators
* @see org.springframework.data.mongodb.core.aggregation.ArrayOperators
* @see org.springframework.data.mongodb.core.aggregation.ComparisonOperators
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperators
* @see org.springframework.data.mongodb.core.aggregation.ConvertOperators
* @see org.springframework.data.mongodb.core.aggregation.DateOperators
* @see org.springframework.data.mongodb.core.aggregation.ObjectOperators
* @see org.springframework.data.mongodb.core.aggregation.SetOperators
* @see org.springframework.data.mongodb.core.aggregation.StringOperators
*/
@FunctionalInterface
public interface MongoExpression {
/**
* Create a new {@link MongoExpression} from plain {@link String} (eg. {@code $toUpper : $name}). <br />
* The given expression will be wrapped with <code>{ ... }</code> to match an actual MongoDB {@link org.bson.Document}
* if necessary.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link MongoExpression}.
*/
static MongoExpression create(String expression) {
return new BindableMongoExpression(expression, null);
}
/**
* Create a new {@link MongoExpression} from plain {@link String} containing placeholders (eg. {@code $toUpper : ?0})
* that will be resolved on first call of {@link #toDocument()}. <br />
* The given expression will be wrapped with <code>{ ... }</code> to match an actual MongoDB {@link org.bson.Document}
* if necessary.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link MongoExpression}.
*/
static MongoExpression create(String expression, Object... args) {
return new BindableMongoExpression(expression, args);
}
/**
* Obtain the native {@link org.bson.Document} representation.
*
* @return never {@literal null}.
*/
org.bson.Document toDocument();
}

View File

@@ -61,8 +61,8 @@ public @interface EnableMongoAuditing {
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the timestamp to be used for setting
* creation and modification dates.
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
* used for setting creation and modification dates.
*
* @return empty {@link String} by default.
*/

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.domain.ReactiveAuditorAware;
/**
* Annotation to enable auditing in MongoDB using reactive infrastructure via annotation configuration.
*
* @author Mark Paluch
* @since 3.1
*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ReactiveMongoAuditingRegistrar.class)
public @interface EnableReactiveMongoAuditing {
/**
* Configures the {@link ReactiveAuditorAware} bean to be used to lookup the current principal.
*
* @return empty {@link String} by default.
*/
String auditorAwareRef() default "";
/**
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
*
* @return {@literal true} by default.
*/
boolean setDates() default true;
/**
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
*
* @return {@literal true} by default.
*/
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the timestamp to be used for setting
* creation and modification dates.
*
* @return empty {@link String} by default.
*/
String dateTimeProviderRef() default "";
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.config;
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;
@@ -27,8 +28,14 @@ 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.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.AuditingEntityCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMongoAuditing} annotation.
@@ -39,6 +46,9 @@ import org.springframework.util.Assert;
*/
class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
MongoAuditingRegistrar.class.getClassLoader());
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
@@ -81,7 +91,7 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContextLookup.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
@@ -106,6 +116,68 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);
if (PROJECT_REACTOR_AVAILABLE) {
registerReactiveAuditingEntityCallback(registry, auditingHandlerDefinition.getSource());
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry, Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(source);
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
}
/**
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingMongoConverter} bean available in
* the application context.
*
* @author Oliver Gierke
*/
static class MongoMappingContextLookup
implements FactoryBean<MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>> {
private final MappingMongoConverter converter;
/**
* Creates a new {@link MongoMappingContextLookup} for the given {@link MappingMongoConverter}.
*
* @param converter must not be {@literal null}.
*/
public MongoMappingContextLookup(MappingMongoConverter converter) {
this.converter = converter;
}
/*
* (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;
}
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
@@ -139,7 +140,8 @@ public abstract class MongoConfigurationSupport {
}
/**
* Scans the given base package for entities, i.e. MongoDB specific types annotated with {@link Document}.
* 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
@@ -159,6 +161,7 @@ public abstract class MongoConfigurationSupport {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
/**
* Simple helper to be able to wire the {@link PersistentEntities} from a {@link MappingMongoConverter} bean available
* in the application context.
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Christoph Strobl
* @since 3.1
*/
class PersistentEntitiesFactoryBean implements FactoryBean<PersistentEntities> {
private final MappingMongoConverter converter;
/**
* Creates a new {@link PersistentEntitiesFactoryBean} for the given {@link MappingMongoConverter}.
*
* @param converter must not be {@literal null}.
*/
public PersistentEntitiesFactoryBean(MappingMongoConverter converter) {
this.converter = converter;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public PersistentEntities getObject() {
return PersistentEntities.of(converter.getMappingContext());
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return PersistentEntities.class;
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.lang.annotation.Annotation;
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.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
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.event.ReactiveAuditingEntityCallback;
import org.springframework.util.Assert;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableReactiveMongoAuditing} annotation.
*
* @author Mark Paluch
* @since 3.1
*/
class ReactiveMongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableReactiveMongoAuditing.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
*/
@Override
protected String getAuditingHandlerBeanName() {
return "reactiveMongoAuditingHandler";
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(auditingHandlerDefinition.getSource());
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AllArgsConstructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -27,9 +29,7 @@ import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.DomainTypeMapping;
import org.springframework.data.mongodb.core.aggregation.CountOperation;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.QueryMapper;
@@ -37,7 +37,6 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -50,50 +49,34 @@ import org.springframework.util.ObjectUtils;
* @author Mark Paluch
* @since 2.1
*/
@AllArgsConstructor
class AggregationUtil {
QueryMapper queryMapper;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
Lazy<AggregationOperationContext> untypedMappingContext;
AggregationUtil(QueryMapper queryMapper,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
/**
* Prepare the {@link AggregationOperationContext} for a given aggregation by either returning the context itself it
* is not {@literal null}, create a {@link TypeBasedAggregationOperationContext} if the aggregation contains type
* information (is a {@link TypedAggregation}) or use the {@link Aggregation#DEFAULT_CONTEXT}.
*
* @param aggregation must not be {@literal null}.
* @param context can be {@literal null}.
* @return the root {@link AggregationOperationContext} to use.
*/
AggregationOperationContext prepareAggregationContext(Aggregation aggregation,
@Nullable AggregationOperationContext context) {
this.queryMapper = queryMapper;
this.mappingContext = mappingContext;
this.untypedMappingContext = Lazy
.of(() -> new RelaxedTypeBasedAggregationOperationContext(Object.class, mappingContext, queryMapper));
}
AggregationOperationContext createAggregationContext(Aggregation aggregation, @Nullable Class<?> inputType) {
DomainTypeMapping domainTypeMapping = aggregation.getOptions().getDomainTypeMapping();
if (domainTypeMapping == DomainTypeMapping.NONE) {
return Aggregation.DEFAULT_CONTEXT;
if (context != null) {
return context;
}
if (!(aggregation instanceof TypedAggregation)) {
if(inputType == null) {
return untypedMappingContext.get();
}
if (domainTypeMapping == DomainTypeMapping.STRICT
&& !aggregation.getPipeline().containsUnionWith()) {
return new TypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
}
return new RelaxedTypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
if (aggregation instanceof TypedAggregation) {
return new TypeBasedAggregationOperationContext(((TypedAggregation) aggregation).getInputType(), mappingContext,
queryMapper);
}
inputType = ((TypedAggregation<?>) aggregation).getInputType();
if (domainTypeMapping == DomainTypeMapping.STRICT
&& !aggregation.getPipeline().containsUnionWith()) {
return new TypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
}
return new RelaxedTypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
return Aggregation.DEFAULT_CONTEXT;
}
/**
@@ -105,7 +88,7 @@ class AggregationUtil {
*/
List<Document> createPipeline(Aggregation aggregation, AggregationOperationContext context) {
if (ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
if (!ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
return aggregation.toPipeline(context);
}
@@ -132,6 +115,53 @@ class AggregationUtil {
return command;
}
/**
* Create a {@code $count} aggregation for {@link Query} and optionally a {@link Class entity class}.
*
* @param query must not be {@literal null}.
* @param entityClass can be {@literal null} if the {@link Query} object is empty.
* @return the {@link Aggregation} pipeline definition to run a {@code $count} aggregation.
*/
Aggregation createCountAggregation(Query query, @Nullable Class<?> entityClass) {
List<AggregationOperation> pipeline = computeCountAggregationPipeline(query, entityClass);
Aggregation aggregation = entityClass != null ? Aggregation.newAggregation(entityClass, pipeline)
: Aggregation.newAggregation(pipeline);
aggregation.withOptions(AggregationOptions.builder().collation(query.getCollation().orElse(null)).build());
return aggregation;
}
private List<AggregationOperation> computeCountAggregationPipeline(Query query, @Nullable Class<?> entityType) {
CountOperation count = Aggregation.count().as("totalEntityCount");
if (query.getQueryObject().isEmpty()) {
return Collections.singletonList(count);
}
Assert.notNull(entityType, "Entity type must not be null!");
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(),
mappingContext.getPersistentEntity(entityType));
CriteriaDefinition criteria = new CriteriaDefinition() {
@Override
public Document getCriteriaObject() {
return mappedQuery;
}
@Nullable
@Override
public String getKey() {
return null;
}
};
return Arrays.asList(Aggregation.match(criteria), count);
}
private List<Document> mapAggregationPipeline(List<Document> pipeline) {
return pipeline.stream().map(val -> queryMapper.getMappedObject(val, Optional.empty()))

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.EqualsAndHashCode;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@@ -25,7 +27,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.messaging.Message;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.client.model.changestream.OperationType;
@@ -38,6 +39,7 @@ import com.mongodb.client.model.changestream.OperationType;
* @author Mark Paluch
* @since 2.1
*/
@EqualsAndHashCode
public class ChangeStreamEvent<T> {
@SuppressWarnings("rawtypes") //
@@ -185,8 +187,8 @@ public class ChangeStreamEvent<T> {
return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
}
throw new IllegalArgumentException(
String.format("No converter found capable of converting %s to %s", fullDocument.getClass(), targetType));
throw new IllegalArgumentException(String.format("No converter found capable of converting %s to %s",
fullDocument.getClass(), targetType));
}
/*
@@ -197,27 +199,4 @@ public class ChangeStreamEvent<T> {
public String toString() {
return "ChangeStreamEvent {" + "raw=" + raw + ", targetType=" + targetType + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ChangeStreamEvent<?> that = (ChangeStreamEvent<?>) o;
if (!ObjectUtils.nullSafeEquals(this.raw, that.raw)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.targetType, that.targetType);
}
@Override
public int hashCode() {
int result = raw != null ? raw.hashCode() : 0;
result = 31 * result + ObjectUtils.nullSafeHashCode(targetType);
return result;
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.EqualsAndHashCode;
import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;
@@ -23,6 +25,7 @@ import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
@@ -42,6 +45,7 @@ import com.mongodb.client.model.changestream.FullDocument;
* @author Mark Paluch
* @since 2.1
*/
@EqualsAndHashCode
public class ChangeStreamOptions {
private @Nullable Object filter;
@@ -152,44 +156,6 @@ public class ChangeStreamOptions {
+ ObjectUtils.nullSafeClassName(timestamp));
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ChangeStreamOptions that = (ChangeStreamOptions) o;
if (!ObjectUtils.nullSafeEquals(this.filter, that.filter)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.resumeToken, that.resumeToken)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.fullDocumentLookup, that.fullDocumentLookup)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.collation, that.collation)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.resumeTimestamp, that.resumeTimestamp)) {
return false;
}
return resume == that.resume;
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(filter);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeToken);
result = 31 * result + ObjectUtils.nullSafeHashCode(fullDocumentLookup);
result = 31 * result + ObjectUtils.nullSafeHashCode(collation);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeTimestamp);
result = 31 * result + ObjectUtils.nullSafeHashCode(resume);
return result;
}
/**
* @author Christoph Strobl
* @since 2.2

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
import org.springframework.data.mongodb.core.query.Collation;
@@ -310,6 +312,7 @@ public class CollectionOptions {
* @author Andreas Zink
* @since 2.1
*/
@RequiredArgsConstructor
public static class ValidationOptions {
private static final ValidationOptions NONE = new ValidationOptions(null, null, null);
@@ -318,13 +321,6 @@ public class CollectionOptions {
private final @Nullable ValidationLevel validationLevel;
private final @Nullable ValidationAction validationAction;
public ValidationOptions(Validator validator, ValidationLevel validationLevel, ValidationAction validationAction) {
this.validator = validator;
this.validationLevel = validationLevel;
this.validationAction = validationAction;
}
/**
* Create an empty {@link ValidationOptions}.
*

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.Value;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -24,9 +27,8 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
@@ -45,9 +47,7 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.util.Pair;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoCollection;
@@ -64,7 +64,6 @@ import com.mongodb.client.model.*;
* @author Jens Schauder
* @author Michail Nikolaev
* @author Roman Puchkovskiy
* @author Jacob Botuck
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -74,6 +73,7 @@ class DefaultBulkOperations implements BulkOperations {
private final BulkOperationContext bulkOperationContext;
private final List<SourceAwareWriteModelHolder> models = new ArrayList<>();
private PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern defaultWriteConcern;
private BulkWriteOptions bulkOptions;
@@ -97,9 +97,19 @@ class DefaultBulkOperations implements BulkOperations {
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.bulkOperationContext = bulkOperationContext;
this.exceptionTranslator = new MongoExceptionTranslator();
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
}
/**
* Configures the {@link PersistenceExceptionTranslator} to be used. Defaults to {@link MongoExceptionTranslator}.
*
* @param exceptionTranslator can be {@literal null}.
*/
public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator == null ? new MongoExceptionTranslator() : exceptionTranslator;
}
/**
* Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}.
*
@@ -306,26 +316,11 @@ class DefaultBulkOperations implements BulkOperations {
collection = collection.withWriteConcern(defaultWriteConcern);
}
try {
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
} catch (RuntimeException ex) {
if (ex instanceof MongoBulkWriteException) {
MongoBulkWriteException mongoBulkWriteException = (MongoBulkWriteException) ex;
if (mongoBulkWriteException.getWriteConcernError() != null) {
throw new DataIntegrityViolationException(ex.getMessage(), ex);
}
throw new BulkOperationException(ex.getMessage(), mongoBulkWriteException);
}
throw ex;
}
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
}
private WriteModel<Document> extractAndMapWriteModel(SourceAwareWriteModelHolder it) {
@@ -552,93 +547,15 @@ class DefaultBulkOperations implements BulkOperations {
* @author Christoph Strobl
* @since 2.0
*/
static final class BulkOperationContext {
@Value
static class BulkOperationContext {
private final BulkMode bulkMode;
private final Optional<? extends MongoPersistentEntity<?>> entity;
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final ApplicationEventPublisher eventPublisher;
private final EntityCallbacks entityCallbacks;
BulkOperationContext(BulkOperations.BulkMode bulkMode, Optional<? extends MongoPersistentEntity<?>> entity,
QueryMapper queryMapper, UpdateMapper updateMapper, ApplicationEventPublisher eventPublisher,
EntityCallbacks entityCallbacks) {
this.bulkMode = bulkMode;
this.entity = entity;
this.queryMapper = queryMapper;
this.updateMapper = updateMapper;
this.eventPublisher = eventPublisher;
this.entityCallbacks = entityCallbacks;
}
public BulkMode getBulkMode() {
return this.bulkMode;
}
public Optional<? extends MongoPersistentEntity<?>> getEntity() {
return this.entity;
}
public QueryMapper getQueryMapper() {
return this.queryMapper;
}
public UpdateMapper getUpdateMapper() {
return this.updateMapper;
}
public ApplicationEventPublisher getEventPublisher() {
return this.eventPublisher;
}
public EntityCallbacks getEntityCallbacks() {
return this.entityCallbacks;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BulkOperationContext that = (BulkOperationContext) o;
if (bulkMode != that.bulkMode)
return false;
if (!ObjectUtils.nullSafeEquals(this.entity, that.entity)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.queryMapper, that.queryMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.updateMapper, that.updateMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.eventPublisher, that.eventPublisher)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.entityCallbacks, that.entityCallbacks);
}
@Override
public int hashCode() {
int result = bulkMode != null ? bulkMode.hashCode() : 0;
result = 31 * result + ObjectUtils.nullSafeHashCode(entity);
result = 31 * result + ObjectUtils.nullSafeHashCode(queryMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(updateMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(eventPublisher);
result = 31 * result + ObjectUtils.nullSafeHashCode(entityCallbacks);
return result;
}
public String toString() {
return "DefaultBulkOperations.BulkOperationContext(bulkMode=" + this.getBulkMode() + ", entity="
+ this.getEntity() + ", queryMapper=" + this.getQueryMapper() + ", updateMapper=" + this.getUpdateMapper()
+ ", eventPublisher=" + this.getEventPublisher() + ", entityCallbacks=" + this.getEntityCallbacks() + ")";
}
@NonNull BulkMode bulkMode;
@NonNull Optional<? extends MongoPersistentEntity<?>> entity;
@NonNull QueryMapper queryMapper;
@NonNull UpdateMapper updateMapper;
ApplicationEventPublisher eventPublisher;
EntityCallbacks entityCallbacks;
}
/**
@@ -647,50 +564,10 @@ class DefaultBulkOperations implements BulkOperations {
* @since 2.2
* @author Christoph Strobl
*/
private static final class SourceAwareWriteModelHolder {
@Value
private static class SourceAwareWriteModelHolder {
private final Object source;
private final WriteModel<Document> model;
SourceAwareWriteModelHolder(Object source, WriteModel<Document> model) {
this.source = source;
this.model = model;
}
public Object getSource() {
return this.source;
}
public WriteModel<Document> getModel() {
return this.model;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SourceAwareWriteModelHolder that = (SourceAwareWriteModelHolder) o;
if (!ObjectUtils.nullSafeEquals(this.source, that.source)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.model, that.model);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(model);
result = 31 * result + ObjectUtils.nullSafeHashCode(source);
return result;
}
public String toString() {
return "DefaultBulkOperations.SourceAwareWriteModelHolder(source=" + this.getSource() + ", model="
+ this.getModel() + ")";
}
Object source;
WriteModel<Document> model;
}
}

View File

@@ -47,7 +47,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider {
* @see org.springframework.data.mongodb.core.index.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
*/
@Override
public IndexOperations indexOps(String collectionName, Class<?> type) {
return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper, type);
public IndexOperations indexOps(String collectionName) {
return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper);
}
}

View File

@@ -15,8 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
@@ -41,7 +44,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
/**
* Common operations performed on an entity in the context of it's mapping metadata.
@@ -53,15 +55,12 @@ import org.springframework.util.ObjectUtils;
* @see MongoTemplate
* @see ReactiveMongoTemplate
*/
@RequiredArgsConstructor
class EntityOperations {
private static final String ID_FIELD = "_id";
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
EntityOperations(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
this.context = context;
}
private final @NonNull MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
/**
* Creates a new {@link Entity} for the given bean.
@@ -70,7 +69,7 @@ class EntityOperations {
* @return new instance of {@link Entity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
<T> Entity<T> forEntity(T entity) {
public <T> Entity<T> forEntity(T entity) {
Assert.notNull(entity, "Bean must not be null!");
@@ -93,7 +92,7 @@ class EntityOperations {
* @return new instance of {@link AdaptibleEntity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
<T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
public <T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
Assert.notNull(entity, "Bean must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null!");
@@ -109,20 +108,6 @@ class EntityOperations {
return AdaptibleMappedEntity.of(entity, context, conversionService);
}
/**
* @param source can be {@literal null}.
* @return {@literal true} if the given value is an {@literal array}, {@link Collection} or {@link Iterator}.
* @since 3.2
*/
static boolean isCollectionLike(@Nullable Object source) {
if (source == null) {
return false;
}
return ObjectUtils.isArray(source) || source instanceof Collection || source instanceof Iterator;
}
/**
* @param entityClass should not be null.
* @return the {@link MongoPersistentEntity#getCollection() collection name}.
@@ -361,14 +346,11 @@ class EntityOperations {
Number getVersion();
}
@RequiredArgsConstructor
private static class UnmappedEntity<T extends Map<String, Object>> implements AdaptibleEntity<T> {
private final T map;
protected UnmappedEntity(T map) {
this.map = map;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.PersistableSource#getIdPropertyName()
@@ -478,7 +460,7 @@ class EntityOperations {
private static class SimpleMappedEntity<T extends Map<String, Object>> extends UnmappedEntity<T> {
protected SimpleMappedEntity(T map) {
SimpleMappedEntity(T map) {
super(map);
}
@@ -501,19 +483,12 @@ class EntityOperations {
}
}
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
private static class MappedEntity<T> implements Entity<T> {
private final MongoPersistentEntity<?> entity;
private final IdentifierAccessor idAccessor;
private final PersistentPropertyAccessor<T> propertyAccessor;
protected MappedEntity(MongoPersistentEntity<?> entity, IdentifierAccessor idAccessor,
PersistentPropertyAccessor<T> propertyAccessor) {
this.entity = entity;
this.idAccessor = idAccessor;
this.propertyAccessor = propertyAccessor;
}
private final @NonNull MongoPersistentEntity<?> entity;
private final @NonNull IdentifierAccessor idAccessor;
private final @NonNull PersistentPropertyAccessor<T> propertyAccessor;
private static <T> MappedEntity<T> of(T bean,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
@@ -784,12 +759,11 @@ class EntityOperations {
* {@link TypedOperations} for generic entities that are not represented with {@link PersistentEntity} (e.g. custom
* conversions).
*/
@RequiredArgsConstructor
enum UntypedOperations implements TypedOperations<Object> {
INSTANCE;
UntypedOperations() {}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> TypedOperations<T> instance() {
return (TypedOperations) INSTANCE;
@@ -824,13 +798,10 @@ class EntityOperations {
*
* @param <T>
*/
@RequiredArgsConstructor
static class TypedEntityOperations<T> implements TypedOperations<T> {
private final MongoPersistentEntity<T> entity;
protected TypedEntityOperations(MongoPersistentEntity<T> entity) {
this.entity = entity;
}
private final @NonNull MongoPersistentEntity<T> entity;
/*
* (non-Javadoc)

View File

@@ -15,10 +15,16 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -29,13 +35,10 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableAggregationOperationSupport implements ExecutableAggregationOperation {
private final MongoTemplate template;
ExecutableAggregationOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -53,21 +56,15 @@ class ExecutableAggregationOperationSupport implements ExecutableAggregationOper
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableAggregationSupport<T>
implements AggregationWithAggregation<T>, ExecutableAggregation<T>, TerminatingAggregation<T> {
private final MongoTemplate template;
private final Class<T> domainType;
private final Aggregation aggregation;
private final String collection;
public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, Aggregation aggregation,
String collection) {
this.template = template;
this.domainType = domainType;
this.aggregation = aggregation;
this.collection = collection;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
@Nullable Aggregation aggregation;
@Nullable String collection;
/*
* (non-Javadoc)

View File

@@ -125,11 +125,6 @@ public interface ExecutableFindOperation {
/**
* Get the number of matching elements.
* <p />
* This method uses an {@link com.mongodb.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions) aggregation
* execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees shard,
* session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link MongoOperations#estimatedCount(String)} for empty queries instead.
*
* @return total number of matching elements.
*/

View File

@@ -15,6 +15,12 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.ReadPreference;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -31,7 +37,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.ReadPreference;
import com.mongodb.client.FindIterable;
/**
@@ -41,15 +46,12 @@ import com.mongodb.client.FindIterable;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableFindOperationSupport implements ExecutableFindOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableFindOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -68,23 +70,16 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableFindSupport<T>
implements ExecutableFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
private final MongoTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
@Nullable private final String collection;
private final Query query;
ExecutableFindSupport(MongoTemplate template, Class<?> domainType, Class<T> returnType,
String collection, Query query) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.collection = collection;
this.query = query;
}
@NonNull MongoTemplate template;
@NonNull Class<?> domainType;
Class<T> returnType;
@Nullable String collection;
Query query;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,13 +37,10 @@ import com.mongodb.bulk.BulkWriteResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
private final MongoTemplate template;
ExecutableInsertOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -56,20 +58,14 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableInsertSupport<T> implements ExecutableInsert<T> {
private final MongoTemplate template;
private final Class<T> domainType;
@Nullable private final String collection;
@Nullable private final BulkMode bulkMode;
ExecutableInsertSupport(MongoTemplate template, Class<T> domainType, String collection, BulkMode bulkMode) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
this.bulkMode = bulkMode;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
@Nullable String collection;
@Nullable BulkMode bulkMode;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
@@ -29,17 +32,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor
class ExecutableMapReduceOperationSupport implements ExecutableMapReduceOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableMapReduceOperationSupport(MongoTemplate template) {
Assert.notNull(template, "Template must not be null!");
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javascript)

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.List;
import org.springframework.data.mongodb.core.query.Query;
@@ -31,15 +36,12 @@ import com.mongodb.client.result.DeleteResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate tempate;
public ExecutableRemoveOperationSupport(MongoTemplate tempate) {
this.tempate = tempate;
}
private final @NonNull MongoTemplate tempate;
/*
* (non-Javadoc)
@@ -57,19 +59,14 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableRemoveSupport<T> implements ExecutableRemove<T>, RemoveWithCollection<T> {
private final MongoTemplate template;
private final Class<T> domainType;
private final Query query;
@Nullable private final String collection;
public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.collection = collection;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
Query query;
@Nullable String collection;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
@@ -30,15 +35,12 @@ import com.mongodb.client.result.UpdateResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableUpdateOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -56,34 +58,21 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableUpdateSupport<T>
implements ExecutableUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
FindAndReplaceWithOptions<T>, TerminatingFindAndReplace<T>, FindAndReplaceWithProjection<T> {
private final MongoTemplate template;
private final Class domainType;
private final Query query;
@Nullable private final UpdateDefinition update;
@Nullable private final String collection;
@Nullable private final FindAndModifyOptions findAndModifyOptions;
@Nullable private final FindAndReplaceOptions findAndReplaceOptions;
@Nullable private final Object replacement;
private final Class<T> targetType;
ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, UpdateDefinition update,
String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
Object replacement, Class<T> targetType) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.update = update;
this.collection = collection;
this.findAndModifyOptions = findAndModifyOptions;
this.findAndReplaceOptions = findAndReplaceOptions;
this.replacement = replacement;
this.targetType = targetType;
}
@NonNull MongoTemplate template;
@NonNull Class domainType;
Query query;
@Nullable UpdateDefinition update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@Nullable Object replacement;
@NonNull Class<T> targetType;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.List;
@@ -24,6 +27,8 @@ import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.util.StreamUtils;
import com.mongodb.client.model.Filters;
/**
* A MongoDB document in its mapped state. I.e. after a source document has been mapped using mapping information of the
* entity the source document was supposed to represent.
@@ -31,20 +36,13 @@ import org.springframework.data.util.StreamUtils;
* @author Oliver Gierke
* @since 2.1
*/
@RequiredArgsConstructor(staticName = "of")
public class MappedDocument {
private static final String ID_FIELD = "_id";
private static final Document ID_ONLY_PROJECTION = new Document(ID_FIELD, 1);
private final Document document;
private MappedDocument(Document document) {
this.document = document;
}
public static MappedDocument of(Document document) {
return new MappedDocument(document);
}
private final @Getter Document document;
public static Document getIdOnlyProjection() {
return ID_ONLY_PROJECTION;
@@ -93,10 +91,6 @@ public class MappedDocument {
return new MappedUpdate(Update.fromDocument(document, ID_FIELD));
}
public Document getDocument() {
return this.document;
}
/**
* An {@link UpdateDefinition} that indicates that the {@link #getUpdateObject() update object} has already been
* mapped to the specific domain type.

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Value;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
@@ -22,7 +24,6 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.WriteConcern;
@@ -170,15 +171,11 @@ public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFac
* @author Christoph Strobl
* @since 2.1
*/
static final class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory {
@Value
static class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory {
private final ClientSession session;
private final MongoDatabaseFactory delegate;
public ClientSessionBoundMongoDbFactory(ClientSession session, MongoDatabaseFactory delegate) {
this.session = session;
this.delegate = delegate;
}
ClientSession session;
MongoDatabaseFactory delegate;
/*
* (non-Javadoc)
@@ -259,40 +256,5 @@ public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFac
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
public ClientSession getSession() {
return this.session;
}
public MongoDatabaseFactory getDelegate() {
return this.delegate;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o;
if (!ObjectUtils.nullSafeEquals(this.session, that.session)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(this.session);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate);
return result;
}
public String toString() {
return "MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session=" + this.getSession() + ", delegate="
+ this.getDelegate() + ")";
}
}
}

View File

@@ -1160,12 +1160,6 @@ public interface MongoOperations extends FluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(Class)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -1182,12 +1176,6 @@ public interface MongoOperations extends FluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(String)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents.
* @param collectionName must not be {@literal null} or empty.
@@ -1196,35 +1184,6 @@ public interface MongoOperations extends FluentMongoOperations {
*/
long count(Query query, String collectionName);
/**
* Estimate the number of documents, in the collection {@link #getCollectionName(Class) identified by the given type},
* based on collection statistics.
* <p />
* Please make sure to read the MongoDB reference documentation about limitations on eg. sharded cluster or inside
* transactions.
*
* @param entityClass must not be {@literal null}.
* @return the estimated number of documents.
* @since 3.1
*/
default long estimatedCount(Class<?> entityClass) {
Assert.notNull(entityClass, "Entity class must not be null!");
return estimatedCount(getCollectionName(entityClass));
}
/**
* Estimate the number of documents in the given collection based on collection statistics.
* <p />
* Please make sure to read the MongoDB reference documentation about limitations on eg. sharded cluster or inside
* transactions.
*
* @param collectionName must not be {@literal null}.
* @return the estimated number of documents.
* @since 3.1
*/
long estimatedCount(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}. <br />
@@ -1232,12 +1191,6 @@ public interface MongoOperations extends FluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(String)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -1258,13 +1211,11 @@ public interface MongoOperations extends FluentMongoOperations {
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
* <p/>
* The {@code objectToSave} must not be collection-like.
* Insert is used to initially store the object into the database. To update an existing object use the save method.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the inserted object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> T insert(T objectToSave);
@@ -1275,13 +1226,10 @@ public interface MongoOperations extends FluentMongoOperations {
* configured otherwise, an instance of {@link MappingMongoConverter} will be used.
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
* <p/>
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
* @return the inserted object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> T insert(T objectToSave, String collectionName);
@@ -1324,12 +1272,9 @@ public interface MongoOperations extends FluentMongoOperations {
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <p />
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the saved object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> T save(T objectToSave);
@@ -1345,13 +1290,10 @@ public interface MongoOperations extends FluentMongoOperations {
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See <a
* https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation">Spring's Type
* Conversion"</a> for more details.
* <p />
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
* @return the saved object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> T save(T objectToSave, String collectionName);

View File

@@ -17,6 +17,11 @@ package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -28,6 +33,7 @@ import org.bson.Document;
import org.bson.conversions.Bson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -54,7 +60,6 @@ import org.springframework.data.mongodb.SessionSynchronization;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
import org.springframework.data.mongodb.core.QueryOperations.DeleteContext;
import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
@@ -155,14 +160,22 @@ import com.mongodb.client.result.UpdateResult;
* @author Cimon Lucas
* @author Michael J. Simons
* @author Roman Puchkovskiy
* @author Yadhukrishna S Pai
* @author Anton Barkan
* @author Bartłomiej Mazur
*/
public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
private static final Collection<String> ITERABLE_CLASSES;
static {
Set<String> iterableClasses = new HashSet<>();
iterableClasses.add(List.class.getName());
iterableClasses.add(Collection.class.getName());
iterableClasses.add(Iterator.class.getName());
ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses);
}
private final MongoConverter mongoConverter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
@@ -705,17 +718,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
});
}
@Override
public IndexOperations indexOps(String collectionName) {
return indexOps(collectionName, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.String)
*/
public IndexOperations indexOps(String collectionName, @Nullable Class<?> type) {
return new DefaultIndexOperations(this, collectionName, type);
public IndexOperations indexOps(String collectionName) {
return new DefaultIndexOperations(this, collectionName, null);
}
/*
@@ -723,7 +731,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.Class)
*/
public IndexOperations indexOps(Class<?> entityClass) {
return indexOps(getCollectionName(entityClass), entityClass);
return new DefaultIndexOperations(this, getCollectionName(entityClass), entityClass);
}
/*
@@ -755,6 +763,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
new BulkOperationContext(mode, Optional.ofNullable(getPersistentEntity(entityType)), queryMapper, updateMapper,
eventPublisher, entityCallbacks));
operations.setExceptionTranslator(exceptionTranslator);
operations.setDefaultWriteConcern(writeConcern);
return operations;
@@ -1131,19 +1140,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#estimatedCount(java.lang.String)
*/
@Override
public long estimatedCount(String collectionName) {
return doEstimatedCount(collectionName, new EstimatedDocumentCountOptions());
}
protected long doEstimatedCount(String collectionName, EstimatedDocumentCountOptions options) {
return execute(collectionName, collection -> collection.estimatedDocumentCount(options));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#insert(java.lang.Object)
@@ -1172,28 +1168,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return (T) doInsert(collectionName, objectToSave, this.mongoConverter);
}
/**
* Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
* {@link Iterator}.
*
* @param source can be {@literal null}.
* @deprecated since 3.2. Call {@link #ensureNotCollectionLike(Object)} instead.
*/
protected void ensureNotIterable(@Nullable Object source) {
ensureNotCollectionLike(source);
}
/**
* Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
* {@link Iterator}.
*
* @param source can be {@literal null}.
* @since 3.2.
*/
protected void ensureNotCollectionLike(@Nullable Object source) {
if (EntityOperations.isCollectionLike(source)) {
throw new IllegalArgumentException("Cannot use a collection here.");
protected void ensureNotIterable(@Nullable Object o) {
if (o != null) {
if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) {
throw new IllegalArgumentException("Cannot use a collection here.");
}
}
}
@@ -1376,7 +1355,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(objectToSave, "Object to save must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
ensureNotCollectionLike(objectToSave);
AdaptibleEntity<T> source = operations.forEntity(objectToSave, mongoConverter.getConversionService());
@@ -1991,7 +1969,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(aggregation, "Aggregation pipeline must not be null!");
return aggregate(aggregation, inputCollectionName, outputType, null);
AggregationOperationContext context = new TypeBasedAggregationOperationContext(aggregation.getInputType(),
mappingContext, queryMapper);
return aggregate(aggregation, inputCollectionName, outputType, context);
}
/* (non-Javadoc)
@@ -2001,7 +1981,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
public <O> AggregationResults<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType) {
return aggregate(aggregation, getCollectionName(inputType), outputType,
queryOperations.createAggregation(aggregation, inputType).getAggregationOperationContext());
new TypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper));
}
/* (non-Javadoc)
@@ -2108,13 +2088,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(aggregation, "Aggregation pipeline must not be null!");
Assert.notNull(outputType, "Output type must not be null!");
return doAggregate(aggregation, collectionName, outputType,
queryOperations.createAggregation(aggregation, context));
}
private <O> AggregationResults<O> doAggregate(Aggregation aggregation, String collectionName, Class<O> outputType,
AggregationDefinition context) {
return doAggregate(aggregation, collectionName, outputType, context.getAggregationOperationContext());
AggregationOperationContext contextToUse = new AggregationUtil(queryMapper, mappingContext)
.prepareAggregationContext(aggregation, context);
return doAggregate(aggregation, collectionName, outputType, contextToUse);
}
@SuppressWarnings("ConstantConditions")
@@ -2165,7 +2141,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
options.getComment().ifPresent(aggregateIterable::comment);
options.getHint().ifPresent(aggregateIterable::hint);
if (options.hasExecutionTimeLimit()) {
aggregateIterable = aggregateIterable.maxTime(options.getMaxTime().toMillis(), TimeUnit.MILLISECONDS);
@@ -2202,10 +2177,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(outputType, "Output type must not be null!");
Assert.isTrue(!aggregation.getOptions().isExplain(), "Can't use explain option with streaming!");
AggregationDefinition aggregationDefinition = queryOperations.createAggregation(aggregation, context);
AggregationUtil aggregationUtil = new AggregationUtil(queryMapper, mappingContext);
AggregationOperationContext rootContext = aggregationUtil.prepareAggregationContext(aggregation, context);
AggregationOptions options = aggregation.getOptions();
List<Document> pipeline = aggregationDefinition.getAggregationPipeline();
List<Document> pipeline = aggregationUtil.createPipeline(aggregation, rootContext);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Streaming aggregation: {} in collection {}", serializeToJsonSafely(pipeline), collectionName);
@@ -2223,7 +2199,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
options.getComment().ifPresent(cursor::comment);
options.getHint().ifPresent(cursor::hint);
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation) aggregation).getInputType()
: null;
@@ -2986,17 +2961,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
private class ExistsCallback implements CollectionCallback<Boolean> {
private final Document mappedQuery;
private final com.mongodb.client.model.Collation collation;
ExistsCallback(Document mappedQuery, com.mongodb.client.model.Collation collation) {
this.mappedQuery = mappedQuery;
this.collation = collation;
}
@Override
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
@@ -3018,7 +2988,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final Document sort;
private final Optional<Collation> collation;
FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
public FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
this.query = query;
this.fields = fields;
@@ -3044,9 +3014,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final List<Document> arrayFilters;
private final FindAndModifyOptions options;
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
FindAndModifyOptions options) {
public FindAndModifyCallback(Document query, Document fields, Document sort, Object update,
List<Document> arrayFilters, FindAndModifyOptions options) {
this.query = query;
this.fields = fields;
this.sort = sort;
@@ -3155,19 +3124,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @author Christoph Strobl
* @author Roman Puchkovskiy
*/
@RequiredArgsConstructor
private class ReadDocumentCallback<T> implements DocumentCallback<T> {
private final EntityReader<? super T, Bson> reader;
private final Class<T> type;
private final @NonNull EntityReader<? super T, Bson> reader;
private final @NonNull Class<T> type;
private final String collectionName;
ReadDocumentCallback(EntityReader<? super T, Bson> reader, Class<T> type, String collectionName) {
this.reader = reader;
this.type = type;
this.collectionName = collectionName;
}
@Nullable
public T doWith(@Nullable Document document) {
@@ -3195,21 +3158,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param <T>
* @since 2.0
*/
@RequiredArgsConstructor
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
private final EntityReader<Object, Bson> reader;
private final Class<S> entityType;
private final Class<T> targetType;
private final String collectionName;
ProjectingReadCallback(EntityReader<Object, Bson> reader, Class<S> entityType, Class<T> targetType,
String collectionName) {
this.reader = reader;
this.entityType = entityType;
this.targetType = targetType;
this.collectionName = collectionName;
}
private final @NonNull EntityReader<Object, Bson> reader;
private final @NonNull Class<S> entityType;
private final @NonNull Class<T> targetType;
private final @NonNull String collectionName;
/*
* (non-Javadoc)
@@ -3245,7 +3200,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final Query query;
private final @Nullable Class<?> type;
QueryCursorPreparer(Query query, @Nullable Class<?> type) {
public QueryCursorPreparer(Query query, @Nullable Class<?> type) {
this.query = query;
this.type = type;
@@ -3306,10 +3261,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
cursorToUse = cursorToUse.batchSize(meta.getCursorBatchSize());
}
if (meta.getAllowDiskUse() != null) {
cursorToUse = cursorToUse.allowDiskUse(meta.getAllowDiskUse());
}
for (Meta.CursorOption option : meta.getFlags()) {
switch (option) {
@@ -3393,6 +3344,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @author Thomas Darimont
* @since 1.7
*/
@AllArgsConstructor(access = AccessLevel.PACKAGE)
static class CloseableIterableCursorAdapter<T> implements CloseableIterator<T> {
private volatile @Nullable MongoCursor<Document> cursor;
@@ -3406,22 +3358,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param exceptionTranslator
* @param objectReadCallback
*/
CloseableIterableCursorAdapter(MongoIterable<Document> cursor, PersistenceExceptionTranslator exceptionTranslator,
DocumentCallback<T> objectReadCallback) {
public CloseableIterableCursorAdapter(MongoIterable<Document> cursor,
PersistenceExceptionTranslator exceptionTranslator, DocumentCallback<T> objectReadCallback) {
this.cursor = cursor.iterator();
this.exceptionTranslator = exceptionTranslator;
this.objectReadCallback = objectReadCallback;
}
CloseableIterableCursorAdapter(MongoCursor<Document> cursor, PersistenceExceptionTranslator exceptionTranslator,
DocumentCallback<T> objectReadCallback) {
this.cursor = cursor;
this.exceptionTranslator = exceptionTranslator;
this.objectReadCallback = objectReadCallback;
}
@Override
public boolean hasNext() {
@@ -3475,20 +3419,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
}
/**
* @deprecated since 3.1.4. Use {@link #getMongoDatabaseFactory()} instead.
* @return the {@link MongoDatabaseFactory} in use.
*/
@Deprecated
public MongoDatabaseFactory getMongoDbFactory() {
return getMongoDatabaseFactory();
}
/**
* @return the {@link MongoDatabaseFactory} in use.
* @since 3.1.4
*/
public MongoDatabaseFactory getMongoDatabaseFactory() {
return mongoDbFactory;
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.bson.Document;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
@@ -30,14 +33,11 @@ import org.springframework.util.ClassUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class PropertyOperations {
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
PropertyOperations(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
/**
* For cases where {@code fields} is {@link Document#isEmpty() empty} include only fields that are required for
* creating the projection (target) type if the {@code targetType} is a {@literal DTO projection} or a

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -32,17 +31,11 @@ import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.MongoExpression;
import org.springframework.data.mongodb.core.MappedDocument.MappedUpdate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationExpression;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
@@ -55,7 +48,6 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -202,34 +194,9 @@ class QueryOperations {
return new DeleteContext(query, false);
}
/**
* Create a new {@link AggregationDefinition} for the given {@link Aggregation}.
*
* @param aggregation must not be {@literal null}.
* @param inputType fallback mapping type in case of untyped aggregation. Can be {@literal null}.
* @return new instance of {@link AggregationDefinition}.
* @since 3.2
*/
AggregationDefinition createAggregation(Aggregation aggregation, @Nullable Class<?> inputType) {
return new AggregationDefinition(aggregation, inputType);
}
/**
* Create a new {@link AggregationDefinition} for the given {@link Aggregation}.
*
* @param aggregation must not be {@literal null}.
* @param aggregationOperationContext the {@link AggregationOperationContext} to use. Can be {@literal null}.
* @return new instance of {@link AggregationDefinition}.
* @since 3.2
*/
AggregationDefinition createAggregation(Aggregation aggregation,
@Nullable AggregationOperationContext aggregationOperationContext) {
return new AggregationDefinition(aggregation, aggregationOperationContext);
}
/**
* {@link QueryContext} encapsulates common tasks required to convert a {@link Query} into its MongoDB document
* representation, mapping field names, as well as determining and applying {@link Collation collations}.
* representation, mapping fieldnames, as well as determinging and applying {@link Collation collations}.
*
* @author Christoph Strobl
*/
@@ -238,7 +205,7 @@ class QueryOperations {
private final Query query;
/**
* Create new a {@link QueryContext} instance from the given {@literal query} (can be either a {@link Query} or a
* Create new a {@link QueryContext} instance from the given {@literal query} (can be eihter a {@link Query} or a
* plain {@link Document}.
*
* @param query can be {@literal null}.
@@ -291,21 +258,7 @@ class QueryOperations {
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType,
ProjectionFactory projectionFactory) {
Document fields = new Document();
for (Entry<String, Object> entry : query.getFieldsObject().entrySet()) {
if (entry.getValue() instanceof MongoExpression) {
AggregationOperationContext ctx = entity == null ? Aggregation.DEFAULT_CONTEXT
: new RelaxedTypeBasedAggregationOperationContext(entity.getType(), mappingContext, queryMapper);
fields.put(entry.getKey(), AggregationExpression.from((MongoExpression) entry.getValue()).toDocument(ctx));
} else {
fields.put(entry.getKey(), entry.getValue());
}
}
Document fields = query.getFieldsObject();
Document mappedFields = fields;
if (entity == null) {
@@ -322,7 +275,7 @@ class QueryOperations {
mappingContext.getRequiredPersistentEntity(targetType));
}
if (entity.hasTextScoreProperty() && !query.getQueryObject().containsKey("$text")) {
if (entity != null && entity.hasTextScoreProperty() && !query.getQueryObject().containsKey("$text")) {
mappedFields.remove(entity.getTextScoreProperty().getFieldName());
}
@@ -388,8 +341,7 @@ class QueryOperations {
}
@Override
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType,
ProjectionFactory projectionFactory) {
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType, ProjectionFactory projectionFactory) {
return getMappedFields(entity);
}
@@ -756,10 +708,10 @@ class QueryOperations {
*/
List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
Class<?> type = domainType != null ? domainType : Object.class;
AggregationOperationContext context = domainType != null
? new RelaxedTypeBasedAggregationOperationContext(domainType, mappingContext, queryMapper)
: Aggregation.DEFAULT_CONTEXT;
AggregationOperationContext context = new RelaxedTypeBasedAggregationOperationContext(type, mappingContext,
queryMapper);
return aggregationUtil.createPipeline((AggregationUpdate) update, context);
}
@@ -809,105 +761,4 @@ class QueryOperations {
return multi;
}
}
/**
* A value object that encapsulates common tasks required when running {@literal aggregations}.
*
* @since 3.2
*/
class AggregationDefinition {
private final Aggregation aggregation;
private final Lazy<AggregationOperationContext> aggregationOperationContext;
private final Lazy<List<Document>> pipeline;
private final @Nullable Class<?> inputType;
/**
* Creates new instance of {@link AggregationDefinition} extracting the input type from either the
* {@link org.springframework.data.mongodb.core.aggregation.Aggregation} in case of a {@link TypedAggregation} or
* the given {@literal aggregationOperationContext} if present. <br />
* Creates a new {@link AggregationOperationContext} if none given, based on the {@link Aggregation} input type and
* the desired {@link AggregationOptions#getDomainTypeMapping() domain type mapping}. <br />
* Pipelines are mapped on first access of {@link #getAggregationPipeline()} and cached for reuse.
*
* @param aggregation the source aggregation.
* @param aggregationOperationContext can be {@literal null}.
*/
AggregationDefinition(Aggregation aggregation, @Nullable AggregationOperationContext aggregationOperationContext) {
this.aggregation = aggregation;
if (aggregation instanceof TypedAggregation) {
this.inputType = ((TypedAggregation<?>) aggregation).getInputType();
} else if (aggregationOperationContext instanceof TypeBasedAggregationOperationContext) {
this.inputType = ((TypeBasedAggregationOperationContext) aggregationOperationContext).getType();
} else {
this.inputType = null;
}
this.aggregationOperationContext = Lazy.of(() -> aggregationOperationContext != null ? aggregationOperationContext
: aggregationUtil.createAggregationContext(aggregation, getInputType()));
this.pipeline = Lazy.of(() -> aggregationUtil.createPipeline(this.aggregation, getAggregationOperationContext()));
}
/**
* Creates new instance of {@link AggregationDefinition} extracting the input type from either the
* {@link org.springframework.data.mongodb.core.aggregation.Aggregation} in case of a {@link TypedAggregation} or
* the given {@literal aggregationOperationContext} if present. <br />
* Creates a new {@link AggregationOperationContext} based on the {@link Aggregation} input type and the desired
* {@link AggregationOptions#getDomainTypeMapping() domain type mapping}. <br />
* Pipelines are mapped on first access of {@link #getAggregationPipeline()} and cached for reuse.
*
* @param aggregation the source aggregation.
* @param inputType can be {@literal null}.
*/
AggregationDefinition(Aggregation aggregation, @Nullable Class<?> inputType) {
this.aggregation = aggregation;
if (aggregation instanceof TypedAggregation) {
this.inputType = ((TypedAggregation<?>) aggregation).getInputType();
} else {
this.inputType = inputType;
}
this.aggregationOperationContext = Lazy
.of(() -> aggregationUtil.createAggregationContext(aggregation, getInputType()));
this.pipeline = Lazy.of(() -> aggregationUtil.createPipeline(this.aggregation, getAggregationOperationContext()));
}
/**
* Obtain the already mapped pipeline.
*
* @return never {@literal null}.
*/
List<Document> getAggregationPipeline() {
return pipeline.get();
}
/**
* @return {@literal true} if the last aggregation stage is either {@literal $out} or {@literal $merge}.
* @see AggregationPipeline#isOutOrMerge()
*/
boolean isOutOrMerge() {
return aggregation.getPipeline().isOutOrMerge();
}
/**
* Obtain the {@link AggregationOperationContext} used for mapping the pipeline.
*
* @return never {@literal null}.
*/
AggregationOperationContext getAggregationOperationContext() {
return aggregationOperationContext.get();
}
/**
* @return the input type to map the pipeline against. Can be {@literal null}.
*/
@Nullable
Class<?> getInputType() {
return inputType;
}
}
}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -58,22 +62,15 @@ class ReactiveAggregationOperationSupport implements ReactiveAggregationOperatio
return new ReactiveAggregationSupport<>(template, domainType, null, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveAggregationSupport<T>
implements AggregationOperationWithAggregation<T>, ReactiveAggregation<T>, TerminatingAggregationOperation<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final Aggregation aggregation;
private final String collection;
ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, Aggregation aggregation,
String collection) {
this.template = template;
this.domainType = domainType;
this.aggregation = aggregation;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
Aggregation aggregation;
String collection;
/*
* (non-Javadoc)

View File

@@ -106,12 +106,6 @@ public interface ReactiveFindOperation {
/**
* Get the number of matching elements.
* <p />
* This method uses an
* {@link com.mongodb.reactivestreams.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but
* guarantees shard, session and transaction compliance. In case an inaccurate count satisfies the applications
* needs use {@link ReactiveMongoOperations#estimatedCount(String)} for empty queries instead.
*
* @return {@link Mono} emitting total number of matching elements. Never {@literal null}.
*/

View File

@@ -15,10 +15,15 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.bson.Document;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -34,15 +39,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveFindOperationSupport implements ReactiveFindOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveFindOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -62,24 +64,16 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveFindSupport<T>
implements ReactiveFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
private final ReactiveMongoTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final String collection;
private final Query query;
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType,
String collection, Query query) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.collection = collection;
this.query = query;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<?> domainType;
Class<T> returnType;
String collection;
Query query;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -30,13 +34,10 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
private final ReactiveMongoTemplate template;
ReactiveInsertOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -50,18 +51,13 @@ class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
return new ReactiveInsertSupport<>(template, domainType, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveInsertSupport<T> implements ReactiveInsert<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final String collection;
ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> domainType, String collection) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
String collection;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
@@ -29,15 +31,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor
class ReactiveMapReduceOperationSupport implements ReactiveMapReduceOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveMapReduceOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javascript)

View File

@@ -15,15 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import org.reactivestreams.Publisher;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.util.Assert;
import com.mongodb.reactivestreams.client.ClientSession;
/**
@@ -33,7 +29,7 @@ import com.mongodb.reactivestreams.client.ClientSession;
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.1
* @see Mono#deferContextual(Function)
* @see Mono#subscriberContext()
* @see Context
*/
public class ReactiveMongoContext {
@@ -50,14 +46,8 @@ public class ReactiveMongoContext {
*/
public static Mono<ClientSession> getSession() {
return Mono.deferContextual(ctx -> {
if (ctx.hasKey(SESSION_KEY)) {
return ctx.<Mono<ClientSession>> get(SESSION_KEY);
}
return Mono.empty();
});
return Mono.subscriberContext().filter(ctx -> ctx.hasKey(SESSION_KEY))
.flatMap(ctx -> ctx.<Mono<ClientSession>> get(SESSION_KEY));
}
/**

View File

@@ -940,12 +940,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.reactivestreams.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(Class)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -962,12 +956,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.reactivestreams.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(String)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents.
* @param collectionName must not be {@literal null} or empty.
@@ -983,12 +971,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* <p />
* This method uses an
* {@link com.mongodb.reactivestreams.client.MongoCollection#countDocuments(org.bson.conversions.Bson, com.mongodb.client.model.CountOptions)
* aggregation execution} even for empty {@link Query queries} which may have an impact on performance, but guarantees
* shard, session and transaction compliance. In case an inaccurate count satisfies the applications needs use
* {@link #estimatedCount(String)} for empty queries instead.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -998,35 +980,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*/
Mono<Long> count(Query query, @Nullable Class<?> entityClass, String collectionName);
/**
* Estimate the number of documents, in the collection {@link #getCollectionName(Class) identified by the given type},
* based on collection statistics.
* <p />
* Please make sure to read the MongoDB reference documentation about limitations on eg. sharded cluster or inside
* transactions.
*
* @param entityClass must not be {@literal null}.
* @return a {@link Mono} emitting the estimated number of documents.
* @since 3.1
*/
default Mono<Long> estimatedCount(Class<?> entityClass) {
Assert.notNull(entityClass, "Entity class must not be null!");
return estimatedCount(getCollectionName(entityClass));
}
/**
* Estimate the number of documents in the given collection based on collection statistics.
* <p />
* Please make sure to read the MongoDB reference documentation about limitations on eg. sharded cluster or inside
* transactions.
*
* @param collectionName must not be {@literal null}.
* @return a {@link Mono} emitting the estimated number of documents.
* @since 3.1
*/
Mono<Long> estimatedCount(String collectionName);
/**
* Insert the object into the collection for the entity type of the object to save.
* <p/>
@@ -1038,13 +991,11 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > 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.
* <p />
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the inserted object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> Mono<T> insert(T objectToSave);
@@ -1055,13 +1006,10 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* configured otherwise, an instance of {@link MappingMongoConverter} will be used.
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
* <p />
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
* @return the inserted object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> Mono<T> insert(T objectToSave, String collectionName);
@@ -1103,6 +1051,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > 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.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
@@ -1149,12 +1098,9 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <p />
* The {@code objectToSave} must not be collection-like.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the saved object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> Mono<T> save(T objectToSave);
@@ -1174,7 +1120,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @param collectionName name of the collection to store the object in. Must not be {@literal null}.
* @return the saved object.
* @throws IllegalArgumentException in case the {@code objectToSave} is collection-like.
*/
<T> Mono<T> save(T objectToSave, String collectionName);

View File

@@ -17,6 +17,9 @@ package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@@ -36,6 +39,7 @@ import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -60,7 +64,6 @@ import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.ReactiveMongoDatabaseUtils;
import org.springframework.data.mongodb.SessionSynchronization;
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
import org.springframework.data.mongodb.core.QueryOperations.DeleteContext;
import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
@@ -70,7 +73,6 @@ import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
@@ -118,7 +120,16 @@ import com.mongodb.CursorType;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.client.model.*;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.CreateCollectionOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.ValidationOptions;
import com.mongodb.client.model.changestream.FullDocument;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertOneResult;
@@ -147,7 +158,6 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
* @author Christoph Strobl
* @author Roman Puchkovskiy
* @author Mathieu Ouellet
* @author Yadhukrishna S Pai
* @since 2.0
*/
public class ReactiveMongoTemplate implements ReactiveMongoOperations, ApplicationContextAware {
@@ -156,6 +166,18 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMongoTemplate.class);
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
private static final Collection<Class<?>> ITERABLE_CLASSES;
static {
Set<Class<?>> iterableClasses = new HashSet<>();
iterableClasses.add(List.class);
iterableClasses.add(Collection.class);
iterableClasses.add(Iterator.class);
iterableClasses.add(Publisher.class);
ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses);
}
private final MongoConverter mongoConverter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
@@ -562,7 +584,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
ReactiveMongoTemplate.this);
return Flux.from(action.doInSession(operations)) //
.contextWrite(ctx -> ReactiveMongoContext.setSession(ctx, Mono.just(session)));
.subscriberContext(ctx -> ReactiveMongoContext.setSession(ctx, Mono.just(session)));
}
/*
@@ -934,7 +956,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
Assert.notNull(aggregation, "Aggregation pipeline must not be null!");
return doAggregate(aggregation, inputCollectionName, aggregation.getInputType(), outputType);
AggregationOperationContext context = new TypeBasedAggregationOperationContext(aggregation.getInputType(),
mappingContext, queryMapper);
return aggregate(aggregation, inputCollectionName, outputType, context);
}
/*
@@ -952,7 +976,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
*/
@Override
public <O> Flux<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType) {
return doAggregate(aggregation, getCollectionName(inputType), inputType, outputType);
return aggregate(aggregation, getCollectionName(inputType), outputType,
new TypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper));
}
/*
@@ -961,29 +987,40 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
*/
@Override
public <O> Flux<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType) {
return doAggregate(aggregation, collectionName, null, outputType);
return aggregate(aggregation, collectionName, outputType, null);
}
protected <O> Flux<O> doAggregate(Aggregation aggregation, String collectionName, @Nullable Class<?> inputType, Class<O> outputType) {
/**
* @param aggregation must not be {@literal null}.
* @param collectionName must not be {@literal null}.
* @param outputType must not be {@literal null}.
* @param context can be {@literal null} and will be defaulted to {@link Aggregation#DEFAULT_CONTEXT}.
* @return never {@literal null}.
*/
protected <O> Flux<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType,
@Nullable AggregationOperationContext context) {
Assert.notNull(aggregation, "Aggregation pipeline must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
Assert.notNull(outputType, "Output type must not be null!");
AggregationUtil aggregationUtil = new AggregationUtil(queryMapper, mappingContext);
AggregationOperationContext rootContext = aggregationUtil.prepareAggregationContext(aggregation, context);
AggregationOptions options = aggregation.getOptions();
List<Document> pipeline = aggregationUtil.createPipeline(aggregation, rootContext);
Assert.isTrue(!options.isExplain(), "Cannot use explain option with streaming!");
AggregationDefinition ctx = queryOperations.createAggregation(aggregation, inputType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Streaming aggregation: {} in collection {}", serializeToJsonSafely(ctx.getAggregationPipeline()), collectionName);
LOGGER.debug("Streaming aggregation: {} in collection {}", serializeToJsonSafely(pipeline), collectionName);
}
ReadDocumentCallback<O> readCallback = new ReadDocumentCallback<>(mongoConverter, outputType, collectionName);
return execute(collectionName,
collection -> aggregateAndMap(collection, ctx.getAggregationPipeline(), ctx.isOutOrMerge(), options,
collection -> aggregateAndMap(collection, pipeline, aggregation.getPipeline().isOutOrMerge(), options,
readCallback,
ctx.getInputType()));
aggregation instanceof TypedAggregation ? ((TypedAggregation<?>) aggregation).getInputType() : null));
}
private <O> Flux<O> aggregateAndMap(MongoCollection<Document> collection, List<Document> pipeline,
@@ -998,7 +1035,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
}
options.getComment().ifPresent(cursor::comment);
options.getHint().ifPresent(cursor::hint);
Optionals.firstNonEmpty(options::getCollation, () -> operations.forType(inputType).getCollation()) //
.map(Collation::toMongoCollation) //
@@ -1222,15 +1258,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#estimatedCount(java.lang.String)
*/
@Override
public Mono<Long> estimatedCount(String collectionName) {
return doEstimatedCount(collectionName, new EstimatedDocumentCountOptions());
}
/**
* Run the actual count operation against the collection with given name.
*
@@ -1245,11 +1272,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
}
protected Mono<Long> doEstimatedCount(String collectionName, EstimatedDocumentCountOptions options) {
return createMono(collectionName, collection -> collection.estimatedDocumentCount(options));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#insert(reactor.core.publisher.Mono)
@@ -2086,7 +2108,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
AggregationOperationContext context = agg instanceof TypedAggregation
? new TypeBasedAggregationOperationContext(((TypedAggregation<?>) agg).getInputType(),
getConverter().getMappingContext(), queryMapper)
: new RelaxedTypeBasedAggregationOperationContext(Object.class, mappingContext, queryMapper);
: Aggregation.DEFAULT_CONTEXT;
return agg.toPipeline(new PrefixingDelegatingAggregationOperationContext(context, "fullDocument",
Arrays.asList("operationType", "fullDocument", "documentKey", "updateDescription", "ns")));
@@ -2396,8 +2418,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
* @param query the query document that specifies the criteria used to find a record.
* @param fields the document that specifies the fields to be returned.
* @param entityClass the parameterized type of the returned list.
* @param preparer allows for customization of the {@link com.mongodb.client.FindIterable} used when iterating over
* the result set, (apply limits, skips and so on).
* @param preparer allows for customization of the {@link com.mongodb.client.FindIterable} used when iterating over the result set, (apply
* limits, skips and so on).
* @return the {@link List} of converted objects.
*/
protected <T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
@@ -2655,27 +2677,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
}
}
/**
* Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
* {@link Iterator}.
*
* @param source can be {@literal null}.
* @deprecated since 3.2. Call {@link #ensureNotCollectionLike(Object)} instead.
*/
protected void ensureNotIterable(@Nullable Object source) {
ensureNotCollectionLike(source);
}
protected void ensureNotIterable(Object o) {
/**
* Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
* {@link Iterator}.
*
* @param source can be {@literal null}.
* @since 3.2.
*/
protected void ensureNotCollectionLike(@Nullable Object source) {
boolean isIterable = o.getClass().isArray()
|| ITERABLE_CLASSES.stream().anyMatch(iterableClass -> iterableClass.isAssignableFrom(o.getClass())
|| o.getClass().getName().equals(iterableClass.getName()));
if (EntityOperations.isCollectionLike(source) || source instanceof Publisher) {
if (isIterable) {
throw new IllegalArgumentException("Cannot use a collection here.");
}
}
@@ -2717,14 +2725,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
return potentiallyForceAcknowledgedWrite(wc);
}
/**
* @return the {@link MongoDatabaseFactory} in use.
* @since 3.1.4
*/
public ReactiveMongoDatabaseFactory getMongoDatabaseFactory() {
return mongoDatabaseFactory;
}
@Nullable
private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) {
@@ -2893,6 +2893,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
*
* @author Mark Paluch
*/
@RequiredArgsConstructor
private static class FindCallback implements ReactiveCollectionQueryCallback<Document> {
private final @Nullable Document query;
@@ -2902,12 +2903,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
this(query, null);
}
FindCallback(Document query, Document fields) {
this.query = query;
this.fields = fields;
}
@Override
public FindPublisher<Document> doInCollection(MongoCollection<Document> collection) {
@@ -2961,6 +2956,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
/**
* @author Mark Paluch
*/
@RequiredArgsConstructor
private static class FindAndModifyCallback implements ReactiveCollectionCallback<Document> {
private final Document query;
@@ -2970,17 +2966,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
private final List<Document> arrayFilters;
private final FindAndModifyOptions options;
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
FindAndModifyOptions options) {
this.query = query;
this.fields = fields;
this.sort = sort;
this.update = update;
this.arrayFilters = arrayFilters;
this.options = options;
}
@Override
public Publisher<Document> doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
@@ -3036,6 +3021,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
private static class FindAndReplaceCallback implements ReactiveCollectionCallback<Document> {
private final Document query;
@@ -3045,17 +3031,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
private final @Nullable com.mongodb.client.model.Collation collation;
private final FindAndReplaceOptions options;
FindAndReplaceCallback(Document query, Document fields, Document sort, Document update,
com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
this.query = query;
this.fields = fields;
this.sort = sort;
this.update = update;
this.collation = collation;
this.options = options;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveCollectionCallback#doInCollection(com.mongodb.reactivestreams.client.MongoCollection)
@@ -3172,20 +3147,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
* @author Roman Puchkovskiy
* @since 2.0
*/
@RequiredArgsConstructor
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
private final EntityReader<Object, Bson> reader;
private final Class<S> entityType;
private final Class<T> targetType;
private final String collectionName;
ProjectingReadCallback(EntityReader<Object, Bson> reader, Class<S> entityType, Class<T> targetType,
String collectionName) {
this.reader = reader;
this.entityType = entityType;
this.targetType = targetType;
this.collectionName = collectionName;
}
private final @NonNull EntityReader<Object, Bson> reader;
private final @NonNull Class<S> entityType;
private final @NonNull Class<T> targetType;
private final @NonNull String collectionName;
@SuppressWarnings("unchecked")
public Mono<T> doWith(Document document) {
@@ -3324,10 +3292,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
if (meta.getCursorBatchSize() != null) {
findPublisherToUse = findPublisherToUse.batchSize(meta.getCursorBatchSize());
}
if (meta.getAllowDiskUse() != null) {
findPublisherToUse = findPublisherToUse.allowDiskUse(meta.getAllowDiskUse());
}
}
} catch (RuntimeException e) {
@@ -3410,14 +3374,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
}
}
@RequiredArgsConstructor
class IndexCreatorEventListener implements ApplicationListener<MappingContextEvent<?, ?>> {
final Consumer<Throwable> subscriptionExceptionHandler;
public IndexCreatorEventListener(Consumer<Throwable> subscriptionExceptionHandler) {
this.subscriptionExceptionHandler = subscriptionExceptionHandler;
}
@Override
public void onApplicationEvent(MappingContextEvent<?, ?> event) {

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -31,15 +35,12 @@ import com.mongodb.client.result.DeleteResult;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate tempate;
ReactiveRemoveOperationSupport(ReactiveMongoTemplate tempate) {
this.tempate = tempate;
}
private final @NonNull ReactiveMongoTemplate tempate;
/*
* (non-Javadoc)
@@ -53,20 +54,14 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
return new ReactiveRemoveSupport<>(tempate, domainType, ALL_QUERY, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCollection<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final Query query;
private final String collection;
ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
Query query;
String collection;
/*
* (non-Javadoc)

View File

@@ -15,10 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -32,15 +35,12 @@ import com.mongodb.client.result.UpdateResult;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveUpdateOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -54,34 +54,21 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
return new ReactiveUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null, null, null, domainType);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveUpdateSupport<T>
implements ReactiveUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
FindAndReplaceWithOptions<T>, FindAndReplaceWithProjection<T>, TerminatingFindAndReplace<T> {
private final ReactiveMongoTemplate template;
private final Class<?> domainType;
private final Query query;
private final org.springframework.data.mongodb.core.query.UpdateDefinition update;
@Nullable private final String collection;
@Nullable private final FindAndModifyOptions findAndModifyOptions;
@Nullable private final FindAndReplaceOptions findAndReplaceOptions;
@Nullable private final Object replacement;
private final Class<T> targetType;
ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, UpdateDefinition update,
String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
Object replacement, Class<T> targetType) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.update = update;
this.collection = collection;
this.findAndModifyOptions = findAndModifyOptions;
this.findAndReplaceOptions = findAndReplaceOptions;
this.replacement = replacement;
this.targetType = targetType;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<?> domainType;
Query query;
org.springframework.data.mongodb.core.query.UpdateDefinition update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@Nullable Object replacement;
@NonNull Class<T> targetType;
/*
* (non-Javadoc)
@@ -136,9 +123,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
String collectionName = getCollectionName();
return template.findAndModify(query, update,
findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType,
collectionName);
return template.findAndModify(query, update, findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType, collectionName);
}
/*

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Value;
import reactor.core.publisher.Mono;
import org.bson.codecs.configuration.CodecRegistry;
@@ -26,7 +27,6 @@ import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
@@ -175,16 +175,11 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* @author Christoph Strobl
* @since 2.1
*/
static final class ClientSessionBoundMongoDbFactory implements ReactiveMongoDatabaseFactory {
@Value
static class ClientSessionBoundMongoDbFactory implements ReactiveMongoDatabaseFactory {
private final ClientSession session;
private final ReactiveMongoDatabaseFactory delegate;
ClientSessionBoundMongoDbFactory(ClientSession session, ReactiveMongoDatabaseFactory delegate) {
this.session = session;
this.delegate = delegate;
}
ClientSession session;
ReactiveMongoDatabaseFactory delegate;
/*
* (non-Javadoc)
@@ -273,40 +268,5 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
public ClientSession getSession() {
return this.session;
}
public ReactiveMongoDatabaseFactory getDelegate() {
return this.delegate;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o;
if (!ObjectUtils.nullSafeEquals(this.session, that.session)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(this.session);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate);
return result;
}
public String toString() {
return "SimpleReactiveMongoDatabaseFactory.ClientSessionBoundMongoDbFactory(session=" + this.getSession()
+ ", delegate=" + this.getDelegate() + ")";
}
}
}

View File

@@ -29,11 +29,8 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Support class for {@link AggregationExpression} implementations.
*
* @author Christoph Strobl
* @author Matt Morrissette
* @author Mark Paluch
* @since 1.10
*/
abstract class AbstractAggregationExpression implements AggregationExpression {
@@ -52,6 +49,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return toDocument(this.value, context);
}
@SuppressWarnings("unchecked")
public Document toDocument(Object value, AggregationOperationContext context) {
return new Document(getMongoMethod(), unpack(value, context));
}
@@ -103,19 +101,17 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return value;
}
@SuppressWarnings("unchecked")
protected List<Object> append(Object value, Expand expandList) {
if (this.value instanceof List) {
List<Object> clone = new ArrayList<>((List<Object>) this.value);
List<Object> clone = new ArrayList<Object>((List) this.value);
if (value instanceof Collection && Expand.EXPAND_VALUES.equals(expandList)) {
clone.addAll((Collection<?>) value);
} else {
clone.add(value);
}
return clone;
}
@@ -133,72 +129,25 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return append(value, Expand.EXPAND_VALUES);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Map<String, Object> append(String key, Object value) {
@SuppressWarnings("unchecked")
protected java.util.Map<String, Object> append(String key, Object value) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
java.util.Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
clone.put(key, value);
return clone;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Map<String, Object> remove(String key) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
clone.remove(key);
return clone;
}
/**
* Append the given key at the position in the underlying {@link LinkedHashMap}.
*
* @param index
* @param key
* @param value
* @return
* @since 3.1
*/
@SuppressWarnings({ "unchecked" })
protected Map<String, Object> appendAt(int index, String key, Object value) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>();
int i = 0;
for (Map.Entry<String, Object> entry : ((Map<String, Object>) this.value).entrySet()) {
if (i == index) {
clone.put(key, value);
}
if (!entry.getKey().equals(key)) {
clone.put(entry.getKey(), entry.getValue());
}
i++;
}
if (i <= index) {
clone.put(key, value);
}
return clone;
}
@SuppressWarnings({ "rawtypes" })
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<>(Collections.singletonList(value));
}
@@ -228,7 +177,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
return (T) ((Map<String, Object>) this.value).get(key);
return (T) ((java.util.Map<String, Object>) this.value).get(key);
}
/**
@@ -238,11 +187,11 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
* @return
*/
@SuppressWarnings("unchecked")
protected Map<String, Object> argumentMap() {
protected java.util.Map<String, Object> argumentMap() {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
return Collections.unmodifiableMap((java.util.Map<String, Object>) value);
return Collections.unmodifiableMap((java.util.Map) value);
}
/**
@@ -259,7 +208,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return false;
}
return ((Map<String, Object>) this.value).containsKey(key);
return ((java.util.Map<String, Object>) this.value).containsKey(key);
}
protected abstract String getMongoMethod();

View File

@@ -16,7 +16,6 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
import org.springframework.data.mongodb.MongoExpression;
/**
* An {@link AggregationExpression} can be used with field expressions in aggregation pipeline stages like
@@ -26,37 +25,7 @@ import org.springframework.data.mongodb.MongoExpression;
* @author Oliver Gierke
* @author Christoph Strobl
*/
public interface AggregationExpression extends MongoExpression {
/**
* Create an {@link AggregationExpression} out of a given {@link MongoExpression} to ensure the resulting
* {@link MongoExpression#toDocument() Document} is mapped against the {@link AggregationOperationContext}. <br />
* If the given expression is already an {@link AggregationExpression} the very same instance is returned.
*
* @param expression must not be {@literal null}.
* @return never {@literal null}.
* @since 3.2
*/
static AggregationExpression from(MongoExpression expression) {
if (expression instanceof AggregationExpression) {
return AggregationExpression.class.cast(expression);
}
return (context) -> context.getMappedObject(expression.toDocument());
}
/**
* Obtain the as is (unmapped) representation of the {@link AggregationExpression}. Use
* {@link #toDocument(AggregationOperationContext)} with a matching {@link AggregationOperationContext context} to
* engage domain type mapping including field name resolution.
*
* @see org.springframework.data.mongodb.MongoExpression#toDocument()
*/
@Override
default Document toDocument() {
return toDocument(Aggregation.DEFAULT_CONTEXT);
}
public interface AggregationExpression {
/**
* Turns the {@link AggregationExpression} into a {@link Document} within the given

View File

@@ -32,7 +32,6 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Yadhukrishna S Pai
* @see Aggregation#withOptions(AggregationOptions)
* @see TypedAggregation#withOptions(AggregationOptions)
* @since 1.6
@@ -46,17 +45,14 @@ public class AggregationOptions {
private static final String COLLATION = "collation";
private static final String COMMENT = "comment";
private static final String MAX_TIME = "maxTimeMS";
private static final String HINT = "hint";
private final boolean allowDiskUse;
private final boolean explain;
private final Optional<Document> cursor;
private final Optional<Collation> collation;
private final Optional<String> comment;
private final Optional<Document> hint;
private Duration maxTime = Duration.ZERO;
private ResultOptions resultOptions = ResultOptions.READ;
private DomainTypeMapping domainTypeMapping = DomainTypeMapping.RELAXED;
/**
* Creates a new {@link AggregationOptions}.
@@ -75,13 +71,13 @@ public class AggregationOptions {
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
* aggregation.
* @param collation collation for string comparison. Can be {@literal null}.
* @since 2.0
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation) {
this(allowDiskUse, explain, cursor, collation, null, null);
this(allowDiskUse, explain, cursor, collation, null);
}
/**
@@ -90,37 +86,19 @@ public class AggregationOptions {
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
* aggregation.
* @param collation collation for string comparison. Can be {@literal null}.
* @param comment execution comment. Can be {@literal null}.
* @since 2.2
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment) {
this(allowDiskUse, explain, cursor, collation, comment, null);
}
/**
* Creates a new {@link AggregationOptions}.
*
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
* @param collation collation for string comparison. Can be {@literal null}.
* @param comment execution comment. Can be {@literal null}.
* @param hint can be {@literal null}, used to provide an index that would be forcibly used by query optimizer.
* @since 3.1
*/
private AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment, @Nullable Document hint) {
this.allowDiskUse = allowDiskUse;
this.explain = explain;
this.cursor = Optional.ofNullable(cursor);
this.collation = Optional.ofNullable(collation);
this.comment = Optional.ofNullable(comment);
this.hint = Optional.ofNullable(hint);
}
/**
@@ -152,9 +130,8 @@ public class AggregationOptions {
Collation collation = document.containsKey(COLLATION) ? Collation.from(document.get(COLLATION, Document.class))
: null;
String comment = document.getString(COMMENT);
Document hint = document.get(HINT, Document.class);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
if (document.containsKey(MAX_TIME)) {
options.maxTime = Duration.ofMillis(document.getLong(MAX_TIME));
}
@@ -235,16 +212,6 @@ public class AggregationOptions {
return comment;
}
/**
* Get the hint used to to fulfill the aggregation.
*
* @return never {@literal null}.
* @since 3.1
*/
public Optional<Document> getHint() {
return hint;
}
/**
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
* @since 3.0
@@ -262,14 +229,6 @@ public class AggregationOptions {
return ResultOptions.SKIP.equals(resultOptions);
}
/**
* @return the domain type mapping strategy do apply. Never {@literal null}.
* @since 3.2
*/
public DomainTypeMapping getDomainTypeMapping() {
return domainTypeMapping;
}
/**
* Returns a new potentially adjusted copy for the given {@code aggregationCommandObject} with the configuration
* applied.
@@ -289,10 +248,6 @@ public class AggregationOptions {
result.put(EXPLAIN, explain);
}
if (result.containsKey(HINT)) {
hint.ifPresent(val -> result.append(HINT, val));
}
if (!result.containsKey(CURSOR)) {
cursor.ifPresent(val -> result.put(CURSOR, val));
}
@@ -322,7 +277,6 @@ public class AggregationOptions {
cursor.ifPresent(val -> document.put(CURSOR, val));
collation.ifPresent(val -> document.append(COLLATION, val.toDocument()));
comment.ifPresent(val -> document.append(COMMENT, val));
hint.ifPresent(val -> document.append(HINT, val));
if (hasExecutionTimeLimit()) {
document.append(MAX_TIME, maxTime.toMillis());
@@ -364,10 +318,8 @@ public class AggregationOptions {
private @Nullable Document cursor;
private @Nullable Collation collation;
private @Nullable String comment;
private @Nullable Document hint;
private @Nullable Duration maxTime;
private @Nullable ResultOptions resultOptions;
private @Nullable DomainTypeMapping domainTypeMapping;
/**
* Defines whether to off-load intensive sort-operations to disk.
@@ -444,24 +396,11 @@ public class AggregationOptions {
return this;
}
/**
* Define a hint that is used by query optimizer to to fulfill the aggregation.
*
* @param hint can be {@literal null}.
* @return this.
* @since 3.1
*/
public Builder hint(@Nullable Document hint) {
this.hint = hint;
return this;
}
/**
* Set the time limit for processing.
*
* @param maxTime {@link Duration#ZERO} is used for the default unbounded behavior. {@link Duration#isNegative()
* Negative} values will be ignored.
* Negative} values will be ignored.
* @return this.
* @since 3.0
*/
@@ -485,44 +424,6 @@ public class AggregationOptions {
return this;
}
/**
* Apply a strict domain type mapping considering {@link org.springframework.data.mongodb.core.mapping.Field}
* annotations throwing errors for non-existent, but referenced fields.
*
* @return this.
* @since 3.2
*/
public Builder strictMapping() {
this.domainTypeMapping = DomainTypeMapping.STRICT;
return this;
}
/**
* Apply a relaxed domain type mapping considering {@link org.springframework.data.mongodb.core.mapping.Field}
* annotations using the user provided name if a referenced field does not exist.
*
* @return this.
* @since 3.2
*/
public Builder relaxedMapping() {
this.domainTypeMapping = DomainTypeMapping.RELAXED;
return this;
}
/**
* Apply no domain type mapping at all taking the pipeline as-is.
*
* @return this.
* @since 3.2
*/
public Builder noMapping() {
this.domainTypeMapping = DomainTypeMapping.NONE;
return this;
}
/**
* Returns a new {@link AggregationOptions} instance with the given configuration.
*
@@ -530,16 +431,13 @@ public class AggregationOptions {
*/
public AggregationOptions build() {
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
if (maxTime != null) {
options.maxTime = maxTime;
}
if (resultOptions != null) {
options.resultOptions = resultOptions;
}
if (domainTypeMapping != null) {
options.domainTypeMapping = domainTypeMapping;
}
return options;
}
@@ -559,27 +457,4 @@ public class AggregationOptions {
*/
READ;
}
/**
* Aggregation pipeline Domain type mappings supported by the mapping layer.
*
* @since 3.2
*/
public enum DomainTypeMapping {
/**
* Mapping throws errors for non-existent, but referenced fields.
*/
STRICT,
/**
* Fields that do not exist in the model are treated as-is.
*/
RELAXED,
/**
* Do not attempt to map fields against the model and treat the entire pipeline as-is.
*/
NONE
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.bson.Document;
import org.springframework.util.Assert;
@@ -27,7 +26,6 @@ import org.springframework.util.Assert;
* The {@link AggregationPipeline} holds the collection of {@link AggregationOperation aggregation stages}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0.2
*/
public class AggregationPipeline {
@@ -47,8 +45,6 @@ public class AggregationPipeline {
* @param aggregationOperations must not be {@literal null}.
*/
public AggregationPipeline(List<AggregationOperation> aggregationOperations) {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
pipeline = new ArrayList<>(aggregationOperations);
}
@@ -86,77 +82,30 @@ public class AggregationPipeline {
*/
public boolean isOutOrMerge() {
if (isEmpty()) {
if (pipeline.isEmpty()) {
return false;
}
AggregationOperation operation = pipeline.get(pipeline.size() - 1);
return isOut(operation) || isMerge(operation);
String operator = pipeline.get(pipeline.size() - 1).getOperator();
return operator.equals("$out") || operator.equals("$merge");
}
void verify() {
// check $out/$merge is the last operation if it exists
for (AggregationOperation operation : pipeline) {
for (AggregationOperation aggregationOperation : pipeline) {
if (isOut(operation) && !isLast(operation)) {
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation)) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
if (isMerge(operation) && !isLast(operation)) {
if (aggregationOperation instanceof MergeOperation && !isLast(aggregationOperation)) {
throw new IllegalArgumentException("The $merge operator must be the last stage in the pipeline.");
}
}
}
/**
* Return whether this aggregation pipeline defines a {@code $unionWith} stage that may contribute documents from
* other collections. Checking for presence of union stages is useful when attempting to determine the aggregation
* element type for mapping metadata computation.
*
* @return {@literal true} the aggregation pipeline makes use of {@code $unionWith}.
* @since 3.1
*/
public boolean containsUnionWith() {
return containsOperation(AggregationPipeline::isUnionWith);
}
/**
* @return {@literal true} if the pipeline does not contain any stages.
* @since 3.1
*/
public boolean isEmpty() {
return pipeline.isEmpty();
}
private boolean containsOperation(Predicate<AggregationOperation> predicate) {
if (isEmpty()) {
return false;
}
for (AggregationOperation element : pipeline) {
if (predicate.test(element)) {
return true;
}
}
return false;
}
private boolean isLast(AggregationOperation aggregationOperation) {
return pipeline.indexOf(aggregationOperation) == pipeline.size() - 1;
}
private static boolean isUnionWith(AggregationOperation operator) {
return operator instanceof UnionWithOperation || operator.getOperator().equals("$unionWith");
}
private static boolean isMerge(AggregationOperation operator) {
return operator instanceof MergeOperation || operator.getOperator().equals("$merge");
}
private static boolean isOut(AggregationOperation operator) {
return operator instanceof OutOperation || operator.getOperator().equals("$out");
}
}

View File

@@ -104,7 +104,7 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc
return new Document(getOperator(), options);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/

View File

@@ -1835,7 +1835,7 @@ public class DateOperators {
* @param millisecond must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal millisecond} is {@literal null}
* @deprecated since 3.2, use {@link #millisecond(Object)} instead.
* @deprecated since 3.0.7, use {@link #millisecond(Object)} instead.
*/
@Deprecated
default T milliseconds(Object millisecond) {
@@ -1849,7 +1849,7 @@ public class DateOperators {
* @param millisecond must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal millisecond} is {@literal null}
* @since 3.2
* @since 3.0.7
*/
T millisecond(Object millisecond);
@@ -1859,7 +1859,7 @@ public class DateOperators {
* @param fieldReference must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
* @deprecated since 3.2,use {@link #millisecondOf(String)} instead.
* @deprecated since 3.0.7,use {@link #millisecondOf(String)} instead.
*/
@Deprecated
default T millisecondsOf(String fieldReference) {
@@ -1872,7 +1872,7 @@ public class DateOperators {
* @param fieldReference must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
* @since 3.2
* @since 3.0.7
*/
default T millisecondOf(String fieldReference) {
return milliseconds(Fields.field(fieldReference));
@@ -1884,7 +1884,7 @@ public class DateOperators {
* @param expression must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
* @deprecated since 3.2, use {@link #millisecondOf(AggregationExpression)} instead.
* @deprecated since 3.0.7, use {@link #millisecondOf(AggregationExpression)} instead.
*/
@Deprecated
default T millisecondsOf(AggregationExpression expression) {
@@ -1897,7 +1897,7 @@ public class DateOperators {
* @param expression must not be {@literal null}.
* @return new instance.
* @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
* @since 3.2
* @since 3.0.7
*/
default T millisecondOf(AggregationExpression expression) {
return milliseconds(expression);

View File

@@ -149,12 +149,4 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
}
return null;
}
/**
* @return obtain the root context used to resolve references.
* @since 3.1
*/
AggregationOperationContext getRootContext() {
return rootContext;
}
}

View File

@@ -23,7 +23,6 @@ import java.util.List;
import org.bson.Document;
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.ScriptOperators.Accumulator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -376,17 +375,6 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
return newBuilder(GroupOps.STD_DEV_POP, null, expr);
}
/**
* Generates an {@link GroupOperationBuilder} for an {@code $accumulator}-expression.
*
* @param accumulator must not be {@literal null}.
* @return never {@literal null}.
* @since 3.2
*/
public GroupOperationBuilder accumulate(Accumulator accumulator) {
return new GroupOperationBuilder(this, new Operation(accumulator));
}
private GroupOperationBuilder newBuilder(Keyword keyword, @Nullable String reference, @Nullable Object value) {
return new GroupOperationBuilder(this, new Operation(keyword, null, reference, value));
}
@@ -477,16 +465,12 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
static class Operation implements AggregationOperation {
private final @Nullable Keyword op;
private final Keyword op;
private final @Nullable String key;
private final @Nullable String reference;
private final @Nullable Object value;
Operation(AggregationExpression expression) {
this(null, null, null, expression);
}
public Operation(@Nullable Keyword op, @Nullable String key, @Nullable String reference, @Nullable Object value) {
public Operation(Keyword op, @Nullable String key, @Nullable String reference, @Nullable Object value) {
this.op = op;
this.key = key;
@@ -503,12 +487,7 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
}
public Document toDocument(AggregationOperationContext context) {
Object value = getValue(context);
if(op == null && value instanceof Document) {
return new Document(key, value);
}
return new Document(key, new Document(op.toString(), value));
return new Document(key, new Document(op.toString(), getValue(context)));
}
public Object getValue(AggregationOperationContext context) {

View File

@@ -52,7 +52,7 @@ public class LimitOperation implements AggregationOperation {
return new Document(getOperator(), Long.valueOf(maxElements));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
@@ -56,7 +55,7 @@ public class RelaxedTypeBasedAggregationOperationContext extends TypeBasedAggreg
try {
return super.getReferenceFor(field);
} catch (MappingException e) {
} catch (InvalidPersistentPropertyPath e) {
return new DirectFieldReference(new ExposedField(field, true));
}
}

View File

@@ -1,587 +0,0 @@
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorBuilder;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorInitBuilder;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Gateway to {@literal $function} and {@literal $accumulator} aggregation operations.
* <p />
* Using {@link ScriptOperators} as part of the {@link Aggregation} requires MongoDB server to have
* <a href="https://docs.mongodb.com/master/core/server-side-javascript/">server-side JavaScript</a> execution
* <a href="https://docs.mongodb.com/master/reference/configuration-options/#security.javascriptEnabled">enabled</a>.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.1
*/
public class ScriptOperators {
/**
* Create a custom aggregation
* <a href="https://docs.mongodb.com/master/reference/operator/aggregation/function/">$function<a /> in JavaScript.
*
* @param body The function definition. Must not be {@literal null}.
* @return new instance of {@link Function}.
*/
public static Function function(String body) {
return Function.function(body);
}
/**
* Create a custom <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator
* operator</a> in Javascript.
*
* @return new instance of {@link AccumulatorInitBuilder}.
*/
public static AccumulatorInitBuilder accumulatorBuilder() {
return new AccumulatorBuilder();
}
/**
* {@link Function} defines a custom aggregation
* <a href="https://docs.mongodb.com/master/reference/operator/aggregation/function/">$function</a> in JavaScript.
* <p />
* <code class="java">
* {
* $function: {
* body: ...,
* args: ...,
* lang: "js"
* }
* }
* </code>
* <p />
* {@link Function} cannot be used as part of {@link org.springframework.data.mongodb.core.schema.MongoJsonSchema
* schema} validation query expression. <br />
* <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a>
* execution must be
* <a href="https://docs.mongodb.com/master/reference/configuration-options/#security.javascriptEnabled">enabled</a>
*
* @see <a href="https://docs.mongodb.com/master/reference/operator/aggregation/function/">MongoDB Documentation:
* $function</a>
*/
public static class Function extends AbstractAggregationExpression {
private Function(Map<String, Object> values) {
super(values);
}
/**
* Create a new {@link Function} with the given function definition.
*
* @param body must not be {@literal null}.
* @return new instance of {@link Function}.
*/
public static Function function(String body) {
Assert.notNull(body, "Function body must not be null!");
Map<String, Object> function = new LinkedHashMap<>(2);
function.put(Fields.BODY.toString(), body);
function.put(Fields.ARGS.toString(), Collections.emptyList());
function.put(Fields.LANG.toString(), "js");
return new Function(function);
}
/**
* Set the arguments passed to the function body.
*
* @param args the arguments passed to the function body. Leave empty if the function does not take any arguments.
* @return new instance of {@link Function}.
*/
public Function args(Object... args) {
return args(Arrays.asList(args));
}
/**
* Set the arguments passed to the function body.
*
* @param args the arguments passed to the function body. Leave empty if the function does not take any arguments.
* @return new instance of {@link Function}.
*/
public Function args(List<Object> args) {
Assert.notNull(args, "Args must not be null! Use an empty list instead.");
return new Function(appendAt(1, Fields.ARGS.toString(), args));
}
/**
* The language used in the body.
*
* @param lang must not be {@literal null} nor empty.
* @return new instance of {@link Function}.
*/
public Function lang(String lang) {
Assert.hasText(lang, "Lang must not be null nor empty! The default would be 'js'.");
return new Function(appendAt(2, Fields.LANG.toString(), lang));
}
@Nullable
List<Object> getArgs() {
return get(Fields.ARGS.toString());
}
String getBody() {
return get(Fields.BODY.toString());
}
String getLang() {
return get(Fields.LANG.toString());
}
@Override
protected String getMongoMethod() {
return "$function";
}
enum Fields {
BODY, ARGS, LANG;
@Override
public String toString() {
return name().toLowerCase();
}
}
}
/**
* {@link Accumulator} defines a custom aggregation
* <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator operator</a>,
* one that maintains its state (e.g. totals, maximums, minimums, and related data) as documents progress through the
* pipeline, in JavaScript.
* <p />
* <code class="java">
* {
* $accumulator: {
* init: ...,
* intArgs: ...,
* accumulate: ...,
* accumulateArgs: ...,
* merge: ...,
* finalize: ...,
* lang: "js"
* }
* }
* </code>
* <p />
* {@link Accumulator} can be used as part of {@link GroupOperation $group}, {@link BucketOperation $bucket} and
* {@link BucketAutoOperation $bucketAuto} pipeline stages. <br />
* <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a>
* execution must be
* <a href="https://docs.mongodb.com/master/reference/configuration-options/#security.javascriptEnabled">enabled</a>
*
* @see <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">MongoDB Documentation:
* $accumulator</a>
*/
public static class Accumulator extends AbstractAggregationExpression {
private Accumulator(Map<String, Object> value) {
super(value);
}
@Override
protected String getMongoMethod() {
return "$accumulator";
}
enum Fields {
ACCUMULATE("accumulate"), //
ACCUMULATE_ARGS("accumulateArgs"), //
FINALIZE("finalize"), //
INIT("init"), //
INIT_ARGS("initArgs"), //
LANG("lang"), //
MERGE("merge"); //
private String field;
Fields(String field) {
this.field = field;
}
@Override
public String toString() {
return field;
}
}
public interface AccumulatorInitBuilder {
/**
* Define the {@code init} {@link Function} for the {@link Accumulator accumulators} initial state. The function
* receives its arguments from the {@link Function#args(Object...) initArgs} array expression.
* <p />
* <code class="java">
* function(initArg1, initArg2, ...) {
* ...
* return initialState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
default AccumulatorAccumulateBuilder init(Function function) {
return init(function.getBody()).initArgs(function.getArgs());
}
/**
* Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives
* its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression.
* <p />
* <code class="java">
* function(initArg1, initArg2, ...) {
* ...
* return initialState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
AccumulatorInitArgsBuilder init(String function);
/**
* The language used in the {@code $accumulator} code.
*
* @param lang must not be {@literal null}. Default is {@literal js}.
* @return this.
*/
AccumulatorInitBuilder lang(String lang);
}
public interface AccumulatorInitArgsBuilder extends AccumulatorAccumulateBuilder {
/**
* Define the optional {@code initArgs} for the {@link AccumulatorInitBuilder#init(String)} function.
*
* @param args must not be {@literal null}.
* @return this.
*/
default AccumulatorAccumulateBuilder initArgs(Object... args) {
return initArgs(Arrays.asList(args));
}
/**
* Define the optional {@code initArgs} for the {@link AccumulatorInitBuilder#init(String)} function.
*
* @param args must not be {@literal null}.
* @return this.
*/
AccumulatorAccumulateBuilder initArgs(List<Object> args);
}
public interface AccumulatorAccumulateBuilder {
/**
* Set the {@code accumulate} {@link Function} that updates the state for each document. The functions first
* argument is the current {@code state}, additional arguments can be defined via {@link Function#args(Object...)
* accumulateArgs}.
* <p />
* <code class="java">
* function(state, accumArg1, accumArg2, ...) {
* ...
* return newState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
default AccumulatorMergeBuilder accumulate(Function function) {
return accumulate(function.getBody()).accumulateArgs(function.getArgs());
}
/**
* Set the {@code accumulate} function that updates the state for each document. The functions first argument is
* the current {@code state}, additional arguments can be defined via
* {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}.
* <p />
* <code class="java">
* function(state, accumArg1, accumArg2, ...) {
* ...
* return newState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
AccumulatorAccumulateArgsBuilder accumulate(String function);
}
public interface AccumulatorAccumulateArgsBuilder extends AccumulatorMergeBuilder {
/**
* Define additional {@code accumulateArgs} for the {@link AccumulatorAccumulateBuilder#accumulate(String)}
* function.
*
* @param args must not be {@literal null}.
* @return this.
*/
default AccumulatorMergeBuilder accumulateArgs(Object... args) {
return accumulateArgs(Arrays.asList(args));
}
/**
* Define additional {@code accumulateArgs} for the {@link AccumulatorAccumulateBuilder#accumulate(String)}
* function.
*
* @param args must not be {@literal null}.
* @return this.
*/
AccumulatorMergeBuilder accumulateArgs(List<Object> args);
}
public interface AccumulatorMergeBuilder {
/**
* Set the {@code merge} function used to merge two internal states. <br />
* This might be required because the operation is run on a sharded cluster or when the operator exceeds its
* memory limit.
* <p />
* <code class="java">
* function(state1, state2) {
* ...
* return newState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
AccumulatorFinalizeBuilder merge(String function);
}
public interface AccumulatorFinalizeBuilder {
/**
* Set the {@code finalize} function used to update the result of the accumulation when all documents have been
* processed.
* <p />
* <code class="java">
* function(state) {
* ...
* return finalState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return new instance of {@link Accumulator}.
*/
Accumulator finalize(String function);
/**
* Build the {@link Accumulator} object without specifying a {@link #finalize(String) finalize function}.
*
* @return new instance of {@link Accumulator}.
*/
Accumulator build();
}
static class AccumulatorBuilder
implements AccumulatorInitBuilder, AccumulatorInitArgsBuilder, AccumulatorAccumulateBuilder,
AccumulatorAccumulateArgsBuilder, AccumulatorMergeBuilder, AccumulatorFinalizeBuilder {
private List<Object> initArgs;
private String initFunction;
private List<Object> accumulateArgs;
private String accumulateFunction;
private String mergeFunction;
private String finalizeFunction;
private String lang = "js";
/**
* Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives
* its arguments from the {@link #initArgs(Object...)} array expression.
* <p />
* <code class="java">
* function(initArg1, initArg2, ...) {
* ...
* return initialState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
@Override
public AccumulatorBuilder init(String function) {
this.initFunction = function;
return this;
}
/**
* Define the optional {@code initArgs} for the {@link #init(String)} function.
*
* @param function must not be {@literal null}.
* @return this.
*/
@Override
public AccumulatorBuilder initArgs(List<Object> args) {
Assert.notNull(args, "Args must not be null");
this.initArgs = new ArrayList<>(args);
return this;
}
/**
* Set the {@code accumulate} function that updates the state for each document. The functions first argument is
* the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}.
* <p />
* <code class="java">
* function(state, accumArg1, accumArg2, ...) {
* ...
* return newState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
@Override
public AccumulatorBuilder accumulate(String function) {
Assert.notNull(function, "Accumulate function must not be null");
this.accumulateFunction = function;
return this;
}
/**
* Define additional {@code accumulateArgs} for the {@link #accumulate(String)} function.
*
* @param args must not be {@literal null}.
* @return this.
*/
@Override
public AccumulatorBuilder accumulateArgs(List<Object> args) {
Assert.notNull(args, "Args must not be null");
this.accumulateArgs = new ArrayList<>(args);
return this;
}
/**
* Set the {@code merge} function used to merge two internal states. <br />
* This might be required because the operation is run on a sharded cluster or when the operator exceeds its
* memory limit.
* <p />
* <code class="java">
* function(state1, state2) {
* ...
* return newState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return this.
*/
@Override
public AccumulatorBuilder merge(String function) {
Assert.notNull(function, "Merge function must not be null");
this.mergeFunction = function;
return this;
}
/**
* The language used in the {@code $accumulator} code.
*
* @param lang must not be {@literal null}. Default is {@literal js}.
* @return this.
*/
public AccumulatorBuilder lang(String lang) {
Assert.hasText(lang, "Lang must not be null nor empty! The default would be 'js'.");
this.lang = lang;
return this;
}
/**
* Set the {@code finalize} function used to update the result of the accumulation when all documents have been
* processed.
* <p />
* <code class="java">
* function(state) {
* ...
* return finalState
* }
* </code>
*
* @param function must not be {@literal null}.
* @return new instance of {@link Accumulator}.
*/
@Override
public Accumulator finalize(String function) {
Assert.notNull(function, "Finalize function must not be null");
this.finalizeFunction = function;
Map<String, Object> args = createArgumentMap();
args.put(Fields.FINALIZE.toString(), finalizeFunction);
return new Accumulator(args);
}
@Override
public Accumulator build() {
return new Accumulator(createArgumentMap());
}
private Map<String, Object> createArgumentMap() {
Map<String, Object> args = new LinkedHashMap<>();
args.put(Fields.INIT.toString(), initFunction);
if (!CollectionUtils.isEmpty(initArgs)) {
args.put(Fields.INIT_ARGS.toString(), initArgs);
}
args.put(Fields.ACCUMULATE.toString(), accumulateFunction);
if (!CollectionUtils.isEmpty(accumulateArgs)) {
args.put(Fields.ACCUMULATE_ARGS.toString(), accumulateArgs);
}
args.put(Fields.MERGE.toString(), mergeFunction);
args.put(Fields.LANG.toString(), lang);
return args;
}
}
}
}

View File

@@ -78,7 +78,7 @@ public class SortByCountOperation implements AggregationOperation {
: groupByExpression.toDocument(context));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/

View File

@@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
@@ -30,7 +29,6 @@ import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldRefe
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -48,7 +46,6 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
private final Class<?> type;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final QueryMapper mapper;
private final Lazy<MongoPersistentEntity<?>> entity;
/**
* Creates a new {@link TypeBasedAggregationOperationContext} for the given type, {@link MappingContext} and
@@ -68,7 +65,6 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
this.type = type;
this.mappingContext = mappingContext;
this.mapper = mapper;
this.entity = Lazy.of(() -> mappingContext.getPersistentEntity(type));
}
/*
@@ -137,37 +133,16 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
*/
@Override
public AggregationOperationContext continueOnMissingFieldReference() {
return continueOnMissingFieldReference(type);
}
/**
* This toggle allows the {@link AggregationOperationContext context} to use any given field name without checking for
* its existence. Typically the {@link AggregationOperationContext} fails when referencing unknown fields, those that
* are not present in one of the previous stages or the input source, throughout the pipeline.
*
* @param type The domain type to map fields to.
* @return a more relaxed {@link AggregationOperationContext}.
* @since 3.1
*/
public AggregationOperationContext continueOnMissingFieldReference(Class<?> type) {
return new RelaxedTypeBasedAggregationOperationContext(type, mappingContext, mapper);
}
protected FieldReference getReferenceFor(Field field) {
if(entity.getNullable() == null) {
return new DirectFieldReference(new ExposedField(field, true));
}
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext
.getPersistentPropertyPath(field.getTarget(), type);
.getPersistentPropertyPath(field.getTarget(), type);
Field mappedField = field(field.getName(),
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
return new DirectFieldReference(new ExposedField(mappedField, true));
}
public Class<?> getType() {
return type;
}
}

View File

@@ -1,164 +0,0 @@
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.Arrays;
import java.util.List;
import org.bson.Document;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* The <a href="https://docs.mongodb.com/master/reference/operator/aggregation/unionWith/">$unionWith</a> aggregation
* stage (available since MongoDB 4.4) performs a union of two collections by combining pipeline results, potentially
* containing duplicates, into a single result set that is handed over to the next stage. <br />
* In order to remove duplicates it is possible to append a {@link GroupOperation} right after
* {@link UnionWithOperation}.
* <p />
* If the {@link UnionWithOperation} uses a
* <a href="https://docs.mongodb.com/master/reference/operator/aggregation/unionWith/#unionwith-pipeline">pipeline</a>
* to process documents, field names within the pipeline will be treated as is. In order to map domain type property
* names to actual field names (considering potential {@link org.springframework.data.mongodb.core.mapping.Field}
* annotations) make sure the enclosing aggregation is a {@link TypedAggregation} and provide the target type for the
* {@code $unionWith} stage via {@link #mapFieldsTo(Class)}.
*
* @author Christoph Strobl
* @see <a href="https://docs.mongodb.com/master/reference/operator/aggregation/unionWith/">Aggregation Pipeline Stage:
* $unionWith</a>
* @since 3.1
*/
public class UnionWithOperation implements AggregationOperation {
private final String collection;
private final @Nullable AggregationPipeline pipeline;
private final @Nullable Class<?> domainType;
public UnionWithOperation(String collection, @Nullable AggregationPipeline pipeline, @Nullable Class<?> domainType) {
Assert.notNull(collection, "Collection must not be null!");
this.collection = collection;
this.pipeline = pipeline;
this.domainType = domainType;
}
/**
* Set the name of the collection from which pipeline results should be included in the result set.<br />
* The collection name is used to set the {@code coll} parameter of {@code $unionWith}.
*
* @param collection the MongoDB collection name. Must not be {@literal null}.
* @return new instance of {@link UnionWithOperation}.
* @throws IllegalArgumentException if the required argument is {@literal null}.
*/
public static UnionWithOperation unionWith(String collection) {
return new UnionWithOperation(collection, null, null);
}
/**
* Set the {@link AggregationPipeline} to apply to the specified collection. The pipeline corresponds to the optional
* {@code pipeline} field of the {@code $unionWith} aggregation stage and is used to compute the documents going into
* the result set.
*
* @param pipeline the {@link AggregationPipeline} that computes the documents. Must not be {@literal null}.
* @return new instance of {@link UnionWithOperation}.
* @throws IllegalArgumentException if the required argument is {@literal null}.
*/
public UnionWithOperation pipeline(AggregationPipeline pipeline) {
return new UnionWithOperation(collection, pipeline, domainType);
}
/**
* Set the aggregation pipeline stages to apply to the specified collection. The pipeline corresponds to the optional
* {@code pipeline} field of the {@code $unionWith} aggregation stage and is used to compute the documents going into
* the result set.
*
* @param aggregationStages the aggregation pipeline stages that compute the documents. Must not be {@literal null}.
* @return new instance of {@link UnionWithOperation}.
* @throws IllegalArgumentException if the required argument is {@literal null}.
*/
public UnionWithOperation pipeline(List<AggregationOperation> aggregationStages) {
return new UnionWithOperation(collection, new AggregationPipeline(aggregationStages), domainType);
}
/**
* Set the aggregation pipeline stages to apply to the specified collection. The pipeline corresponds to the optional
* {@code pipeline} field of the {@code $unionWith} aggregation stage and is used to compute the documents going into
* the result set.
*
* @param aggregationStages the aggregation pipeline stages that compute the documents. Must not be {@literal null}.
* @return new instance of {@link UnionWithOperation}.
* @throws IllegalArgumentException if the required argument is {@literal null}.
*/
public UnionWithOperation pipeline(AggregationOperation... aggregationStages) {
return new UnionWithOperation(collection, new AggregationPipeline(Arrays.asList(aggregationStages)), domainType);
}
/**
* Set domain type used for field name mapping of property references used by the {@link AggregationPipeline}.
* Remember to also use a {@link TypedAggregation} in the outer pipeline.<br />
* If not set, field names used within {@link AggregationOperation pipeline operations} are taken as is.
*
* @param domainType the domain type to map field names used in pipeline operations to. Must not be {@literal null}.
* @return new instance of {@link UnionWithOperation}.
* @throws IllegalArgumentException if the required argument is {@literal null}.
*/
public UnionWithOperation mapFieldsTo(Class<?> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new UnionWithOperation(collection, pipeline, domainType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
Document $unionWith = new Document("coll", collection);
if (pipeline == null || pipeline.isEmpty()) {
return new Document(getOperator(), $unionWith);
}
$unionWith.append("pipeline", pipeline.toDocuments(computeContext(context)));
return new Document(getOperator(), $unionWith);
}
private AggregationOperationContext computeContext(AggregationOperationContext source) {
if (source instanceof TypeBasedAggregationOperationContext) {
return ((TypeBasedAggregationOperationContext) source).continueOnMissingFieldReference(domainType != null ? domainType : Object.class);
}
if (source instanceof ExposedFieldsAggregationOperationContext) {
return computeContext(((ExposedFieldsAggregationOperationContext) source).getRootContext());
}
return source;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/
@Override
public String getOperator() {
return "$unionWith";
}
}

View File

@@ -108,7 +108,7 @@ public class UnwindOperation
return new Document(getOperator(), unwindArgs);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import org.bson.Document;
import org.bson.conversions.Bson;
@@ -67,19 +66,6 @@ class DocumentAccessor {
return this.document;
}
/**
* Copies all of the mappings from the given {@link Document} to the underlying target {@link Document}. These
* mappings will replace any mappings that the target document had for any of the keys currently in the specified map.
*
* @param source
*/
public void putAll(Document source) {
Map<String, Object> target = BsonUtils.asMap(document);
target.putAll(source);
}
/**
* Puts the given value into the backing {@link Document} based on the coordinates defined through the given
* {@link MongoPersistentProperty}. By default this will be the plain field name. But field names might also consist
@@ -133,7 +119,6 @@ class DocumentAccessor {
* @param entity must not be {@literal null}.
* @return
*/
@Nullable
public Object getRawId(MongoPersistentEntity<?> entity) {
return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.asMap(document).get("_id");
}

View File

@@ -26,7 +26,6 @@ import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -145,9 +144,9 @@ public interface MongoConverter
try {
return getConversionService().canConvert(id.getClass(), targetType)
? getConversionService().convert(id, targetType)
: convertToMongoType(id, (TypeInformation<?>) null);
: convertToMongoType(id, null);
} catch (ConversionException o_O) {
return convertToMongoType(id,(TypeInformation<?>) null);
return convertToMongoType(id, null);
}
}
}

View File

@@ -27,18 +27,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Currency;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.bson.BsonReader;
import org.bson.BsonTimestamp;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.types.Binary;
import org.bson.types.Code;
import org.bson.types.Decimal128;
@@ -52,12 +45,11 @@ import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mongodb.core.query.Term;
import org.springframework.data.mongodb.core.script.NamedMongoScript;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClientSettings;
/**
* Wrapper class to contain useful converters for the usage with Mongo.
*
@@ -244,27 +236,9 @@ abstract class MongoConverters {
INSTANCE;
private final Codec<Document> codec = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new Codec<UUID>() {
@Override
public void encode(BsonWriter writer, UUID value, EncoderContext encoderContext) {
writer.writeString(value.toString());
}
@Override
public Class<UUID> getEncoderClass() {
return UUID.class;
}
@Override
public UUID decode(BsonReader reader, DecoderContext decoderContext) {
throw new IllegalStateException("decode not supported");
}
}), MongoClientSettings.getDefaultCodecRegistry()).get(Document.class);
@Override
public String convert(Document source) {
return source.toJson(codec);
return source.toJson();
}
}
@@ -294,7 +268,7 @@ abstract class MongoConverters {
@Override
public NamedMongoScript convert(Document source) {
if (source.isEmpty()) {
if(source.isEmpty()) {
return null;
}

View File

@@ -40,7 +40,6 @@ import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.mongodb.core.query.UntypedExampleMatcher;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.support.ExampleMatcherAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
@@ -135,7 +134,7 @@ public class MongoExampleMapper {
while (iter.hasNext()) {
Map.Entry<String, Object> entry = iter.next();
String propertyPath = DotPath.from(path).append(entry.getKey()).toString();
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);
if (isEmptyIdProperty(entry)) {

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core.convert;
import org.bson.conversions.Bson;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
@@ -44,7 +43,7 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> {
*/
@Nullable
default Object convertToMongoType(@Nullable Object obj) {
return convertToMongoType(obj, (TypeInformation<?>) null);
return convertToMongoType(obj, null);
}
/**
@@ -58,9 +57,6 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> {
@Nullable
Object convertToMongoType(@Nullable Object obj, @Nullable TypeInformation<?> typeInformation);
default Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity<?> entity) {
return convertToMongoType(obj, entity.getTypeInformation());
}
/**
* Creates a {@link DBRef} to refer to the given object.
*

View File

@@ -36,14 +36,12 @@ import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.MongoExpression;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
@@ -142,23 +140,9 @@ public class QueryMapper {
try {
Field field = createPropertyField(entity, key, mappingContext);
Entry<String, Object> entry = getMappedObjectForField(field, BsonUtils.get(query, key));
// TODO: move to dedicated method
if (field.getProperty() != null && field.getProperty().isUnwrapped()) {
Object theNestedObject = BsonUtils.get(query, key);
Document mappedValue = (Document) getMappedValue(field, theNestedObject);
if (!StringUtils.hasText(field.getMappedKey())) {
result.putAll(mappedValue);
} else {
result.put(field.getMappedKey(), mappedValue);
}
} else {
Entry<String, Object> entry = getMappedObjectForField(field, BsonUtils.get(query, key));
result.put(entry.getKey(), entry.getValue());
}
result.put(entry.getKey(), entry.getValue());
} catch (InvalidPersistentPropertyPath invalidPathException) {
// in case the object has not already been mapped
@@ -189,16 +173,10 @@ public class QueryMapper {
return new Document();
}
sortObject = filterUnwrappedObjects(sortObject, entity);
Document mappedSort = new Document();
for (Map.Entry<String, Object> entry : BsonUtils.asMap(sortObject).entrySet()) {
Field field = createPropertyField(entity, entry.getKey(), mappingContext);
if (field.getProperty() != null && field.getProperty().isUnwrapped()) {
continue;
}
mappedSort.put(field.getMappedKey(), entry.getValue());
}
@@ -219,9 +197,7 @@ public class QueryMapper {
Assert.notNull(fieldsObject, "FieldsObject must not be null!");
fieldsObject = filterUnwrappedObjects(fieldsObject, entity);
Document mappedFields = getMappedObject(fieldsObject, entity);
Document mappedFields = fieldsObject.isEmpty() ? new Document() : getMappedObject(fieldsObject, entity);
mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
return mappedFields;
}
@@ -241,44 +217,6 @@ public class QueryMapper {
}
}
private Document filterUnwrappedObjects(Document fieldsObject, @Nullable MongoPersistentEntity<?> entity) {
if (fieldsObject.isEmpty() || entity == null) {
return fieldsObject;
}
Document target = new Document();
for (Entry<String, Object> field : fieldsObject.entrySet()) {
try {
PropertyPath path = PropertyPath.from(field.getKey(), entity.getTypeInformation());
PersistentPropertyPath<MongoPersistentProperty> persistentPropertyPath = mappingContext
.getPersistentPropertyPath(path);
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getRequiredLeafProperty();
if (property.isUnwrapped() && property.isEntity()) {
MongoPersistentEntity<?> unwrappedEntity = mappingContext.getRequiredPersistentEntity(property);
for (MongoPersistentProperty unwrappedProperty : unwrappedEntity) {
DotPath dotPath = DotPath.from(persistentPropertyPath.toDotPath()).append(unwrappedProperty.getName());
target.put(dotPath.toString(), field.getValue());
}
} else {
target.put(field.getKey(), field.getValue());
}
} catch (RuntimeException e) {
target.put(field.getKey(), field.getValue());
}
}
return target;
}
private Document getMappedTextScoreField(MongoPersistentProperty property) {
return new Document(property.getFieldName(), META_TEXT_SCORE);
}
@@ -295,10 +233,6 @@ public class QueryMapper {
String key = field.getMappedKey();
Object value;
if (rawValue instanceof MongoExpression) {
return createMapEntry(key, getMappedObject(((MongoExpression) rawValue).toDocument(), field.getEntity()));
}
if (isNestedKeyword(rawValue) && !field.isIdField()) {
Keyword keyword = new Keyword((Document) rawValue);
value = getMappedKeyword(field, keyword);
@@ -563,11 +497,6 @@ public class QueryMapper {
*/
@Nullable
protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
if (entity != null && entity.isUnwrapped()) {
return converter.convertToMongoType(source, entity);
}
return converter.convertToMongoType(source, entity == null ? null : entity.getTypeInformation());
}
@@ -938,11 +867,6 @@ public class QueryMapper {
return null;
}
@Nullable
MongoPersistentEntity<?> getEntity() {
return null;
}
/**
* Returns whether the field represents an association.
*
@@ -988,7 +912,6 @@ public class QueryMapper {
public TypeInformation<?> getTypeHint() {
return ClassTypeInformation.OBJECT;
}
}
/**
@@ -1095,12 +1018,6 @@ public class QueryMapper {
return property == null ? null : mappingContext.getPersistentEntity(property);
}
@Nullable
@Override
public MongoPersistentEntity<?> getEntity() {
return entity;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#isAssociation()

View File

@@ -132,11 +132,6 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
if(entity != null && entity.isUnwrapped()) {
return converter.convertToMongoType(source, entity);
}
return converter.convertToMongoType(source,
entity == null ? ClassTypeInformation.OBJECT : getTypeHintForEntity(source, entity));
}

View File

@@ -16,7 +16,7 @@
package org.springframework.data.mongodb.core.geo;
/**
* Interface definition for structures defined in <a href="https://geojson.org/>GeoJSON</a> format.
* Interface definition for structures defined in <a href="https://geojson.org/">GeoJSON</a> format.
*
* @author Christoph Strobl
* @since 1.7

View File

@@ -24,7 +24,7 @@ import org.springframework.data.geo.Point;
import org.springframework.lang.Nullable;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
@@ -34,10 +34,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
/**
* A Jackson {@link Module} to register custom {@link JsonDeserializer}s for GeoJSON types.
* <p />
* Use {@link #geoJsonModule()} to obtain a {@link Module} containing both {@link JsonSerializer serializers} and
* {@link JsonDeserializer deserializers}.
* A Jackson {@link Module} to register custom {@link JsonSerializer} and {@link JsonDeserializer}s for GeoJSON types.
*
* @author Christoph Strobl
* @author Oliver Gierke
@@ -50,87 +47,12 @@ public class GeoJsonModule extends SimpleModule {
public GeoJsonModule() {
registerDeserializersIn(this);
// TODO: add serializers as of next major version (4.0).
}
/**
* Obtain a {@link Module} containing {@link JsonDeserializer deserializers} for the following {@link GeoJson} types:
* <ul>
* <li>{@link GeoJsonPoint}</li>
* <li>{@link GeoJsonMultiPoint}</li>
* <li>{@link GeoJsonLineString}</li>
* <li>{@link GeoJsonMultiLineString}</li>
* <li>{@link GeoJsonPolygon}</li>
* <li>{@link GeoJsonMultiPolygon}</li>
* </ul>
*
* @return a {@link Module} containing {@link JsonDeserializer deserializers} for {@link GeoJson} types.
* @since 3.2
*/
public static Module deserializers() {
SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson - Deserializers",
new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson"));
registerDeserializersIn(module);
return module;
}
/**
* Obtain a {@link Module} containing {@link JsonSerializer serializers} for the following {@link GeoJson} types:
* <ul>
* <li>{@link GeoJsonPoint}</li>
* <li>{@link GeoJsonMultiPoint}</li>
* <li>{@link GeoJsonLineString}</li>
* <li>{@link GeoJsonMultiLineString}</li>
* <li>{@link GeoJsonPolygon}</li>
* <li>{@link GeoJsonMultiPolygon}</li>
* </ul>
*
* @return a {@link Module} containing {@link JsonSerializer serializers} for {@link GeoJson} types.
* @since 3.2
*/
public static Module serializers() {
SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson - Serializers",
new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson"));
GeoJsonSerializersModule.registerSerializersIn(module);
return module;
}
/**
* Obtain a {@link Module} containing {@link JsonSerializer serializers} and {@link JsonDeserializer deserializers}
* for the following {@link GeoJson} types:
* <ul>
* <li>{@link GeoJsonPoint}</li>
* <li>{@link GeoJsonMultiPoint}</li>
* <li>{@link GeoJsonLineString}</li>
* <li>{@link GeoJsonMultiLineString}</li>
* <li>{@link GeoJsonPolygon}</li>
* <li>{@link GeoJsonMultiPolygon}</li>
* </ul>
*
* @return a {@link Module} containing {@link JsonSerializer serializers} and {@link JsonDeserializer deserializers}
* for {@link GeoJson} types.
* @since 3.2
*/
public static Module geoJsonModule() {
SimpleModule module = new SimpleModule("Spring Data MongoDB GeoJson",
new Version(3, 2, 0, null, "org.springframework.data", "spring-data-mongodb-geojson"));
GeoJsonSerializersModule.registerSerializersIn(module);
registerDeserializersIn(module);
return module;
}
private static void registerDeserializersIn(SimpleModule module) {
module.addDeserializer(GeoJsonPoint.class, new GeoJsonPointDeserializer());
module.addDeserializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointDeserializer());
module.addDeserializer(GeoJsonLineString.class, new GeoJsonLineStringDeserializer());
module.addDeserializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringDeserializer());
module.addDeserializer(GeoJsonPolygon.class, new GeoJsonPolygonDeserializer());
module.addDeserializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonDeserializer());
addDeserializer(GeoJsonPoint.class, new GeoJsonPointDeserializer());
addDeserializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointDeserializer());
addDeserializer(GeoJsonLineString.class, new GeoJsonLineStringDeserializer());
addDeserializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringDeserializer());
addDeserializer(GeoJsonPolygon.class, new GeoJsonPolygonDeserializer());
addDeserializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonDeserializer());
}
/**
@@ -145,7 +67,8 @@ public class GeoJsonModule extends SimpleModule {
*/
@Nullable
@Override
public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException {
public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.readValueAsTree();
JsonNode coordinates = node.get("coordinates");
@@ -211,7 +134,7 @@ public class GeoJsonModule extends SimpleModule {
return Collections.emptyList();
}
List<Point> points = new ArrayList<>(node.size());
List<Point> points = new ArrayList<Point>(node.size());
for (JsonNode coordinatePair : node) {
if (coordinatePair.isArray()) {
@@ -222,7 +145,7 @@ public class GeoJsonModule extends SimpleModule {
}
protected GeoJsonLineString toLineString(ArrayNode node) {
return new GeoJsonLineString(toPoints(node));
return new GeoJsonLineString(toPoints((ArrayNode) node));
}
}
@@ -336,7 +259,7 @@ public class GeoJsonModule extends SimpleModule {
@Override
protected GeoJsonMultiLineString doDeserialize(ArrayNode coordinates) {
List<GeoJsonLineString> lines = new ArrayList<>(coordinates.size());
List<GeoJsonLineString> lines = new ArrayList<GeoJsonLineString>(coordinates.size());
for (JsonNode lineString : coordinates) {
if (lineString.isArray()) {
@@ -413,7 +336,7 @@ public class GeoJsonModule extends SimpleModule {
@Override
protected GeoJsonMultiPolygon doDeserialize(ArrayNode coordinates) {
List<GeoJsonPolygon> polygones = new ArrayList<>(coordinates.size());
List<GeoJsonPolygon> polygones = new ArrayList<GeoJsonPolygon>(coordinates.size());
for (JsonNode polygon : coordinates) {
for (JsonNode ring : polygon) {

View File

@@ -1,313 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.io.IOException;
import org.springframework.data.geo.Point;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* A Jackson {@link Module} to register custom {@link JsonSerializer}s for GeoJSON types.
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
class GeoJsonSerializersModule extends SimpleModule {
private static final long serialVersionUID = 1340494654898895610L;
GeoJsonSerializersModule() {
registerSerializersIn(this);
}
static void registerSerializersIn(SimpleModule module) {
module.addSerializer(GeoJsonPoint.class, new GeoJsonPointSerializer());
module.addSerializer(GeoJsonMultiPoint.class, new GeoJsonMultiPointSerializer());
module.addSerializer(GeoJsonLineString.class, new GeoJsonLineStringSerializer());
module.addSerializer(GeoJsonMultiLineString.class, new GeoJsonMultiLineStringSerializer());
module.addSerializer(GeoJsonPolygon.class, new GeoJsonPolygonSerializer());
module.addSerializer(GeoJsonMultiPolygon.class, new GeoJsonMultiPolygonSerializer());
}
/**
* @param <T>
* @author Christoph Strobl
*/
private static abstract class GeoJsonSerializer<T extends GeoJson<? extends Iterable>> extends JsonSerializer<T> {
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.JsonSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
*/
@Override
public void serialize(T shape, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("type", shape.getType());
jsonGenerator.writeArrayFieldStart("coordinates");
doSerialize(shape, jsonGenerator);
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}
/**
* Perform the actual serialization given the {@literal shape} as {@link GeoJson}.
*
* @param shape
* @param jsonGenerator
* @return
*/
protected abstract void doSerialize(T shape, JsonGenerator jsonGenerator) throws IOException;
/**
* Write a {@link Point} as array. <br />
* {@code [10.0, 20.0]}
*
* @param point
* @param jsonGenerator
* @throws IOException
*/
protected void writePoint(Point point, JsonGenerator jsonGenerator) throws IOException {
jsonGenerator.writeStartArray();
writeRawCoordinates(point, jsonGenerator);
jsonGenerator.writeEndArray();
}
/**
* Write the {@link Point} coordinates. <br />
* {@code 10.0, 20.0}
*
* @param point
* @param jsonGenerator
* @throws IOException
*/
protected void writeRawCoordinates(Point point, JsonGenerator jsonGenerator) throws IOException {
jsonGenerator.writeNumber(point.getX());
jsonGenerator.writeNumber(point.getY());
}
/**
* Write an {@link Iterable} of {@link Point} as array. <br />
* {@code [ [10.0, 20.0], [30.0, 40.0], [50.0, 60.0] ]}
*
* @param points
* @param jsonGenerator
* @throws IOException
*/
protected void writeLine(Iterable<Point> points, JsonGenerator jsonGenerator) throws IOException {
jsonGenerator.writeStartArray();
writeRawLine(points, jsonGenerator);
jsonGenerator.writeEndArray();
}
/**
* Write an {@link Iterable} of {@link Point}. <br />
* {@code [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]}
*
* @param points
* @param jsonGenerator
* @throws IOException
*/
protected void writeRawLine(Iterable<Point> points, JsonGenerator jsonGenerator) throws IOException {
for (Point point : points) {
writePoint(point, jsonGenerator);
}
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonPoint} to:
*
* <pre>
* <code>
* { "type": "Point", "coordinates": [10.0, 20.0] }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonPointSerializer extends GeoJsonSerializer<GeoJsonPoint> {
@Override
protected void doSerialize(GeoJsonPoint value, JsonGenerator jsonGenerator) throws IOException {
writeRawCoordinates(value, jsonGenerator);
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonLineString} to:
*
* <pre>
* <code>
* {
* "type": "LineString",
* "coordinates": [
* [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]
* ]
* }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonLineStringSerializer extends GeoJsonSerializer<GeoJsonLineString> {
@Override
protected void doSerialize(GeoJsonLineString value, JsonGenerator jsonGenerator) throws IOException {
writeRawLine(value.getCoordinates(), jsonGenerator);
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonMultiPoint} to:
*
* <pre>
* <code>
* {
* "type": "MultiPoint",
* "coordinates": [
* [10.0, 20.0], [30.0, 40.0], [50.0, 60.0]
* ]
* }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonMultiPointSerializer extends GeoJsonSerializer<GeoJsonMultiPoint> {
@Override
protected void doSerialize(GeoJsonMultiPoint value, JsonGenerator jsonGenerator) throws IOException {
writeRawLine(value.getCoordinates(), jsonGenerator);
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonMultiLineString} to:
*
* <pre>
* <code>
* {
* "type": "MultiLineString",
* "coordinates": [
* [ [10.0, 20.0], [30.0, 40.0] ],
* [ [50.0, 60.0] , [70.0, 80.0] ]
* ]
* }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonMultiLineStringSerializer extends GeoJsonSerializer<GeoJsonMultiLineString> {
@Override
protected void doSerialize(GeoJsonMultiLineString value, JsonGenerator jsonGenerator) throws IOException {
for (GeoJsonLineString lineString : value.getCoordinates()) {
writeLine(lineString.getCoordinates(), jsonGenerator);
}
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonPolygon} to:
*
* <pre>
* <code>
* {
* "type": "Polygon",
* "coordinates": [
* [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
* ]
* }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonPolygonSerializer extends GeoJsonSerializer<GeoJsonPolygon> {
@Override
protected void doSerialize(GeoJsonPolygon value, JsonGenerator jsonGenerator) throws IOException {
for (GeoJsonLineString lineString : value.getCoordinates()) {
writeLine(lineString.getCoordinates(), jsonGenerator);
}
}
}
/**
* {@link JsonSerializer} converting {@link GeoJsonMultiPolygon} to:
*
* <pre>
* <code>
* {
* "type": "MultiPolygon",
* "coordinates": [
* [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
* [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
* [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
* ]
* }
* </code>
* </pre>
*
* @author Bjorn Harvold
* @author Christoph Strobl
* @since 3.2
*/
static class GeoJsonMultiPolygonSerializer extends GeoJsonSerializer<GeoJsonMultiPolygon> {
@Override
protected void doSerialize(GeoJsonMultiPolygon value, JsonGenerator jsonGenerator) throws IOException {
for (GeoJsonPolygon polygon : value.getCoordinates()) {
jsonGenerator.writeStartArray();
for (GeoJsonLineString lineString : polygon.getCoordinates()) {
writeLine(lineString.getCoordinates(), jsonGenerator);
}
jsonGenerator.writeEndArray();
}
}
}
}

View File

@@ -46,7 +46,6 @@ import java.lang.annotation.Target;
* @author Philipp Schneider
* @author Johno Crawford
* @author Christoph Strobl
* @author Dave Perryman
*/
@Target({ ElementType.TYPE })
@Documented
@@ -96,8 +95,7 @@ public @interface CompoundIndex {
boolean unique() default false;
/**
* If set to true index will skip over any document that is missing the indexed field. <br />
* Must not be used with {@link #partialFilter()}.
* If set to true index will skip over any document that is missing the indexed field.
*
* @return {@literal false} by default.
* @see <a href=
@@ -172,14 +170,4 @@ public @interface CompoundIndex {
*/
boolean background() default false;
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}. <br />
* Must not be used with {@link #sparse() sparse = true}.
*
* @return empty by default.
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 3.1
*/
String partialFilter() default "";
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.mongodb.core.index;
import org.springframework.lang.Nullable;
/**
* Provider interface to obtain {@link IndexOperations} by MongoDB collection name.
*
@@ -28,22 +26,10 @@ import org.springframework.lang.Nullable;
public interface IndexOperationsProvider {
/**
* Returns the operations that can be performed on indexes.
* Returns the operations that can be performed on indexes
*
* @param collectionName name of the MongoDB collection, must not be {@literal null}.
* @return index operations on the named collection
*/
default IndexOperations indexOps(String collectionName) {
return indexOps(collectionName, null);
}
/**
* Returns the operations that can be performed on indexes.
*
* @param collectionName name of the MongoDB collection, must not be {@literal null}.
* @param type the type used for field mapping. Can be {@literal null}.
* @return index operations on the named collection
* @since 3.2
*/
IndexOperations indexOps(String collectionName, @Nullable Class<?> type);
IndexOperations indexOps(String collectionName);
}

View File

@@ -53,8 +53,7 @@ public @interface Indexed {
IndexDirection direction() default IndexDirection.ASCENDING;
/**
* If set to true index will skip over any document that is missing the indexed field. <br />
* Must not be used with {@link #partialFilter()}.
* If set to true index will skip over any document that is missing the indexed field.
*
* @return {@literal false} by default.
* @see <a href=
@@ -171,15 +170,4 @@ public @interface Indexed {
* @since 2.2
*/
String expireAfter() default "";
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}. <br />
* Must not be used with {@link #sparse() sparse = true}.
*
* @return empty by default.
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 3.1
*/
String partialFilter() default "";
}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core.index;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,6 +33,7 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.Association;
@@ -46,8 +51,6 @@ import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
@@ -71,7 +74,6 @@ import org.springframework.util.StringUtils;
* @author Thomas Darimont
* @author Martin Macko
* @author Mark Paluch
* @author Dave Perryman
* @since 1.5
*/
public class MongoPersistentEntityIndexResolver implements IndexResolver {
@@ -136,9 +138,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
try {
if (persistentProperty.isEntity()) {
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty),
root.getCollection(), guard));
indexes.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getActualType(),
persistentProperty.getFieldName(), Path.of(persistentProperty), root.getCollection(), guard));
}
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(
@@ -162,16 +163,12 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
* @return List of {@link IndexDefinitionHolder} representing indexes for given type and its referenced property
* types. Will never be {@code null}.
*/
private List<IndexDefinitionHolder> resolveIndexForClass( TypeInformation<?> type, String dotPath,
Path path, String collection, CycleGuard guard) {
private List<IndexDefinitionHolder> resolveIndexForClass(final TypeInformation<?> type, final String dotPath,
final Path path, final String collection, final CycleGuard guard) {
return resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(type), dotPath, path, collection, guard);
}
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
private List<IndexDefinitionHolder> resolveIndexForEntity(MongoPersistentEntity<?> entity, String dotPath,
Path path, String collection, CycleGuard guard) {
List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
final List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(dotPath, collection, entity));
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> this
@@ -185,25 +182,21 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty persistentProperty, String dotPath,
Path path, String collection, List<IndexDefinitionHolder> indexes, CycleGuard guard) {
DotPath propertyDotPath = DotPath.from(dotPath);
if (!persistentProperty.isUnwrapped()) {
propertyDotPath = propertyDotPath.append(persistentProperty.getFieldName());
}
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "") + persistentProperty.getFieldName();
Path propertyPath = path.append(persistentProperty);
guard.protect(persistentProperty, propertyPath);
if (persistentProperty.isEntity()) {
try {
indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), propertyDotPath.toString(),
indexes.addAll(resolveIndexForClass(persistentProperty.getTypeInformation().getActualType(), propertyDotPath,
propertyPath, collection, guard));
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage());
}
}
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath.toString(), collection,
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
persistentProperty);
if (!indexDefinitions.isEmpty()) {
@@ -216,13 +209,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
List<IndexDefinitionHolder> indices = new ArrayList<>(2);
if (persistentProperty.isUnwrapped() && (persistentProperty.isAnnotationPresent(Indexed.class)
|| persistentProperty.isAnnotationPresent(HashIndexed.class)
|| persistentProperty.isAnnotationPresent(GeoSpatialIndexed.class))) {
throw new InvalidDataAccessApiUsageException(
String.format("Index annotation not allowed on unwrapped object for path '%s'.", dotPath));
}
if (persistentProperty.isAnnotationPresent(Indexed.class)) {
indices.add(createIndexDefinition(dotPath, collection, persistentProperty));
} else if (persistentProperty.isAnnotationPresent(GeoSpatialIndexed.class)) {
@@ -271,7 +257,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
try {
appendTextIndexInformation(DotPath.empty(), Path.empty(), indexDefinitionBuilder, root,
appendTextIndexInformation("", Path.empty(), indexDefinitionBuilder, root,
new TextIndexIncludeOptions(IncludeStrategy.DEFAULT), new CycleGuard());
} catch (CyclicPropertyReferenceException e) {
LOGGER.info(e.getMessage());
@@ -292,9 +278,9 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
private void appendTextIndexInformation(DotPath dotPath, Path path,
TextIndexDefinitionBuilder indexDefinitionBuilder, MongoPersistentEntity<?> entity,
TextIndexIncludeOptions includeOptions, CycleGuard guard) {
private void appendTextIndexInformation(final String dotPath, final Path path,
final TextIndexDefinitionBuilder indexDefinitionBuilder, final MongoPersistentEntity<?> entity,
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
@@ -303,7 +289,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
guard.protect(persistentProperty, path);
if (persistentProperty.isExplicitLanguageProperty() && dotPath.isEmpty()) {
if (persistentProperty.isExplicitLanguageProperty() && !StringUtils.hasText(dotPath)) {
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
}
@@ -311,8 +297,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
if (includeOptions.isForce() || indexed != null || persistentProperty.isEntity()) {
DotPath propertyDotPath = dotPath
.append(persistentProperty.getFieldName());
String propertyDotPath = (StringUtils.hasText(dotPath) ? dotPath + "." : "")
+ persistentProperty.getFieldName();
Path propertyPath = path.append(persistentProperty);
@@ -325,7 +311,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
TextIndexIncludeOptions optionsForNestedType = includeOptions;
if (!IncludeStrategy.FORCE.equals(includeOptions.getStrategy()) && indexed != null) {
optionsForNestedType = new TextIndexIncludeOptions(IncludeStrategy.FORCE,
new TextIndexedFieldSpec(propertyDotPath.toString(), weight));
new TextIndexedFieldSpec(propertyDotPath, weight));
}
try {
@@ -338,7 +324,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
entity.getName()), e);
}
} else if (includeOptions.isForce() || indexed != null) {
indexDefinitionBuilder.onField(propertyDotPath.toString(), weight);
indexDefinitionBuilder.onField(propertyDotPath, weight);
}
}
@@ -399,10 +385,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
indexDefinition.background();
}
if (StringUtils.hasText(index.partialFilter())) {
indexDefinition.partial(evaluatePartialFilter(index.partialFilter(), entity));
}
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
}
@@ -492,24 +474,9 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
}
}
if (StringUtils.hasText(index.partialFilter())) {
indexDefinition.partial(evaluatePartialFilter(index.partialFilter(), persistentProperty.getOwner()));
}
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
}
private PartialIndexFilter evaluatePartialFilter(String filterExpression, PersistentEntity<?, ?> entity) {
Object result = evaluate(filterExpression, getEvaluationContextForProperty(entity));
if (result instanceof org.bson.Document) {
return PartialIndexFilter.of((org.bson.Document) result);
}
return PartialIndexFilter.of(BsonUtils.parse(filterExpression, null));
}
/**
* Creates {@link HashedIndex} wrapped in {@link IndexDefinitionHolder} out of {@link HashIndexed} for a given
* {@link MongoPersistentProperty}.
@@ -649,7 +616,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
MongoPersistentProperty property = association.getInverse();
DotPath propertyDotPath = DotPath.from(path).append(property.getFieldName());
String propertyDotPath = (StringUtils.hasText(path) ? path + "." : "") + property.getFieldName();
if (property.isAnnotationPresent(GeoSpatialIndexed.class) || property.isAnnotationPresent(TextIndexed.class)) {
throw new MappingException(
@@ -657,7 +624,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
propertyDotPath));
}
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath.toString(), collection,
List<IndexDefinitionHolder> indexDefinitions = createIndexDefinitionHolderForProperty(propertyDotPath, collection,
property);
if (!indexDefinitions.isEmpty()) {
@@ -766,6 +733,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
* @author Christoph Strobl
* @author Mark Paluch
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
static class Path {
private static final Path EMPTY = new Path(Collections.emptyList(), false);
@@ -773,11 +742,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
private final List<PersistentProperty<?>> elements;
private final boolean cycle;
private Path(List<PersistentProperty<?>> elements, boolean cycle) {
this.elements = elements;
this.cycle = cycle;
}
/**
* @return an empty {@link Path}.
* @since 1.10.8
@@ -879,28 +843,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
return builder.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Path that = (Path) o;
if (this.cycle != that.cycle) {
return false;
}
return ObjectUtils.nullSafeEquals(this.elements, that.elements);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(elements);
result = 31 * result + (cycle ? 1 : 0);
return result;
}
}
}

View File

@@ -16,8 +16,14 @@
package org.springframework.data.mongodb.core.index;
import org.bson.Document;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* {@link IndexFilter} implementation for usage with plain {@link Document} as well as {@link CriteriaDefinition} filter
@@ -26,16 +32,10 @@ import org.springframework.util.Assert;
* @author Christoph Strobl
* @since 1.10
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PartialIndexFilter implements IndexFilter {
private final Object filterExpression;
private PartialIndexFilter(Object filterExpression) {
Assert.notNull(filterExpression, "FilterExpression must not be null!");
this.filterExpression = filterExpression;
}
private final @NonNull Object filterExpression;
/**
* Create new {@link PartialIndexFilter} for given {@link Document filter expression}.

View File

@@ -164,8 +164,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
@Override
public org.springframework.data.mongodb.core.query.Collation getCollation() {
Object collationValue = collationExpression != null
? collationExpression.getValue(getEvaluationContext(null), String.class)
Object collationValue = collationExpression != null ? collationExpression.getValue(getEvaluationContext(null), String.class)
: this.collation;
if (collationValue == null) {

View File

@@ -41,7 +41,7 @@ public @interface Document {
* The collection the document representing the entity is supposed to be stored in. If not configured, a default
* collection name will be derived from the type's name. The attribute supports SpEL expressions to dynamically
* calculate the collection to based on a per operation basis.
*
*
* @return the name of the collection to be used.
*/
@AliasFor("collection")
@@ -51,7 +51,7 @@ public @interface Document {
* The collection the document representing the entity is supposed to be stored in. If not configured, a default
* collection name will be derived from the type's name. The attribute supports SpEL expressions to dynamically
* calculate the collection to based on a per operation basis.
*
*
* @return the name of the collection to be used.
*/
@AliasFor("value")

View File

@@ -35,9 +35,8 @@ import org.springframework.lang.Nullable;
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class MongoMappingContext extends AbstractMappingContext<MongoPersistentEntity<?>, MongoPersistentProperty>
public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersistentEntity<?>, MongoPersistentProperty>
implements ApplicationContextAware {
private static final FieldNamingStrategy DEFAULT_NAMING_STRATEGY = PropertyNameFieldNamingStrategy.INSTANCE;
@@ -77,7 +76,7 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder)
*/
@Override
public MongoPersistentProperty createPersistentProperty(Property property, MongoPersistentEntity<?> owner,
public MongoPersistentProperty createPersistentProperty(Property property, BasicMongoPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
return new CachingMongoPersistentProperty(property, owner, simpleTypeHolder, fieldNamingStrategy);
}
@@ -88,7 +87,7 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
*/
@Override
protected <T> BasicMongoPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
return new BasicMongoPersistentEntity<>(typeInformation);
return new BasicMongoPersistentEntity<T>(typeInformation);
}
/*
@@ -97,6 +96,7 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
super.setApplicationContext(applicationContext);
}
@@ -126,17 +126,4 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
public void setAutoIndexCreation(boolean autoCreateIndexes) {
this.autoIndexCreation = autoCreateIndexes;
}
@Nullable
@Override
public MongoPersistentEntity<?> getPersistentEntity(MongoPersistentProperty persistentProperty) {
MongoPersistentEntity<?> entity = super.getPersistentEntity(persistentProperty);
if(entity == null || !persistentProperty.isUnwrapped()) {
return entity;
}
return new UnwrappedMongoPersistentEntity<>(entity, new UnwrapEntityContext(persistentProperty));
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.data.mongodb.core.mapping;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.MutablePersistentEntity;
import org.springframework.lang.Nullable;
/**
@@ -25,7 +24,7 @@ import org.springframework.lang.Nullable;
* @author Oliver Gierke
* @author Christoph Strobl
*/
public interface MongoPersistentEntity<T> extends MutablePersistentEntity<T, MongoPersistentProperty> {
public interface MongoPersistentEntity<T> extends PersistentEntity<T, MongoPersistentProperty> {
/**
* Returns the collection the entity shall be persisted to.
@@ -94,12 +93,4 @@ public interface MongoPersistentEntity<T> extends MutablePersistentEntity<T, Mon
return getShardKey().isSharded();
}
/**
* @return {@literal true} if the entity should be unwrapped.
* @since 3.2
*/
default boolean isUnwrapped() {
return false;
}
}

View File

@@ -123,14 +123,6 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
return field != null ? !FieldType.IMPLICIT.equals(field.targetType()) : false;
}
/**
* @return {@literal true} if the property should be unwrapped.
* @since 3.2
*/
default boolean isUnwrapped() {
return isEntity() && isAnnotationPresent(Unwrapped.class);
}
/**
* Simple {@link Converter} implementation to transform a {@link MongoPersistentProperty} into its field name.
*
@@ -145,10 +137,7 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public String convert(MongoPersistentProperty source) {
if (!source.isUnwrapped()) {
return source.getFieldName();
}
return "";
return source.getFieldName();
}
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
/**
* @author Christoph Strobl
* @since 3.2
*/
class UnwrapEntityContext {
private final MongoPersistentProperty property;
public UnwrapEntityContext(MongoPersistentProperty property) {
this.property = property;
}
public MongoPersistentProperty getProperty() {
return property;
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.meta.When;
import org.springframework.core.annotation.AliasFor;
/**
* The annotation to configure a value object as flattened out in the target document.
* <p />
* Depending on the {@link OnEmpty value} of {@link #onEmpty()} the property is set to {@literal null} or an empty
* instance in the case all unwrapped values are {@literal null} when reading from the result set.
*
* @author Christoph Strobl
* @since 3.2
*/
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
public @interface Unwrapped {
/**
* Set the load strategy for the unwrapped object if all contained fields yield {@literal null} values.
* <p />
* {@link Nullable @Unwrapped.Nullable} and {@link Empty @Unwrapped.Empty} offer shortcuts for this.
*
* @return never {@link} null.
*/
OnEmpty onEmpty();
/**
* @return prefix for columns in the unwrapped value object. An empty {@link String} by default.
*/
String prefix() default "";
/**
* Load strategy to be used {@link Unwrapped#onEmpty()}.
*
* @author Christoph Strobl
*/
enum OnEmpty {
USE_NULL, USE_EMPTY
}
/**
* Shortcut for a nullable unwrapped property.
*
* <pre class="code">
* &#64;Unwrapped.Nullable private Address address;
* </pre>
*
* as alternative to the more verbose
*
* <pre class="code">
* &#64;Unwrapped(onEmpty = USE_NULL) &#64;javax.annotation.Nonnull(when = When.MAYBE) private Address address;
* </pre>
*
* @author Christoph Strobl
* @see Unwrapped#onEmpty()
*/
@Unwrapped(onEmpty = OnEmpty.USE_NULL)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
@javax.annotation.Nonnull(when = When.MAYBE)
@interface Nullable {
/**
* @return prefix for columns in the unwrapped value object. An empty {@link String} by default.
*/
@AliasFor(annotation = Unwrapped.class, attribute = "prefix")
String prefix() default "";
/**
* @return value for columns in the unwrapped value object. An empty {@link String} by default.
*/
@AliasFor(annotation = Unwrapped.class, attribute = "prefix")
String value() default "";
}
/**
* Shortcut for an empty unwrapped property.
*
* <pre class="code">
* &#64;Unwrapped.Empty private Address address;
* </pre>
*
* as alternative to the more verbose
*
* <pre class="code">
* &#64;Unwrapped(onEmpty = USE_EMPTY) &#64;javax.annotation.Nonnull(when = When.NEVER) private Address address;
* </pre>
*
* @author Christoph Strobl
* @see Unwrapped#onEmpty()
*/
@Unwrapped(onEmpty = OnEmpty.USE_EMPTY)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
@javax.annotation.Nonnull(when = When.NEVER)
@interface Empty {
/**
* @return prefix for columns in the unwrapped value object. An empty {@link String} by default.
*/
@AliasFor(annotation = Unwrapped.class, attribute = "prefix")
String prefix() default "";
/**
* @return value for columns in the unwrapped value object. An empty {@link String} by default.
*/
@AliasFor(annotation = Unwrapped.class, attribute = "prefix")
String value() default "";
}
}

View File

@@ -1,326 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.springframework.data.mapping.*;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* Unwrapped variant of {@link MongoPersistentEntity}.
*
* @author Christoph Strobl
* @since 3.2
* @see Unwrapped
*/
class UnwrappedMongoPersistentEntity<T> implements MongoPersistentEntity<T> {
private final UnwrapEntityContext context;
private final MongoPersistentEntity<T> delegate;
public UnwrappedMongoPersistentEntity(MongoPersistentEntity<T> delegate, UnwrapEntityContext context) {
this.context = context;
this.delegate = delegate;
}
@Override
public String getCollection() {
return delegate.getCollection();
}
@Override
public String getLanguage() {
return delegate.getLanguage();
}
@Override
@Nullable
public MongoPersistentProperty getTextScoreProperty() {
return delegate.getTextScoreProperty();
}
@Override
public boolean hasTextScoreProperty() {
return delegate.hasTextScoreProperty();
}
@Override
@Nullable
public Collation getCollation() {
return delegate.getCollation();
}
@Override
public boolean hasCollation() {
return delegate.hasCollation();
}
@Override
public ShardKey getShardKey() {
return delegate.getShardKey();
}
@Override
public boolean isSharded() {
return delegate.isSharded();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
@Nullable
public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
return delegate.getPersistenceConstructor();
}
@Override
public boolean isConstructorArgument(PersistentProperty<?> property) {
return delegate.isConstructorArgument(property);
}
@Override
public boolean isIdProperty(PersistentProperty<?> property) {
return delegate.isIdProperty(property);
}
@Override
public boolean isVersionProperty(PersistentProperty<?> property) {
return delegate.isVersionProperty(property);
}
@Override
@Nullable
public MongoPersistentProperty getIdProperty() {
return delegate.getIdProperty();
}
@Override
public MongoPersistentProperty getRequiredIdProperty() {
return delegate.getRequiredIdProperty();
}
@Override
@Nullable
public MongoPersistentProperty getVersionProperty() {
return delegate.getVersionProperty();
}
@Override
public MongoPersistentProperty getRequiredVersionProperty() {
return delegate.getRequiredVersionProperty();
}
@Override
@Nullable
public MongoPersistentProperty getPersistentProperty(String name) {
return wrap(delegate.getPersistentProperty(name));
}
@Override
public MongoPersistentProperty getRequiredPersistentProperty(String name) {
MongoPersistentProperty persistentProperty = getPersistentProperty(name);
if (persistentProperty != null) {
return persistentProperty;
}
throw new IllegalStateException(String.format("Required property %s not found for %s!", name, getType()));
}
@Override
@Nullable
public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
return wrap(delegate.getPersistentProperty(annotationType));
}
@Override
public Iterable<MongoPersistentProperty> getPersistentProperties(Class<? extends Annotation> annotationType) {
return Streamable.of(delegate.getPersistentProperties(annotationType)).stream().map(this::wrap)
.collect(Collectors.toList());
}
@Override
public boolean hasIdProperty() {
return delegate.hasIdProperty();
}
@Override
public boolean hasVersionProperty() {
return delegate.hasVersionProperty();
}
@Override
public Class<T> getType() {
return delegate.getType();
}
@Override
public Alias getTypeAlias() {
return delegate.getTypeAlias();
}
@Override
public TypeInformation<T> getTypeInformation() {
return delegate.getTypeInformation();
}
@Override
public void doWithProperties(PropertyHandler<MongoPersistentProperty> handler) {
delegate.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> {
handler.doWithPersistentProperty(wrap(property));
});
}
@Override
public void doWithProperties(SimplePropertyHandler handler) {
delegate.doWithProperties((SimplePropertyHandler) property -> {
if (property instanceof MongoPersistentProperty) {
handler.doWithPersistentProperty(wrap((MongoPersistentProperty) property));
} else {
handler.doWithPersistentProperty(property);
}
});
}
@Override
public void doWithAssociations(AssociationHandler<MongoPersistentProperty> handler) {
delegate.doWithAssociations(handler);
}
@Override
public void doWithAssociations(SimpleAssociationHandler handler) {
delegate.doWithAssociations(handler);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
return delegate.findAnnotation(annotationType);
}
@Override
public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) throws IllegalStateException {
return delegate.getRequiredAnnotation(annotationType);
}
@Override
public <A extends Annotation> boolean isAnnotationPresent(Class<A> annotationType) {
return delegate.isAnnotationPresent(annotationType);
}
@Override
public <B> PersistentPropertyAccessor<B> getPropertyAccessor(B bean) {
return delegate.getPropertyAccessor(bean);
}
@Override
public <B> PersistentPropertyPathAccessor<B> getPropertyPathAccessor(B bean) {
return delegate.getPropertyPathAccessor(bean);
}
@Override
public IdentifierAccessor getIdentifierAccessor(Object bean) {
return delegate.getIdentifierAccessor(bean);
}
@Override
public boolean isNew(Object bean) {
return delegate.isNew(bean);
}
@Override
public boolean isImmutable() {
return delegate.isImmutable();
}
@Override
public boolean requiresPropertyPopulation() {
return delegate.requiresPropertyPopulation();
}
@Override
public Iterator<MongoPersistentProperty> iterator() {
List<MongoPersistentProperty> target = new ArrayList<>();
delegate.iterator().forEachRemaining(it -> target.add(wrap(it)));
return target.iterator();
}
@Override
public void forEach(Consumer<? super MongoPersistentProperty> action) {
delegate.forEach(it -> action.accept(wrap(it)));
}
@Override
public Spliterator<MongoPersistentProperty> spliterator() {
return delegate.spliterator();
}
private MongoPersistentProperty wrap(MongoPersistentProperty source) {
if (source == null) {
return source;
}
return new UnwrappedMongoPersistentProperty(source, context);
}
@Override
public void addPersistentProperty(MongoPersistentProperty property) {
}
@Override
public void addAssociation(Association<MongoPersistentProperty> association) {
}
@Override
public void verify() throws MappingException {
}
@Override
public void setPersistentPropertyAccessorFactory(PersistentPropertyAccessorFactory factory) {
}
@Override
public void setEvaluationContextProvider(EvaluationContextProvider provider) {
}
@Override
public boolean isUnwrapped() {
return context.getProperty().isUnwrapped();
}
}

View File

@@ -1,307 +0,0 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* Unwrapped variant of {@link MongoPersistentProperty}.
*
* @author Christoph Strobl
* @since 3.2
* @see Unwrapped
*/
class UnwrappedMongoPersistentProperty implements MongoPersistentProperty {
private final MongoPersistentProperty delegate;
private final UnwrapEntityContext context;
public UnwrappedMongoPersistentProperty(MongoPersistentProperty delegate, UnwrapEntityContext context) {
this.delegate = delegate;
this.context = context;
}
@Override
public String getFieldName() {
if (!context.getProperty().isUnwrapped()) {
return delegate.getFieldName();
}
return context.getProperty().findAnnotation(Unwrapped.class).prefix() + delegate.getFieldName();
}
@Override
public Class<?> getFieldType() {
return delegate.getFieldType();
}
@Override
public int getFieldOrder() {
return delegate.getFieldOrder();
}
@Override
public boolean isDbReference() {
return delegate.isDbReference();
}
@Override
public boolean isExplicitIdProperty() {
return delegate.isExplicitIdProperty();
}
@Override
public boolean isLanguageProperty() {
return delegate.isLanguageProperty();
}
@Override
public boolean isExplicitLanguageProperty() {
return delegate.isExplicitLanguageProperty();
}
@Override
public boolean isTextScoreProperty() {
return delegate.isTextScoreProperty();
}
@Override
@Nullable
public DBRef getDBRef() {
return delegate.getDBRef();
}
@Override
public boolean usePropertyAccess() {
return delegate.usePropertyAccess();
}
@Override
public boolean hasExplicitWriteTarget() {
return delegate.hasExplicitWriteTarget();
}
@Override
public PersistentEntity<?, MongoPersistentProperty> getOwner() {
return delegate.getOwner();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public Class<?> getType() {
return delegate.getType();
}
@Override
public TypeInformation<?> getTypeInformation() {
return delegate.getTypeInformation();
}
@Override
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
return delegate.getPersistentEntityTypes();
}
@Override
@Nullable
public Method getGetter() {
return delegate.getGetter();
}
@Override
public Method getRequiredGetter() {
return delegate.getRequiredGetter();
}
@Override
@Nullable
public Method getSetter() {
return delegate.getSetter();
}
@Override
public Method getRequiredSetter() {
return delegate.getRequiredSetter();
}
@Override
@Nullable
public Method getWither() {
return delegate.getWither();
}
@Override
public Method getRequiredWither() {
return delegate.getRequiredWither();
}
@Override
@Nullable
public Field getField() {
return delegate.getField();
}
@Override
public Field getRequiredField() {
return delegate.getRequiredField();
}
@Override
@Nullable
public String getSpelExpression() {
return delegate.getSpelExpression();
}
@Override
@Nullable
public Association<MongoPersistentProperty> getAssociation() {
return delegate.getAssociation();
}
@Override
public Association<MongoPersistentProperty> getRequiredAssociation() {
return delegate.getRequiredAssociation();
}
@Override
public boolean isEntity() {
return delegate.isEntity();
}
@Override
public boolean isIdProperty() {
return delegate.isIdProperty();
}
@Override
public boolean isVersionProperty() {
return delegate.isVersionProperty();
}
@Override
public boolean isCollectionLike() {
return delegate.isCollectionLike();
}
@Override
public boolean isMap() {
return delegate.isMap();
}
@Override
public boolean isArray() {
return delegate.isArray();
}
@Override
public boolean isTransient() {
return delegate.isTransient();
}
@Override
public boolean isWritable() {
return delegate.isWritable();
}
@Override
public boolean isImmutable() {
return delegate.isImmutable();
}
@Override
public boolean isAssociation() {
return delegate.isAssociation();
}
@Override
public boolean isUnwrapped() {
return delegate.isUnwrapped();
}
@Override
@Nullable
public Class<?> getComponentType() {
return delegate.getComponentType();
}
@Override
public Class<?> getRawType() {
return delegate.getRawType();
}
@Override
@Nullable
public Class<?> getMapValueType() {
return delegate.getMapValueType();
}
@Override
public Class<?> getActualType() {
return delegate.getActualType();
}
@Override
@Nullable
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
return delegate.findAnnotation(annotationType);
}
@Override
public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) throws IllegalStateException {
return delegate.getRequiredAnnotation(annotationType);
}
@Override
@Nullable
public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
return delegate.findPropertyOrOwnerAnnotation(annotationType);
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return delegate.isAnnotationPresent(annotationType);
}
@Override
public boolean hasActualTypeAnnotation(Class<? extends Annotation> annotationType) {
return delegate.hasActualTypeAnnotation(annotationType);
}
@Override
@Nullable
public Class<?> getAssociationTargetType() {
return delegate.getAssociationTargetType();
}
@Override
public <T> PersistentPropertyAccessor<T> getAccessorForOwner(T owner) {
return delegate.getAccessorForOwner(owner);
}
}

View File

@@ -77,7 +77,7 @@ public class MongoMappingEvent<T> extends ApplicationEvent {
/**
* Allows client code to change the underlying source instance by applying the given {@link Function}.
*
*
* @param mapper the {@link Function} to apply, will only be applied if the source is not {@literal null}.
* @since 2.1
*/

View File

@@ -15,12 +15,13 @@
*/
package org.springframework.data.mongodb.core.mapping.event;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.core.Ordered;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
@@ -33,7 +34,7 @@ import org.springframework.util.Assert;
*/
public class ReactiveAuditingEntityCallback implements ReactiveBeforeConvertCallback<Object>, Ordered {
private final ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory;
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
/**
* Creates a new {@link ReactiveAuditingEntityCallback} using the given {@link MappingContext} and
@@ -41,7 +42,7 @@ public class ReactiveAuditingEntityCallback implements ReactiveBeforeConvertCall
*
* @param auditingHandlerFactory must not be {@literal null}.
*/
public ReactiveAuditingEntityCallback(ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory) {
public ReactiveAuditingEntityCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
this.auditingHandlerFactory = auditingHandlerFactory;
@@ -53,7 +54,7 @@ public class ReactiveAuditingEntityCallback implements ReactiveBeforeConvertCall
*/
@Override
public Publisher<Object> onBeforeConvert(Object entity, String collection) {
return auditingHandlerFactory.getObject().markAudited(entity);
return Mono.just(auditingHandlerFactory.getObject().markAudited(entity));
}
/*

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core.messaging;
import lombok.AllArgsConstructor;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
@@ -215,17 +217,12 @@ class ChangeStreamTask extends CursorReadingTask<ChangeStreamDocument<Document>,
*
* @since 2.1
*/
@AllArgsConstructor
static class ChangeStreamEventMessage<T> implements Message<ChangeStreamDocument<Document>, T> {
private final ChangeStreamEvent<T> delegate;
private final MessageProperties messageProperties;
ChangeStreamEventMessage(ChangeStreamEvent<T> delegate, MessageProperties messageProperties) {
this.delegate = delegate;
this.messageProperties = messageProperties;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.messaging.Message#getRaw()

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core.messaging;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -30,7 +34,6 @@ import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.Reque
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ErrorHandler;
import org.springframework.util.ObjectUtils;
/**
* Simple {@link Executor} based {@link MessageListenerContainer} implementation for running {@link Task tasks} like
@@ -256,6 +259,7 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
* @author Christoph Strobl
* @since 2.1
*/
@EqualsAndHashCode
static class TaskSubscription implements Subscription {
private final Task task;
@@ -282,39 +286,19 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
public void cancel() throws DataAccessResourceFailureException {
task.cancel();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TaskSubscription that = (TaskSubscription) o;
return ObjectUtils.nullSafeEquals(this.task, that.task);
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(task);
}
}
/**
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
private static class DecoratingLoggingErrorHandler implements ErrorHandler {
private final Log logger = LogFactory.getLog(DecoratingLoggingErrorHandler.class);
private final ErrorHandler delegate;
DecoratingLoggingErrorHandler(ErrorHandler delegate) {
this.delegate = delegate;
}
@Override
public void handleError(Throwable t) {

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core.messaging;
import lombok.ToString;
import org.bson.Document;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.util.ClassUtils;
@@ -24,6 +26,7 @@ import org.springframework.util.ClassUtils;
* @author Mark Paluch
* @since 2.1
*/
@ToString(of = { "delegate", "targetType" })
class LazyMappingDelegatingMessage<S, T> implements Message<S, T> {
private final Message<S, ?> delegate;
@@ -79,8 +82,4 @@ class LazyMappingDelegatingMessage<S, T> implements Message<S, T> {
public MessageProperties getProperties() {
return delegate.getProperties();
}
public String toString() {
return "LazyMappingDelegatingMessage(delegate=" + this.delegate + ", targetType=" + this.targetType + ")";
}
}

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.mongodb.core.messaging;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* General message abstraction for any type of Event / Message published by MongoDB server to the client. This might be
@@ -63,6 +65,8 @@ public interface Message<S, T> {
* @author Christoph Strobl
* @since 2.1
*/
@ToString
@EqualsAndHashCode
class MessageProperties {
private static final MessageProperties EMPTY = new MessageProperties();
@@ -107,34 +111,6 @@ public interface Message<S, T> {
return new MessagePropertiesBuilder();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
MessageProperties that = (MessageProperties) o;
if (!ObjectUtils.nullSafeEquals(this.databaseName, that.databaseName)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.collectionName, that.collectionName);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(databaseName);
result = 31 * result + ObjectUtils.nullSafeHashCode(collectionName);
return result;
}
public String toString() {
return "Message.MessageProperties(databaseName=" + this.getDatabaseName() + ", collectionName="
+ this.getCollectionName() + ")";
}
/**
* Builder for {@link MessageProperties}.
*

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.mongodb.core.messaging;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Trivial {@link Message} implementation.
@@ -25,6 +27,8 @@ import org.springframework.util.ObjectUtils;
* @author Christoph Strobl
* @since 2.1
*/
@EqualsAndHashCode
@ToString
class SimpleMessage<S, T> implements Message<S, T> {
private @Nullable final S raw;
@@ -71,35 +75,4 @@ class SimpleMessage<S, T> implements Message<S, T> {
public MessageProperties getProperties() {
return properties;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SimpleMessage<?, ?> that = (SimpleMessage<?, ?>) o;
if (!ObjectUtils.nullSafeEquals(this.raw, that.raw)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.body, that.body)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.properties, that.properties);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(raw);
result = 31 * result + ObjectUtils.nullSafeHashCode(body);
result = 31 * result + ObjectUtils.nullSafeHashCode(properties);
return result;
}
public String toString() {
return "SimpleMessage(raw=" + this.getRaw() + ", body=" + this.getBody() + ", properties=" + this.getProperties()
+ ")";
}
}

View File

@@ -67,7 +67,6 @@ public class BasicUpdate extends Update {
}
@Override
@Deprecated
public Update pushAll(String key, Object[] values) {
Document keyValue = new Document();
keyValue.put(key, values);

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core.query;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Locale;
import java.util.Optional;
@@ -510,6 +515,8 @@ public class Collation {
*
* @since 2.0
*/
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
static class ICUComparisonLevel implements ComparisonLevel {
private final int level;
@@ -519,24 +526,6 @@ public class Collation {
ICUComparisonLevel(int level) {
this(level, Optional.empty(), Optional.empty());
}
ICUComparisonLevel(int level, Optional<CaseFirst> caseFirst, Optional<Boolean> caseLevel) {
this.level = level;
this.caseFirst = caseFirst;
this.caseLevel = caseLevel;
}
public int getLevel() {
return this.level;
}
public Optional<CaseFirst> getCaseFirst() {
return this.caseFirst;
}
public Optional<Boolean> getCaseLevel() {
return this.caseLevel;
}
}
/**
@@ -661,6 +650,7 @@ public class Collation {
/**
* @since 2.0
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public static class CaseFirst {
private static final CaseFirst UPPER = new CaseFirst("upper");
@@ -669,10 +659,6 @@ public class Collation {
private final String state;
private CaseFirst(String state) {
this.state = state;
}
/**
* Sort uppercase before lowercase.
*
@@ -704,6 +690,7 @@ public class Collation {
/**
* @since 2.0
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public static class Alternate {
private static final Alternate NON_IGNORABLE = new Alternate("non-ignorable", Optional.empty());
@@ -711,11 +698,6 @@ public class Collation {
final String alternate;
final Optional<String> maxVariable;
Alternate(String alternate, Optional<String> maxVariable) {
this.alternate = alternate;
this.maxVariable = maxVariable;
}
/**
* Consider Whitespace and punctuation as base characters.
*
@@ -779,17 +761,12 @@ public class Collation {
* @see <a href="http://site.icu-project.org">ICU - International Components for Unicode</a>
* @since 2.0
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public static class CollationLocale {
private final String language;
private final Optional<String> variant;
private CollationLocale(String language, Optional<String> variant) {
this.language = language;
this.variant = variant;
}
/**
* Create new {@link CollationLocale} for given language.
*

Some files were not shown because too many files have changed in this diff Show More