Compare commits

..

186 Commits

Author SHA1 Message Date
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
Mark Paluch
f88d1de068 DATAMONGO-2534 - Release version 3.0 GA (Neumann). 2020-05-12 12:28:23 +02:00
Mark Paluch
62bfde2b6b DATAMONGO-2534 - Prepare 3.0 GA (Neumann). 2020-05-12 12:27:59 +02:00
Mark Paluch
93a27f80a3 DATAMONGO-2534 - Updated changelog. 2020-05-12 12:27:47 +02:00
Christoph Strobl
c2b0090ff1 DATAMONGO-2541 - Upgrade to MongoDB 4.0.3 Drivers. 2020-05-11 17:00:39 +02:00
Florian Bernard
0345abb5f8 DATAMONGO-2509 - Add missing Query constructor in reference documentation.
Original pull request: #846.
2020-05-06 11:52:16 +02:00
Mark Paluch
35196e3129 DATAMONGO-2509 - Polishing.
Fix typos, improve wording. Reworks documentation specific to MongoDB 3 and 4.

Original pull request: #853.
2020-05-06 11:46:29 +02:00
Christoph Strobl
37d99c4948 DATAMONGO-2509 - Update Javadoc.
Original pull request: #853.
2020-05-06 11:21:11 +02:00
Christoph Strobl
288e04b2d3 DATAMONGO-2509 - Update Reference Documentation.
Related to: DATAMONGO-365, DATAMONGO-384, DATAMONGO-2192, DATAMONGO-2407

Original pull request: #853.
2020-05-06 11:21:02 +02:00
Greg Turnquist
689c1480bd DATAMONGO-2535 - Remove Travis CI. 2020-05-04 15:09:00 -05:00
Mark Paluch
09fbe6060e DATAMONGO-2503 - After release cleanups. 2020-04-28 15:11:43 +02:00
Mark Paluch
d5cf3dfc22 DATAMONGO-2503 - Prepare next development iteration. 2020-04-28 15:11:42 +02:00
Mark Paluch
0f987f44ef DATAMONGO-2503 - Release version 3.0 RC2 (Neumann). 2020-04-28 15:03:53 +02:00
Mark Paluch
5df22233d8 DATAMONGO-2503 - Prepare 3.0 RC2 (Neumann). 2020-04-28 15:03:33 +02:00
Mark Paluch
9751c6bc8b DATAMONGO-2503 - Updated changelog. 2020-04-28 15:03:26 +02:00
Mark Paluch
7dc509953a DATAMONGO-2500 - Updated changelog. 2020-04-28 14:46:50 +02:00
Mark Paluch
2f4f1e524d DATAMONGO-2484 - Updated changelog. 2020-04-28 11:59:01 +02:00
Christoph Strobl
26ddf9d59f DATAMONGO-2531 - Fix MongoClientSettings UUID configuration hook in MongoConfigurationSupport.
Client code overriding configureClientSettings does not automatically remove the default UUidRepresentation settings.
2020-04-27 09:30:37 +02:00
Mark Paluch
96ff4e1d14 DATAMONGO-2461 - Polishing.
Remove superfluous Test annotation as RepeatFailedTest acts as a TestTemplate so adding the Test annotation causes the test to run twice.
2020-04-24 09:03:18 +02:00
Mark Paluch
2af13c27a7 DATAMONGO-2529 - Ensure that MappingMongoConverter.read(…) is never called with null.
Previously, various methods attempted to pass a null argument as source for the converter. The API is non-null and implementations relying on these constraints were easily breakable.

We now make sure that the source is never null.
2020-04-23 14:57:52 +02:00
Mark Paluch
f4d2fc6231 DATAMONGO-2504 - Polishing.
Update equals/hashCode implementation to use the Spring Data form. Make fields final where possible. Use diamond syntax. Reorder methods. Reformat code. Extend tests.

Original pull request: #848.
2020-04-23 12:24:26 +02:00
ddebray
3ab679bcc7 DATAMONGO-2504 - Add hashCode and equals to TextCriteria and Term.
Original pull request: #848.
2020-04-23 12:24:10 +02:00
Mark Paluch
318d552797 DATAMONGO-2505 - Polishing.
Simplify operators. Remove Lombok usage from newly introduced code. Update reference documentation. Reformat code.

Original pull request: #854.
2020-04-23 12:02:46 +02:00
Mathieu Ouellet
8cfbd39c7e DATAMONGO-2505 - Deferred Database retrieval from ReactiveMongoDatabaseFactory.
Change ReactiveMongoDatabaseFactory#getMongoDatabase() methods return Mono to enable access to the subscriber context.

Original pull request: #854.
2020-04-23 12:02:15 +02:00
Christoph Strobl
3a14af5485 DATAMONGO-2513 - Fix Eq aggregation operator comparing collection values.
Original pull request: #855.
2020-04-22 11:46:49 +02:00
Mark Paluch
00034d899a DATAMONGO-2516 - Polishing.
Reflect nullability change in TextScore Javadoc. Update documentation.

Original pull request: #856.
2020-04-22 11:12:50 +02:00
Christoph Strobl
fba6d7d8be DATAMONGO-2516 - Assert compatibility with MongoDB 4.4-rc0.
Fixes:
- Fields list must not contain text search score property when no $text criteria present.
- Sort must not be an empty document when running map reduce.
- Timeout in tests creating indexes.

Changes:
- score property might now be null when not having a $text criteria present. Was zero before.

Original pull request: #856.
2020-04-22 11:12:29 +02:00
Mark Paluch
95c4707902 DATAMONGO-2523 - Polishing.
Reformat code.

Original pull request: #859.
2020-04-22 10:14:50 +02:00
Christoph Strobl
32f3e60c9f DATAMONGO-2523 - Fix Json binding of SpEL expressions in arrays.
The closing bracket must not have a leading whitespace.

Original pull request: #859.
2020-04-22 10:08:57 +02:00
Mark Paluch
b57e571033 DATAMONGO-2517 - Polishing.
Reformat code.

Original pull request: #857.
2020-04-21 16:02:09 +02:00
Christoph Strobl
6604c507dd DATAMONGO-2517 - Fix invalid entity creation for text queries.
Fix a glitch in the MappingMongoConverter that uses the single String argument constructor (since it matches in type and parameter count to the given input string) to falsely instantiate an Entity when it should not.

Original pull request: #857.
2020-04-21 16:02:04 +02:00
Christoph Strobl
e1df28797a DATAMONGO-2507 - Polishing.
Add 'matching' default method also to imperative variant (ExecutableFindOperation), fix & add tests using 'distinct'.
Update Javadoc and rename input arg 'criteriaDefinition' to 'criteria'.

Original Pull Request: #852
2020-04-14 10:22:01 +02:00
Juergen Zimmermann
c6630aa279 DATAMONGO-2507 - Add default method matching(CriteriaDefinition) to ReactiveFindOperation.DistinctWithQuery.
Original Pull Request: #852
2020-04-14 09:51:23 +02:00
Greg Turnquist
bf921cdbd7 DATAMONGO-2510 - Use JDK 14 for Java.NEXT CI testing. 2020-04-09 14:29:16 -05:00
Christoph Strobl
a1b4e6df59 DATAMONGO-2508 - Upgrade to MongoDB 4.0.2 Drivers. 2020-04-08 11:38:19 +02:00
Christoph Strobl
4b3312998a DATAMONGO-2506 - Provide meaningful error message when using unsupported return type in repository aggregation method.
We improved the error message for unsupported return types instead of running into an IllegalArgumentException for unique results.

Original pull request: #851.
2020-04-07 14:58:38 +02:00
Mark Paluch
c5501db577 DATAMONGO-2502 - Polishing.
Extend tests. Fix generics. Consistently use compiled patterns for positional placeholder removal.

Original pull request: #847.
2020-04-07 14:10:20 +02:00
Christoph Strobl
936a0d35f7 DATAMONGO-2502 - Fix nested array path mapping for updates.
Original pull request: #847.
2020-04-07 14:10:09 +02:00
Christoph Strobl
5dd91d0b6d DATAMONGO-1677 - Polishing.
Switch tests to JUnit Jupiter.

Original Pull Request: #849
2020-04-06 08:51:30 +02:00
Jens Schauder
28510de6c8 DATAMONGO-1677 - Adds a test to confirm a query with more than 10 arguments works just fine.
Original Pull Request: #849
2020-04-06 08:50:56 +02:00
Mark Paluch
4bbf4cd5cf DATAMONGO-2492 - After release cleanups. 2020-03-31 15:08:05 +02:00
Mark Paluch
90bd3f0f18 DATAMONGO-2492 - Prepare next development iteration. 2020-03-31 15:08:04 +02:00
Mark Paluch
1e35116419 DATAMONGO-2492 - Release version 3.0 RC1 (Neumann). 2020-03-31 14:59:41 +02:00
Mark Paluch
dd336f0ecb DATAMONGO-2492 - Prepare 3.0 RC1 (Neumann). 2020-03-31 14:59:20 +02:00
Mark Paluch
d020219ded DATAMONGO-2492 - Updated changelog. 2020-03-31 14:59:15 +02:00
Mark Paluch
0345eff69a DATAMONGO-2477 - Polishing.
Make sure that indexes get created where needed.
2020-03-30 14:37:42 +02:00
Mark Paluch
55fee27fb6 DATAMONGO-625 - Polishing.
Introduce default interface methods where possible. Rename save(…) to store(…) to align with method naming. Reduce constructor visibility to avoid invalid API usage. Replace mutable object in builder with fields to avoid mutation of already built objects when reusing the builder.
Remove Options.chunked(…) factory method to avoid confusion with chunkSize method.

Reformat code, strip trailing whitespaces.

Original pull request: #842.
2020-03-30 14:19:45 +02:00
Christoph Strobl
ffba352e15 DATAMONGO-625 - Add support for GridFS with predefined id.
We now support storing GridFS content with predefined id, which allows to replace an existing resource instead of having to delete and reupload it.

Original pull request: #842.
2020-03-30 14:19:26 +02:00
Mark Paluch
1118df5550 DATAMONGO-2477 - Polishing.
Remove warn log for auto-index creation. Remove unused fields. Document index creation in reference documentation.

Original pull request: #845.
2020-03-26 15:14:03 +01:00
Christoph Strobl
29f05af733 DATAMONGO-2477 - Disable auto index creation by default.
Original pull request: #845.
2020-03-26 15:14:03 +01:00
Oliver Drotbohm
7bac739146 DATAMONGO-2501 - Upgrade to Querydsl 4.3.
Adapted to changes in the APT APIs.
2020-03-26 14:38:51 +01:00
Mark Paluch
6366d3cec1 DATAMONGO-2485 - Updated changelog. 2020-03-25 10:59:47 +01:00
Christoph Strobl
44913abd80 DATAMONGO-2300 - Polishing.
Move null check to event publishing logic.

Original Pull Request: #763
2020-03-23 09:29:49 +01:00
Heesu Jung
7a7f7c942d DATAMONGO-2300 - Add check rawType is null in readMap.
Original Pull Request: #763
2020-03-23 09:29:19 +01:00
Christoph Strobl
e9c9938016 DATAMONGO-2498 - Upgrade to MongoDB 4.0.1 Drivers. 2020-03-23 09:18:07 +01:00
Mark Paluch
c9da0a75ff DATAMONGO-931 - Polishing.
Reformat code. Add private constructor to prevent unwanted instantiation.

Original pull request: #844.
2020-03-20 14:45:11 +01:00
Christoph Strobl
581961e79a DATAMONGO-931 - Add support for $redact aggregation.
We now support $redact via Aggregation.redact.

Aggregation.redact(ConditionalOperators.when(Criteria.where("level").is(5))
    .then(RedactOperation.PRUNE)
    .otherwise(RedactOperation.DESCEND));

Original pull request: #844.
2020-03-20 14:40:16 +01:00
Christoph Strobl
0e0d726457 DATAMONGO-2475 - Polishing.
Fix reduction of $and/$or operations with more than two arguments.

Original Pull Request: #834
2020-03-20 10:15:10 +01:00
Enrique León Molina
7b5fea960f DATAMONGO-2475 - MongodbDocumentSerializer: reduce nested ANDs and ORs.
Reduce the number of nested $and / $or clauses by combining them into a single document where possible. This allows to simplify the following statement

{
  "$or": [
    {
      "$or": [
        {
          "$or": [
            {
              "$or": [
                { "firstname": "Hencxjo" },
                { "lastname": "Leon" }
              ]
            },
            { "age": { "$lte": 30 } }
          ]
        },
        { "age": { "$gte": 20 } }
      ]
    },
    { "uniqueId": { "$exists": false } }
  ]
}

to just

{
 "$or": [
   { "firstname": "Hencxjo" },
   { "lastname": "Leon" },
   { "age": { "$lte": 30 } },
   { "age": { "$gte": 20 } },
   { "uniqueId": { "$exists": false } }
 ]
}

Original Pull Request: #834
2020-03-20 10:13:37 +01:00
Mark Paluch
a04821ff90 DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor. 2020-03-19 15:37:30 +01:00
Mark Paluch
a6bd41bcf2 DATAMONGO-2416 - Polishing.
Reduce visibility of JUnit 5 test classes/methods.

Original Pull Request: #840
2020-03-17 11:41:39 +01:00
Mark Paluch
6387eb9762 DATAMONGO-2416 - Add convenience methods accepting CriteriaDefinition on Fluent API.
The fluent API now exposes default interface methods accepting CriteriaDefinition for a more concise expression of queries that do not require a Query object.

template.query(Person.class).matching(where("firstname").is("luke")).all()

instead of

template.query(Person.class).matching(query(where("firstname").is("luke"))).all()

Original Pull Request: #840
2020-03-17 11:41:14 +01:00
Mark Paluch
5fb4b036bb DATAMONGO-1026 - Include documentation about custom conversions.
We now include the documentations partial about custom conversions that explains default converter registrations, overrides and system setting timezone-sensitivity.
2020-03-17 10:57:15 +01:00
Mark Paluch
4f0dc04a81 DATAMONGO-2479 - Polishing.
Tweak Javadoc. Update reference documentation. Slightly refactor reactive flows to reduce operator count.

Simplify test fixtures and remove Google Guava usage. Reformat code.

Original pull request: #839.
2020-03-16 11:34:27 +01:00
Roman Puchkovskiy
ee59c6b774 DATAMONGO-2479 - Add AfterConvertCallback and AfterSaveCallback (and their reactive versions).
Previously, only BeforeConvertCallback and BeforeSaveCallback were supported (and their reactive counterparts). This commit adds support for 'after-convert' and 'after-save' events using entity callbacks feature.

Original pull request: #839.
2020-03-16 11:34:13 +01:00
Mark Paluch
b0b905ddb7 DATAMONGO-2488 - Polishing.
Simplify conditional entity check. Reduce test method visibility for JUnit 5.

Original pull request: #841.
2020-03-11 14:51:03 +01:00
Christoph Strobl
7f7be5e47d DATAMONGO-2488 - Fix nested array path field name mapping.
Original pull request: #841.
2020-03-11 14:37:36 +01:00
Jens Schauder
5a49aa6519 DATAMONGO-2473 - After release cleanups. 2020-03-11 09:57:42 +01:00
Jens Schauder
b68079c421 DATAMONGO-2473 - Prepare next development iteration. 2020-03-11 09:57:41 +01:00
Jens Schauder
fde49f2a5a DATAMONGO-2473 - Release version 3.0 M4 (Neumann). 2020-03-11 09:47:07 +01:00
Jens Schauder
4d73d76b9f DATAMONGO-2473 - Prepare 3.0 M4 (Neumann). 2020-03-11 09:46:29 +01:00
Jens Schauder
8f2c806403 DATAMONGO-2473 - Updated changelog. 2020-03-11 09:46:17 +01:00
Mark Paluch
d0eb76946e DATAMONGO-2491 - Adapt to Mockito 3.3 changes.
Mockito reports falsely unnecessary stubbings so we're switching to the silent runner for these cases.
2020-03-11 09:14:53 +01:00
Mark Paluch
df43d7fcdb DATAMONGO-2355 - Refactor Abstract…Configuration classes to avoid need for cglib proxies.
Configuration methods now use parameter injections to avoid the need for cglib subclassing.
2020-03-10 14:17:37 +01:00
Mark Paluch
7b34a602ed DATAMONGO-2355 - Declare concrete Template API class type on AbstractReactiveMongoConfiguration.
We now declare bean methods returning Template API types with their concrete implementation to allow for improved introspection of bean definitions.
2020-03-10 13:55:41 +01:00
Mark Paluch
c829387c82 DATAMONGO-2363 - Polishing.
Introduce support for SpEL aggregation expressions for AddFields and Set operations.

Rearrange methods. Make fields final where possible.

Original pull request: #801.
2020-03-09 14:09:00 +01:00
Christoph Strobl
a4e12a96c9 DATAMONGO-2363 - Add support for $merge and $addFields aggregation stage.
MergeOperation.builder().intoCollection("monthlytotals")
    .whenDocumentsMatchApply(
        newAggregation(
            AddFieldsOperation.builder()
                .addField("thumbsup").withValueOf(sumOf("thumbsup").and("$$new.thumbsup"))
                .addField("thumbsdown").withValueOf(sumOf("thumbsdown").and("$$new.thumbsdown"))
                .build()
        )
    )
    .whenDocumentsDontMatch(insertNewDocument())
    .build()

Original pull request: #801.
2020-03-09 14:08:53 +01:00
Mark Paluch
7e3f7bd861 DATAMONGO-2481 - Polishing.
Add since tag.

Original pull request: #838.
2020-03-06 12:20:29 +01:00
Mark Paluch
46ab6b4c94 DATAMONGO-2481 - Polishing.
Migrate more tests to JUnit 5. Rearrange methods. Reduce method visibility according to JUnit 5 requirements.

Remove Java 11 build and only use Java 8 and 13.

Original pull request: #838.
2020-03-06 12:20:26 +01:00
Christoph Strobl
8029acb3fb DATAMONGO-2481 - Improve build time.
Introduce Junit Jupiter extensions.
Flush collections instead of dropping them.
Apply cursor timeout to change stream and capped collections in tests.

Original pull request: #838.
2020-03-06 12:20:20 +01:00
Christoph Strobl
96ffb0b7f4 DATAMONGO-2489 - Upgrade to MongoDB 4.0 Drivers. 2020-03-06 09:20:55 +01:00
Mark Paluch
7849b5333b DATAMONGO-2453 - Updated changelog. 2020-02-26 11:55:05 +01:00
Mark Paluch
fb45b4eb2a DATAMONGO-2452 - Updated changelog. 2020-02-26 11:31:48 +01:00
Mark Paluch
a6a84421b4 DATAMONGO-2478 - Polishing.
Convert test to JUnit 5.

Original pull request: #836.
2020-02-24 11:34:26 +01:00
Christoph Strobl
c4dcc7d0f5 DATAMONGO-2478 - Fix NPE in Query.of when given a proxied source.
Original pull request: #836.
2020-02-24 11:32:09 +01:00
Mark Paluch
c25a1a9e53 DATAMONGO-2476 - Polishing.
Convert test to JUnit 5.

Original pull request: #835.
2020-02-24 11:29:10 +01:00
Christoph Strobl
81c68955fe DATAMONGO-2476 - Fix Json parsing for unquoted placeholders in arrays.
Original pull request: #835.
2020-02-24 11:07:16 +01:00
Mark Paluch
22ca597fca DATAMONGO-2341 - Polishing.
Inline MongoPersistentEntity.idPropertyIsShardKey() into UpdateContext. Move mapped shard key cache to QueryOperations level. Simplify conditionals. Tweak documentation.

Original pull request: #833.
2020-02-17 10:17:35 +01:00
Christoph Strobl
6259cd2c3b DATAMONGO-2341 - Support shard key derivation in save operations via @Sharded annotation.
Spring Data MongoDB uses the @Sharded annotation to identify entities stored in sharded collections.
The shard key consists of a single or multiple properties present in every document within the target collection, and is used to distribute them across shards.

Spring Data MongoDB will do best effort optimisations for sharded scenarios when using repositories by adding required shard key information, if not already present, to replaceOne filter queries when upserting entities. This may require an additional server round trip to determine the actual value of the current shard key.

By setting @Sharded(immutableKey = true) no attempt will be made to check if an entities shard key changed.

Please see the MongoDB Documentation for further details and the list below for which operations are eligible to auto include the shard key.

* Reactive/CrudRepository.save(...)
* Reactive/CrudRepository.saveAll(...)
* Reactive/MongoTemplate.save(...)

Original pull request: #833.
2020-02-17 10:17:20 +01:00
Christoph Strobl
f153399c3b DATAMONGO-2474 - Upgrade to MongoDB driver 4.0.0-rc0. 2020-02-14 07:44:03 +01:00
Mark Paluch
65508eb01b DATAMONGO-2456 - After release cleanups. 2020-02-12 15:04:22 +01:00
Mark Paluch
a7c25c8524 DATAMONGO-2456 - Prepare next development iteration. 2020-02-12 15:04:21 +01:00
Mark Paluch
087f8f78b5 DATAMONGO-2456 - Release version 3.0 M3 (Neumann). 2020-02-12 14:47:16 +01:00
Mark Paluch
534ce2e5f5 DATAMONGO-2456 - Prepare 3.0 M3 (Neumann). 2020-02-12 14:46:56 +01:00
Mark Paluch
0f4f745d3c DATAMONGO-2456 - Updated changelog. 2020-02-12 14:46:51 +01:00
Mark Paluch
ca802c7ca4 DATAMONGO-2470 - Adapt tests to AssertJ 3.15.0. 2020-02-11 10:57:52 +01:00
Mark Paluch
e9d14f41a5 DATAMONGO-2400 - Polishing.
Adapt to changes in commons. Add functional create method to MongoCustomConversions. Update Javadoc and reference documentation. Truncate precision for Java 11 build compatibility in tests.

Original pull request: #810.
2020-02-07 14:16:53 +01:00
Christoph Strobl
3b6880edfd DATAMONGO-2400 - Consider java.time.Instant a store supported native type and add configuration options for other java.time types.
We now use the MongoDB Java driver InstantCodec instead of a dedicated converter. Other java.time types like LocalDate use a different zone offset when writing values which can lead to unexpected behavior. Therefore we added configuration options to MongoCustomConversions that allow to tell the conversion sub system which approach to use when writing those kind of types.

Original pull request: #810.
2020-02-07 12:32:58 +01:00
Mark Paluch
0b77906a83 DATAMONGO-2461 - Polishing.
Extend Javadoc. Reformat code. Reduce log level to info. Refactor resource disposal to use Store.CloseableResource.

Original pull request: #831.
2020-02-05 10:35:58 +01:00
Christoph Strobl
993018feb1 DATAMONGO-2461 - Efficient resource usage and ci related improvments.
- Give the server a little time to think on ci system.
- Reuse and Close MongoClients in tests.
- Use WriteConcern majority in setup.
- Use longer transaction timeout on server
- Use junit-pioneer to repeatable tests known to fail due to network errors.

Original pull request: #831.
2020-02-05 10:35:51 +01:00
Mark Paluch
dd15bbc5c1 DATAMONGO-2079 - Reduce visibility of MappingMongoConverter.getValueInternal.
Do not expose a method that is used internally.

Original Pull Request: #832
2020-02-04 14:21:46 +01:00
Mark Paluch
f6c62d6e2f DATAMONGO-2079 - MappingMongoConverter no longer implements ValueResolver.
MappingMongoConverter no longer implements a package-private interface so that converter instances can be proxied.

Original Pull Request: #832
2020-02-04 14:21:19 +01:00
Christoph Strobl
632c499cf1 DATAMONGO-2417 - Update Copyright headers of Kotlin extensions and test.
Original Pull Request: #809
2020-02-04 08:55:12 +01:00
Mark Paluch
2c9956723e DATAMONGO-2417 - Typesafe Kotlin Extension for distinct queries.
We now provide extensions for imperative and reactive distinct queries accepting Kotlin's KProperty and KProperty1 to express type-safe queries:

mongo.query<Customer>().distinct(Customer::name)

mongo.distinct(Customer::name)

Original Pull Request: #809
2020-02-04 08:54:37 +01:00
Mark Paluch
d0c2c820d7 DATAMONGO-2464 - Polishing.
Apply fix also to reactive MongoDB repository documentation.

Original pull request: #816.
2020-02-03 11:34:00 +01:00
LiangYong
f69ddb6c61 DATAMONGO-2464 - Fix code examples in reference documentation.
fixed repository miss "{" issue.

Original pull request: #816.
2020-02-03 11:34:00 +01:00
Mark Paluch
073668b1d1 DATAMONGO-2460 - Polishing.
Reformat code. Use diamond syntax.

Original pull request: #830.
2020-02-03 11:25:25 +01:00
Christoph Strobl
291d84591c DATAMONGO-2460 - Fix target type computation for complex id properties with @Field annotation.
We now set the target type to org.bson.Document for id properties annotated with @Field having the implicit target type derived from the annotation. Along the lines we fixed warn message when an id property with explicit (unsupported) field name is detected.

Original pull request: #830.
2020-02-03 11:25:11 +01:00
Mark Paluch
46806a5606 DATAMONGO-2449 - Polishing.
Reformat code. Tweak Javadoc.

Original pull request: #827.
2020-02-03 11:02:50 +01:00
Christoph Strobl
85519eb84d DATAMONGO-2449 - Evaluate allowDiskUse added to Meta annotation when executing derived Aggregation.
@Meta(allowDiskUse...) allows to set the according aggregation option when executing a String based annotation via a repository definition.

Original pull request: #827.
2020-02-03 11:02:32 +01:00
Mark Paluch
f8ee9648da DATAMONGO-2365 - Polishing.
Reformat code. Fix method name and generics. Whitespaces.

Original pull request: #828.
2020-01-29 13:40:42 +01:00
Christoph Strobl
c7f9274480 DATAMONGO-2365 - Extract common functionality to centralized component.
Share common code paths between reactive and imperative implementation.

Original pull request: #828.
2020-01-29 13:40:39 +01:00
Christoph Strobl
0bc0fff24e DATAMONGO-2365 - Pass on index name to query hint.
Original pull request: #828.
2020-01-29 13:40:28 +01:00
Mark Paluch
c21b35973d DATAMONGO-2462 - Move off deprecated EntityInstantiators.
We now use the newly introduced EntityInstantiator at o.s.d.mapping.model and moved off the deprecated one in o.s.d.convert.
2020-01-29 10:36:45 +01:00
Mark Paluch
44017485fc DATAMONGO-1997 - Polishing.
Tweak documentation. Reformat code. Remove commented code.

Original pull request: #826.
2020-01-29 10:26:25 +01:00
Christoph Strobl
c56a13ad00 DATAMONGO-1997 - Add support to return the single deleted item for a deleteBy query method.
Added support for:

@Nullable
Person deleteSingleByLastname(String lastname);

Optional<Person> deleteByBirthdate(Date birthdate);

Mono<Person> deleteSinglePersonByLastname(String lastname);

Original pull request: #826.
2020-01-29 10:26:19 +01:00
Mark Paluch
0a2ea88f3c DATAMONGO-2406 - Polishing.
Add optimization for Mono.

Original pull request: #825.
2020-01-29 09:57:42 +01:00
Christoph Strobl
80da9e21ed DATAMONGO-2406 - Derived reactive deleteBy query execution should allow Mono<Void> result.
Mono<Void> is now a supported return type of derived reactive deleteBy queries like:

    Mono<Void> deleteByLastname(String lastname);

Original pull request: #825.
2020-01-29 09:57:34 +01:00
Mark Paluch
708466b323 DATAMONGO-2459 - Polishing.
Slightly tweak wording.

Original pull request: #829
2020-01-29 09:52:38 +01:00
Christoph Strobl
2905315452 DATAMONGO-2459 - Add sample for passing on limit and offset using reactive repositories.
Original pull request: #829.
2020-01-29 09:46:20 +01:00
Christoph Strobl
ecc9f3fd60 DATAMONGO-2457 - Fix id type explanation in repository documentation.
Original pull request: #829.
2020-01-29 09:46:15 +01:00
Mark Paluch
c83929c2a3 DATAMONGO-2427 - Simplify docker setup.
Original pull request: #823.
2020-01-27 11:44:02 +01:00
Mark Paluch
9abf098bdb DATAMONGO-2427 - Polishing.
Guard tests.

Original pull request: #823.
2020-01-27 11:43:52 +01:00
Mark Paluch
f688cca400 DATAMONGO-2427 - Switch to MongoDB Driver 4.0.0-beta1.
Enable disabled tests. Adapt to ReactiveGridFS changes.

Use WriteConcern.MAJORITY in tests.

Original pull request: #823.
2020-01-27 11:43:52 +01:00
Christoph Strobl
f09c622b2f DATAMONGO-2427 - Update GridFS API to use MongoDB native ByteBuffer Publisher API and introduce UUID format configuration options.
Move from AsyncInputStream handling to Publisher for GridFS.
UUID types require additional configuration setup to prevent errors while processing legacy (type 3) binary types. We still use type 3 as default but allow codec configuration for type 4 via Java and XML configuration.
Updated migration guide.

Original pull request: #823.
2020-01-27 11:43:52 +01:00
Mark Paluch
6049541d0a DATAMONGO-2427 - Polishing.
Replace leftovers to Mongo 2.x API with Document and MongoDatabase references and tweak Javadoc. Reorder field declarations to class header. Reflect 3.0 versions in schema configuration. Add TODO markers to disabled tests.
Reflect changes in documentation.

Enable disabled test.

Original pull request: #823.
2020-01-27 11:43:52 +01:00
Mark Paluch
2a3f862703 DATAMONGO-2427 - Rename MongoDbFactory to MongoDatabaseFactory.
Rename to follow MongoDB's naming scheme. Re-introduce deprecated MongoDbFactory to aid migration.

Original pull request: #823.
2020-01-27 11:43:51 +01:00
Christoph Strobl
8b97e932a2 DATAMONGO-2427 - Upgrade to MongoDB 4.0 Driver.
This change switches to the MongoDB 4.0 driver and introduces configuration options for com.mongodb.client.MongoClient.
The XML namespace changed from client-options to client-settings and removed already deprecated elements and attributes.

Imports are switched from single artifact uber jar to split imports for driver-core, -sync and -reactivestreams.
Deprecations have been removed.

Original pull request: #823.
2020-01-27 11:43:51 +01:00
Mark Paluch
5f29bee6c9 DATAMONGO-2419 - Enable JDK 11+ builds. 2020-01-22 21:19:55 -06:00
Mark Paluch
5b8037a0ad DATAMONGO-2454 - After release cleanups. 2020-01-17 09:47:50 +01:00
Mark Paluch
1a19e140ee DATAMONGO-2454 - Prepare next development iteration. 2020-01-17 09:47:49 +01:00
674 changed files with 19697 additions and 10257 deletions

View File

@@ -1,38 +0,0 @@
language: java
jdk:
- oraclejdk8
before_install:
- mkdir -p downloads
- mkdir -p var/db var/log
- if [[ ! -d downloads/mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION} ]] ; then cd downloads && wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION}.tgz && tar xzf mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION}.tgz && cd ..; fi
- downloads/mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION}/bin/mongod --version
- downloads/mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION}/bin/mongod --dbpath var/db --replSet rs0 --fork --logpath var/log/mongod.log
- sleep 10
- |-
downloads/mongodb-linux-x86_64-ubuntu1604-${MONGO_VERSION}/bin/mongo --eval "rs.initiate({_id: 'rs0', members:[{_id: 0, host: '127.0.0.1:27017'}]});"
sleep 15
env:
matrix:
- MONGO_VERSION=4.1.10
- MONGO_VERSION=4.0.4
- MONGO_VERSION=3.6.12
- MONGO_VERSION=3.4.20
global:
- PROFILE=ci
addons:
apt:
packages:
- oracle-java8-installer
sudo: false
cache:
directories:
- $HOME/.m2
- downloads
script: "mvn clean dependency:list test -P${PROFILE} -Dsort"

89
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 {
@@ -30,51 +30,51 @@ pipeline {
}
}
}
stage('Publish JDK 8 + MongoDB 4.1') {
stage('Publish JDK 8 + MongoDB 4.2') {
when {
changeset "ci/openjdk8-mongodb-4.1/**"
changeset "ci/openjdk8-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.1", "ci/openjdk8-mongodb-4.1/")
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.2.0", "ci/openjdk8-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
stage('Publish JDK 8 + MongoDB 4.2') {
when {
changeset "ci/openjdk8-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
stage('Publish JDK 14 + MongoDB 4.2') {
when {
changeset "ci/openjdk14-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.2", "ci/openjdk8-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
steps {
script {
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()
}
}
}
}
}
}
stage("test: baseline") {
stage("test: baseline (jdk8)") {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.2:latest'
image 'springci/spring-data-openjdk8-with-mongodb-4.2.0:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
@@ -83,23 +83,23 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
stage("Test other configurations") {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: mongodb 4.0") {
stage("test: mongodb 4.0 (jdk8)") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.0:latest'
@@ -111,17 +111,18 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
stage("test: mongodb 4.1") {
stage("test: mongodb 4.2 (jdk8)") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.1:latest'
image 'springci/spring-data-openjdk8-with-mongodb-4.2.0:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
@@ -130,11 +131,31 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
stage("test: baseline (jdk14)") {
agent {
docker {
image 'springci/spring-data-openjdk14-with-mongodb-4.2.0:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 30, unit: 'MINUTES') }
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
@@ -143,7 +164,7 @@ pipeline {
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
branch '3.0.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -175,7 +196,7 @@ pipeline {
stage('Publish documentation') {
when {
branch 'master'
branch '3.0.x'
}
agent {
docker {

View File

@@ -50,12 +50,7 @@ public class MyService {
@Configuration
@EnableMongoRepositories
class ApplicationConfig extends AbstractMongoConfiguration {
@Override
public MongoClient mongoClient() {
return new MongoClient();
}
class ApplicationConfig extends AbstractMongoClientConfiguration {
@Override
protected String getDatabaseName() {
@@ -94,6 +89,143 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
</repository>
----
== Upgrading from 2.x
The 4.0 MongoDB Java Driver does no longer support certain features that have already been deprecated in one of the last minor versions.
Some of the changes affect the initial setup configuration as well as compile/runtime features. We summarized the most typical changes one might encounter.
=== XML Namespace
.Changed XML Namespace Elements and Attributes:
|===
Element / Attribute | 2.x | 3.x
| `<mongo:mongo-client />`
| Used to create a `com.mongodb.MongoClient`
| Now exposes a `com.mongodb.client.MongoClient`
| `<mongo:mongo-client replica-set="..." />`
| Was a comma delimited list of replica set members (host/port)
| Now defines the replica set name. +
Use `<mongo:client-settings cluster-hosts="..." />` instead
| `<mongo:db-factory writeConcern="..." />`
| NONE, NORMAL, SAFE, FSYNC_SAFE, REPLICAS_SAFE, MAJORITY
| W1, W2, W3, UNAKNOWLEDGED, AKNOWLEDGED, JOURNALED, MAJORITY
|===
.Removed XML Namespace Elements and Attributes:
|===
Element / Attribute | Replacement in 3.x | Comment
| `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory mongo-client-ref="..." />`
| Referencing a `com.mongodb.client.MongoClient`.
| `<mongo:mongo-client credentials="..." />`
| `<mongo:mongo-client credential="..." />`
| Single authentication data instead of list.
| `<mongo:client-options />`
| `<mongo:client-settings />`
| See `com.mongodb.MongoClientSettings` for details.
|===
.New XML Namespace Elements and Attributes:
|===
Element | Comment
| `<mongo:db-factory mongo-client-ref="..." />`
| Replacement for `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:mongo-client connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:client-settings />`
| Namespace element for `com.mongodb.MongoClientSettings`.
|===
=== Java Configuration
.Java API changes
|===
Type | Comment
| `MongoClientFactoryBean`
| Creates `com.mongodb.client.MongoClient` instead of `com.mongodb.MongoClient` +
Uses `MongoClientSettings` instead of `MongoClientOptions`.
| `MongoDataIntegrityViolationException`
| Uses `WriteConcernResult` instead of `WriteResult`.
| `BulkOperationException`
| Uses `MongoBulkWriteException` and `com.mongodb.bulk.BulkWriteError` instead of `BulkWriteException` and `com.mongodb.BulkWriteError`
| `ReactiveMongoClientFactoryBean`
| Uses `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
| `ReactiveMongoClientSettingsFactoryBean`
| Now produces `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
|===
.Removed Java API:
|===
2.x | Replacement in 3.x | Comment
| `MongoClientOptionsFactoryBean`
| `MongoClientSettingsFactoryBean`
| Creating a `com.mongodb.MongoClientSettings`.
| `AbstractMongoConfiguration`
| `AbstractMongoClientConfiguration` +
(Available since 2.1)
| Using `com.mongodb.client.MongoClient`.
| `MongoDbFactory#getLegacyDb()`
| -
| -
| `SimpleMongoDbFactory`
| `SimpleMongoClientDbFactory` +
(Available since 2.1)
|
| `MapReduceOptions#getOutputType()`
| `MapReduceOptions#getMapReduceAction()`
| Returns `MapReduceAction` instead of `MapReduceCommand.OutputType`.
| `Meta\|Query` maxScan & snapshot
|
|
|===
=== Other Changes
==== UUID Types
The MongoDB UUID representation can now be configured with different formats.
This has to be done via `MongoClientSettings` as shown in the snippet below.
.UUID Codec Configuration
====
[source,java]
----
static class Config extends AbstractMongoClientConfiguration {
@Override
public void configureClientSettings(MongoClientSettings.Builder builder) {
builder.uuidRepresentation(UuidRepresentation.STANDARD);
}
// ...
}
----
====
== Getting Help
Having trouble with Spring Data? Wed love to help!

View File

@@ -0,0 +1,15 @@
FROM adoptopenjdk/openjdk11:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

View File

@@ -0,0 +1,15 @@
FROM adoptopenjdk/openjdk14:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

View File

@@ -1,14 +1,15 @@
FROM adoptopenjdk/openjdk8:latest
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
RUN RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4 ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.0.list; \
echo ${TZ} > /etc/timezone;
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.0.list
RUN apt-get update
RUN apt-get install -y mongodb-org=4.0.9 mongodb-org-server=4.0.9 mongodb-org-shell=4.0.9 mongodb-org-mongos=4.0.9 mongodb-org-tools=4.0.9
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update ; \
apt-get install -y mongodb-org=4.0.14 mongodb-org-server=4.0.14 mongodb-org-shell=4.0.14 mongodb-org-mongos=4.0.14 mongodb-org-tools=4.0.14 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

View File

@@ -1,14 +0,0 @@
FROM adoptopenjdk/openjdk8:latest
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 4B7C549A058F8B6B
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.1 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.1.list
RUN apt-get update
RUN apt-get install -y mongodb-org-unstable=4.1.13 mongodb-org-unstable-server=4.1.13 mongodb-org-unstable-shell=4.1.13 mongodb-org-unstable-mongos=4.1.13 mongodb-org-unstable-tools=4.1.13
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -1,14 +1,15 @@
FROM adoptopenjdk/openjdk8:latest
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list
RUN apt-get update
RUN apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

26
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.M2</version>
<version>3.0.4.RELEASE</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.3.0.M2</version>
<version>2.3.4.RELEASE</version>
</parent>
<modules>
@@ -26,9 +26,9 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.3.0.M2</springdata.commons>
<mongo>3.12.0</mongo>
<mongo.reactivestreams>1.13.0</mongo.reactivestreams>
<springdata.commons>2.3.4.RELEASE</springdata.commons>
<mongo>4.0.5</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -127,15 +127,25 @@
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<artifactId>mongodb-driver-core</artifactId>
<version>${mongo}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.M2</version>
<version>3.0.4.RELEASE</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.0.0.M2</version>
<version>3.0.4.RELEASE</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.0.0.M2</version>
<version>3.0.4.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -65,6 +65,12 @@
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -82,28 +88,19 @@
</dependency>
<!-- reactive -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo.reactivestreams}</version>
<artifactId>mongodb-driver-sync</artifactId>
<version>${mongo}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<version>${mongo}</version>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo.reactivestreams}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -253,6 +250,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.5.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
@@ -340,12 +344,6 @@
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
<reactor.trace.cancel>true</reactor.trace.cancel>
</systemPropertyVariables>
<properties>
<property>
<name>listener</name>
<value>org.springframework.data.mongodb.test.util.CleanMongoDBJunitRunListener</value>
</property>
</properties>
</configuration>
</plugin>

View File

@@ -19,9 +19,9 @@ import java.util.List;
import org.springframework.dao.DataAccessException;
import com.mongodb.BulkWriteError;
import com.mongodb.BulkWriteException;
import com.mongodb.BulkWriteResult;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.bulk.BulkWriteError;
import com.mongodb.bulk.BulkWriteResult;
/**
* Is thrown when errors occur during bulk operations.
@@ -38,12 +38,12 @@ public class BulkOperationException extends DataAccessException {
private final BulkWriteResult result;
/**
* Creates a new {@link BulkOperationException} with the given message and source {@link BulkWriteException}.
* Creates a new {@link BulkOperationException} with the given message and source {@link MongoBulkWriteException}.
*
* @param message must not be {@literal null}.
* @param source must not be {@literal null}.
*/
public BulkOperationException(String message, BulkWriteException source) {
public BulkOperationException(String message, MongoBulkWriteException source) {
super(message, source);

View File

@@ -25,8 +25,10 @@ public class LazyLoadingException extends UncategorizedDataAccessException {
private static final long serialVersionUID = -7089224903873220037L;
/**
* @param msg
* @param cause
* Constructor for LazyLoadingException.
*
* @param msg the detail message.
* @param cause the exception thrown by underlying data access API.
*/
public LazyLoadingException(String msg, Throwable cause) {
super(msg, cause);

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2011-2019 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 org.bson.codecs.configuration.CodecRegistry;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import com.mongodb.ClientSessionOptions;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
* Interface for factories creating {@link MongoDatabase} instances.
*
* @author Mark Pollack
* @author Thomas Darimont
* @author Christoph Strobl
* @since 3.0
*/
public interface MongoDatabaseFactory extends CodecRegistryProvider, MongoSessionProvider {
/**
* Obtain a {@link MongoDatabase} from the underlying factory.
*
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase() throws DataAccessException;
/**
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
*
* @param dbName must not be {@literal null}.
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
/**
* Get the underlying {@link CodecRegistry} used by the MongoDB Java driver.
*
* @return never {@literal null}.
*/
@Override
default CodecRegistry getCodecRegistry() {
return getMongoDatabase().getCodecRegistry();
}
/**
* Obtain a {@link ClientSession} for given ClientSessionOptions.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
ClientSession getSession(ClientSessionOptions options);
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDatabaseFactory} returning {@link MongoDatabase}
* instances that are aware and bound to a new session with given {@link ClientSessionOptions options}.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
default MongoDatabaseFactory withSession(ClientSessionOptions options) {
return withSession(getSession(options));
}
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDatabaseFactory} returning {@link MongoDatabase}
* instances that are aware and bound to the given session.
*
* @param session must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
MongoDatabaseFactory withSession(ClientSession session);
/**
* Returns if the given {@link MongoDatabaseFactory} is bound to a {@link ClientSession} that has an
* {@link ClientSession#hasActiveTransaction() active transaction}.
*
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
* @since 2.1.3
*/
default boolean isTransactionActive() {
return false;
}
}

View File

@@ -27,7 +27,7 @@ import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
* Helper class for managing a {@link MongoDatabase} instances via {@link MongoDbFactory}. Used for obtaining
* Helper class for managing a {@link MongoDatabase} instances via {@link MongoDatabaseFactory}. Used for obtaining
* {@link ClientSession session bound} resources, such as {@link MongoDatabase} and
* {@link com.mongodb.client.MongoCollection} suitable for transactional usage.
* <p />
@@ -41,93 +41,94 @@ import com.mongodb.client.MongoDatabase;
public class MongoDatabaseUtils {
/**
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory} using
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory} using
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(MongoDbFactory factory) {
public static MongoDatabase getDatabase(MongoDatabaseFactory factory) {
return doGetMongoDatabase(null, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory}.
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param sessionSynchronization the synchronization to use. Must not be {@literal null}.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(MongoDbFactory factory, SessionSynchronization sessionSynchronization) {
public static MongoDatabase getDatabase(MongoDatabaseFactory factory, SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(null, factory, sessionSynchronization);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory} using
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory} using
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory) {
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDatabaseFactory factory) {
return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory}.
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param sessionSynchronization the synchronization to use. Must not be {@literal null}.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(String dbName, MongoDbFactory factory,
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDatabaseFactory factory,
SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(dbName, factory, sessionSynchronization);
}
private static MongoDatabase doGetMongoDatabase(@Nullable String dbName, MongoDbFactory factory,
private static MongoDatabase doGetMongoDatabase(@Nullable String dbName, MongoDatabaseFactory factory,
SessionSynchronization sessionSynchronization) {
Assert.notNull(factory, "Factory must not be null!");
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return StringUtils.hasText(dbName) ? factory.getDb(dbName) : factory.getDb();
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}
ClientSession session = doGetSession(factory, sessionSynchronization);
if (session == null) {
return StringUtils.hasText(dbName) ? factory.getDb(dbName) : factory.getDb();
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}
MongoDbFactory factoryToUse = factory.withSession(session);
return StringUtils.hasText(dbName) ? factoryToUse.getDb(dbName) : factoryToUse.getDb();
MongoDatabaseFactory factoryToUse = factory.withSession(session);
return StringUtils.hasText(dbName) ? factoryToUse.getMongoDatabase(dbName) : factoryToUse.getMongoDatabase();
}
/**
* Check if the {@link MongoDbFactory} is actually bound to a {@link ClientSession} that has an active transaction, or
* if a {@link TransactionSynchronization} has been registered for the {@link MongoDbFactory resource} and if the
* associated {@link ClientSession} has an {@link ClientSession#hasActiveTransaction() active transaction}.
* Check if the {@link MongoDatabaseFactory} is actually bound to a {@link ClientSession} that has an active
* transaction, or if a {@link TransactionSynchronization} has been registered for the {@link MongoDatabaseFactory
* resource} and if the associated {@link ClientSession} has an {@link ClientSession#hasActiveTransaction() active
* transaction}.
*
* @param dbFactory the resource to check transactions for. Must not be {@literal null}.
* @return {@literal true} if the factory has an ongoing transaction.
* @since 2.1.3
*/
public static boolean isTransactionActive(MongoDbFactory dbFactory) {
public static boolean isTransactionActive(MongoDatabaseFactory dbFactory) {
if (dbFactory.isTransactionActive()) {
return true;
@@ -138,7 +139,8 @@ public class MongoDatabaseUtils {
}
@Nullable
private static ClientSession doGetSession(MongoDbFactory dbFactory, SessionSynchronization sessionSynchronization) {
private static ClientSession doGetSession(MongoDatabaseFactory dbFactory,
SessionSynchronization sessionSynchronization) {
MongoResourceHolder resourceHolder = (MongoResourceHolder) TransactionSynchronizationManager.getResource(dbFactory);
@@ -169,7 +171,7 @@ public class MongoDatabaseUtils {
return resourceHolder.getSession();
}
private static ClientSession createClientSession(MongoDbFactory dbFactory) {
private static ClientSession createClientSession(MongoDatabaseFactory dbFactory) {
return dbFactory.getSession(ClientSessionOptions.builder().causallyConsistent(true).build());
}
@@ -184,7 +186,7 @@ public class MongoDatabaseUtils {
private final MongoResourceHolder resourceHolder;
MongoSessionSynchronization(MongoResourceHolder resourceHolder, MongoDbFactory dbFactory) {
MongoSessionSynchronization(MongoResourceHolder resourceHolder, MongoDatabaseFactory dbFactory) {
super(resourceHolder, dbFactory);
this.resourceHolder = resourceHolder;

View File

@@ -15,14 +15,8 @@
*/
package org.springframework.data.mongodb;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
@@ -31,92 +25,33 @@ import com.mongodb.client.MongoDatabase;
* @author Mark Pollack
* @author Thomas Darimont
* @author Christoph Strobl
* @deprecated since 3.0, use {@link MongoDatabaseFactory} instead.
*/
public interface MongoDbFactory extends CodecRegistryProvider, MongoSessionProvider {
@Deprecated
public interface MongoDbFactory extends MongoDatabaseFactory {
/**
* Creates a default {@link MongoDatabase} instance.
*
* @return
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getDb() throws DataAccessException;
/**
* Creates a {@link DB} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
* @return
* @throws DataAccessException
*/
MongoDatabase getDb(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
/**
* Get the legacy database entry point. Please consider {@link #getDb()} instead.
*
* @return
* @deprecated since 2.1, use {@link #getDb()}. This method will be removed with a future version as it works only
* with the legacy MongoDB driver.
* @deprecated since 3.0. Use {@link #getMongoDatabase()} instead.
*/
@Deprecated
DB getLegacyDb();
/**
* Get the underlying {@link CodecRegistry} used by the MongoDB Java driver.
*
* @return never {@literal null}.
*/
@Override
default CodecRegistry getCodecRegistry() {
return getDb().getCodecRegistry();
default MongoDatabase getDb() throws DataAccessException {
return getMongoDatabase();
}
/**
* Obtain a {@link ClientSession} for given ClientSessionOptions.
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
*
* @param options must not be {@literal null}.
* @param dbName must not be {@literal null} or empty.
* @return never {@literal null}.
* @since 2.1
* @throws DataAccessException
* @deprecated since 3.0. Use {@link #getMongoDatabase(String)} instead.
*/
ClientSession getSession(ClientSessionOptions options);
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDbFactory} returning {@link MongoDatabase} instances
* that are aware and bound to a new session with given {@link ClientSessionOptions options}.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
default MongoDbFactory withSession(ClientSessionOptions options) {
return withSession(getSession(options));
}
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDbFactory} returning {@link MongoDatabase} instances
* that are aware and bound to the given session.
*
* @param session must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
MongoDbFactory withSession(ClientSession session);
/**
* Returns if the given {@link MongoDbFactory} is bound to a {@link ClientSession} that has an
* {@link ClientSession#hasActiveTransaction() active transaction}.
*
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
* @since 2.1.3
*/
default boolean isTransactionActive() {
return false;
@Deprecated
default MongoDatabase getDb(String dbName) throws DataAccessException {
return getMongoDatabase(dbName);
}
}

View File

@@ -36,15 +36,15 @@ import com.mongodb.client.ClientSession;
class MongoResourceHolder extends ResourceHolderSupport {
private @Nullable ClientSession session;
private MongoDbFactory dbFactory;
private MongoDatabaseFactory dbFactory;
/**
* Create a new {@link MongoResourceHolder} for a given {@link ClientSession session}.
*
* @param session the associated {@link ClientSession}. Can be {@literal null}.
* @param dbFactory the associated {@link MongoDbFactory}. must not be {@literal null}.
* @param dbFactory the associated {@link MongoDatabaseFactory}. must not be {@literal null}.
*/
MongoResourceHolder(@Nullable ClientSession session, MongoDbFactory dbFactory) {
MongoResourceHolder(@Nullable ClientSession session, MongoDatabaseFactory dbFactory) {
this.session = session;
this.dbFactory = dbFactory;
@@ -75,9 +75,9 @@ class MongoResourceHolder extends ResourceHolderSupport {
}
/**
* @return the associated {@link MongoDbFactory}.
* @return the associated {@link MongoDatabaseFactory}.
*/
public MongoDbFactory getDbFactory() {
public MongoDatabaseFactory getDbFactory() {
return dbFactory;
}

View File

@@ -36,17 +36,18 @@ import com.mongodb.client.ClientSession;
/**
* A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages
* {@link ClientSession} based transactions for a single {@link MongoDbFactory}.
* {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
* <p />
* Binds a {@link ClientSession} from the specified {@link MongoDbFactory} to the thread.
* Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
* <p />
* {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal
* consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction()
* commit} or {@link ClientSession#abortTransaction() abort} a transaction.
* <p />
* Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via
* {@link MongoDatabaseUtils#getDatabase(MongoDbFactory)} instead of a standard {@link MongoDbFactory#getDb()} call.
* Spring classes such as {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
* {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard
* {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as
* {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
* <p />
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override
* {@link #doCommit(MongoTransactionObject)} to implement the
@@ -58,46 +59,46 @@ import com.mongodb.client.ClientSession;
* @currentRead Shadow's Edge - Brent Weeks
* @since 2.1
* @see <a href="https://www.mongodb.com/transactions">MongoDB Transaction Documentation</a>
* @see MongoDatabaseUtils#getDatabase(MongoDbFactory, SessionSynchronization)
* @see MongoDatabaseUtils#getDatabase(MongoDatabaseFactory, SessionSynchronization)
*/
public class MongoTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
private @Nullable MongoDbFactory dbFactory;
private @Nullable MongoDatabaseFactory dbFactory;
private @Nullable TransactionOptions options;
/**
* Create a new {@link MongoTransactionManager} for bean-style usage.
* <p />
* <strong>Note:</strong>The {@link MongoDbFactory db factory} has to be {@link #setDbFactory(MongoDbFactory) set}
* before using the instance. Use this constructor to prepare a {@link MongoTransactionManager} via a
* {@link org.springframework.beans.factory.BeanFactory}.
* <strong>Note:</strong>The {@link MongoDatabaseFactory db factory} has to be
* {@link #setDbFactory(MongoDatabaseFactory) set} before using the instance. Use this constructor to prepare a
* {@link MongoTransactionManager} via a {@link org.springframework.beans.factory.BeanFactory}.
* <p />
* Optionally it is possible to set default {@link TransactionOptions transaction options} defining
* {@link com.mongodb.ReadConcern} and {@link com.mongodb.WriteConcern}.
*
* @see #setDbFactory(MongoDbFactory)
* @see #setDbFactory(MongoDatabaseFactory)
* @see #setTransactionSynchronization(int)
*/
public MongoTransactionManager() {}
/**
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDbFactory}.
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDatabaseFactory}.
*
* @param dbFactory must not be {@literal null}.
*/
public MongoTransactionManager(MongoDbFactory dbFactory) {
public MongoTransactionManager(MongoDatabaseFactory dbFactory) {
this(dbFactory, null);
}
/**
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDbFactory} applying the
* given {@link TransactionOptions options}, if present, when starting a new transaction.
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDatabaseFactory}
* applying the given {@link TransactionOptions options}, if present, when starting a new transaction.
*
* @param dbFactory must not be {@literal null}.
* @param options can be {@literal null}.
*/
public MongoTransactionManager(MongoDbFactory dbFactory, @Nullable TransactionOptions options) {
public MongoTransactionManager(MongoDatabaseFactory dbFactory, @Nullable TransactionOptions options) {
Assert.notNull(dbFactory, "DbFactory must not be null!");
@@ -295,11 +296,11 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
}
/**
* Set the {@link MongoDbFactory} that this instance should manage transactions for.
* Set the {@link MongoDatabaseFactory} that this instance should manage transactions for.
*
* @param dbFactory must not be {@literal null}.
*/
public void setDbFactory(MongoDbFactory dbFactory) {
public void setDbFactory(MongoDatabaseFactory dbFactory) {
Assert.notNull(dbFactory, "DbFactory must not be null!");
this.dbFactory = dbFactory;
@@ -315,12 +316,12 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
}
/**
* Get the {@link MongoDbFactory} that this instance manages transactions for.
* Get the {@link MongoDatabaseFactory} that this instance manages transactions for.
*
* @return can be {@literal null}.
*/
@Nullable
public MongoDbFactory getDbFactory() {
public MongoDatabaseFactory getDbFactory() {
return dbFactory;
}
@@ -329,7 +330,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
* @see org.springframework.transaction.support.ResourceTransactionManager#getResourceFactory()
*/
@Override
public MongoDbFactory getResourceFactory() {
public MongoDatabaseFactory getResourceFactory() {
return getRequiredDbFactory();
}
@@ -344,7 +345,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
private MongoResourceHolder newResourceHolder(TransactionDefinition definition, ClientSessionOptions options) {
MongoDbFactory dbFactory = getResourceFactory();
MongoDatabaseFactory dbFactory = getResourceFactory();
MongoResourceHolder resourceHolder = new MongoResourceHolder(dbFactory.getSession(options), dbFactory);
resourceHolder.setTimeoutIfNotDefaulted(determineTimeout(definition));
@@ -355,7 +356,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
/**
* @throws IllegalStateException if {@link #dbFactory} is {@literal null}.
*/
private MongoDbFactory getRequiredDbFactory() {
private MongoDatabaseFactory getRequiredDbFactory() {
Assert.state(dbFactory != null,
"MongoTransactionManager operates upon a MongoDbFactory. Did you forget to provide one? It's required.");

View File

@@ -31,6 +31,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
*/
public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
@@ -38,19 +39,19 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
/**
* Creates a default {@link MongoDatabase} instance.
*
* @return
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase() throws DataAccessException;
Mono<MongoDatabase> getMongoDatabase() throws DataAccessException;
/**
* Creates a {@link MongoDatabase} instance to access the database with the given name.
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
* @return
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase(String dbName) throws DataAccessException;
Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
@@ -64,10 +65,7 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
*
* @return never {@literal null}.
*/
@Override
default CodecRegistry getCodecRegistry() {
return getMongoDatabase().getCodecRegistry();
}
CodecRegistry getCodecRegistry();
/**
* Obtain a {@link Mono} emitting a {@link ClientSession} for given {@link ClientSessionOptions options}.

View File

@@ -41,6 +41,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.2
*/
public class ReactiveMongoDatabaseUtils {
@@ -142,14 +143,13 @@ public class ReactiveMongoDatabaseUtils {
.flatMap(synchronizationManager -> {
return doGetSession(synchronizationManager, factory, sessionSynchronization) //
.map(it -> getMongoDatabaseOrDefault(dbName, factory.withSession(it)));
})
.onErrorResume(NoTransactionException.class,
e -> Mono.fromSupplier(() -> getMongoDatabaseOrDefault(dbName, factory)))
.defaultIfEmpty(getMongoDatabaseOrDefault(dbName, factory));
.flatMap(it -> getMongoDatabaseOrDefault(dbName, factory.withSession(it)));
}) //
.onErrorResume(NoTransactionException.class, e -> getMongoDatabaseOrDefault(dbName, factory))
.switchIfEmpty(getMongoDatabaseOrDefault(dbName, factory));
}
private static MongoDatabase getMongoDatabaseOrDefault(@Nullable String dbName,
private static Mono<MongoDatabase> getMongoDatabaseOrDefault(@Nullable String dbName,
ReactiveMongoDatabaseFactory factory) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}

View File

@@ -42,7 +42,7 @@ class ReactiveMongoResourceHolder extends ResourceHolderSupport {
* Create a new {@link ReactiveMongoResourceHolder} for a given {@link ClientSession session}.
*
* @param session the associated {@link ClientSession}. Can be {@literal null}.
* @param databaseFactory the associated {@link MongoDbFactory}. must not be {@literal null}.
* @param databaseFactory the associated {@link MongoDatabaseFactory}. must not be {@literal null}.
*/
ReactiveMongoResourceHolder(@Nullable ClientSession session, ReactiveMongoDatabaseFactory databaseFactory) {
@@ -99,7 +99,7 @@ class ReactiveMongoResourceHolder extends ResourceHolderSupport {
* If the {@link ReactiveMongoResourceHolder} is {@link #hasSession() not already associated} with a
* {@link ClientSession} the given value is {@link #setSession(ClientSession) set} and returned, otherwise the current
* bound session is returned.
*
*
* @param session
* @return
*/

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2020 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.util.Version;
import org.springframework.util.StringUtils;
import com.mongodb.MongoDriverInformation;
/**
* Class that exposes the SpringData MongoDB specific information like the current {@link Version} or
* {@link MongoDriverInformation driver information}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class SpringDataMongoDB {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringDataMongoDB.class);
private static final Version FALLBACK_VERSION = new Version(3);
private static final MongoDriverInformation DRIVER_INFORMATION = MongoDriverInformation
.builder(MongoDriverInformation.builder().build()).driverName("spring-data").build();
/**
* Obtain the SpringData MongoDB specific driver information.
*
* @return never {@literal null}.
*/
public static MongoDriverInformation driverInformation() {
return DRIVER_INFORMATION;
}
/**
* Fetches the "Implementation-Version" manifest attribute from the jar file.
* <p />
* Note that some ClassLoaders do not expose the package metadata, hence this class might not be able to determine the
* version in all environments. In this case the current Major version is returned as a fallback.
*
* @return never {@literal null}.
*/
public static Version version() {
Package pkg = SpringDataMongoDB.class.getPackage();
String versionString = (pkg != null ? pkg.getImplementationVersion() : null);
if (!StringUtils.hasText(versionString)) {
LOGGER.debug("Unable to find Spring Data MongoDB version.");
return FALLBACK_VERSION;
}
try {
return Version.parse(versionString);
} catch (Exception e) {
LOGGER.debug("Cannot read Spring Data MongoDB version '{}'.", versionString);
}
return FALLBACK_VERSION;
}
}

View File

@@ -17,17 +17,22 @@ package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.lang.Nullable;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
/**
* Base class for Spring Data MongoDB configuration using JavaConfig with {@link com.mongodb.client.MongoClient}.
@@ -35,40 +40,44 @@ import com.mongodb.client.MongoClient;
* @author Christoph Strobl
* @since 2.1
* @see MongoConfigurationSupport
* @see AbstractMongoConfiguration
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public abstract class AbstractMongoClientConfiguration extends MongoConfigurationSupport {
/**
* Return the {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want to expose a
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}. <br />
* Override {@link #mongoClientSettings()} to configure connection details.
*
* @return
* @return never {@literal null}.
* @see #mongoClientSettings()
* @see #configureClientSettings(Builder)
*/
public abstract MongoClient mongoClient();
public MongoClient mongoClient() {
return createMongoClient(mongoClientSettings());
}
/**
* Creates a {@link MongoTemplate}.
*
* @return
* @see #mongoDbFactory()
* @see #mappingMongoConverter(MongoDatabaseFactory, MongoCustomConversions, MongoMappingContext)
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
public MongoTemplate mongoTemplate(MongoDatabaseFactory databaseFactory, MappingMongoConverter converter) {
return new MongoTemplate(databaseFactory, converter);
}
/**
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link MongoClient}
* instance configured in {@link #mongoClient()}.
* Creates a {@link org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory} to be used by the
* {@link MongoTemplate}. Will use the {@link MongoClient} instance configured in {@link #mongoClient()}.
*
* @see #mongoClient()
* @see #mongoTemplate()
* @return
* @see #mongoTemplate(MongoDatabaseFactory, MappingMongoConverter)
*/
@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoClientDbFactory(mongoClient(), getDatabaseName());
public MongoDatabaseFactory mongoDbFactory() {
return new SimpleMongoClientDatabaseFactory(mongoClient(), getDatabaseName());
}
/**
@@ -91,22 +100,32 @@ public abstract class AbstractMongoClientConfiguration extends MongoConfiguratio
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
* {@link #mongoMappingContext(MongoCustomConversions)}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoMappingContext(MongoCustomConversions)
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
DbRefResolver dbRefResolver = new DefaultDbRefResolver(databaseFactory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
converter.setCustomConversions(customConversions);
converter.setCodecRegistryProvider(databaseFactory);
return converter;
}
/**
* Create the Reactive Streams {@link com.mongodb.reactivestreams.client.MongoClient} instance with given
* {@link MongoClientSettings}.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClient createMongoClient(MongoClientSettings settings) {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
}

View File

@@ -1,121 +0,0 @@
/*
* Copyright 2011-2020 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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.lang.Nullable;
import com.mongodb.MongoClient;
/**
* Base class for Spring Data MongoDB configuration using JavaConfig with {@link com.mongodb.MongoClient}.
* <p />
* <strong>INFO:</strong>In case you want to use {@link com.mongodb.client.MongoClients} for configuration please refer
* to {@link AbstractMongoClientConfiguration}.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Ryan Tenney
* @author Christoph Strobl
* @author Mark Paluch
* @see MongoConfigurationSupport
* @see AbstractMongoClientConfiguration
* @deprecated since 2.2 in favor of {@link AbstractMongoClientConfiguration}.
*/
@Configuration
@Deprecated
public abstract class AbstractMongoConfiguration extends MongoConfigurationSupport {
/**
* Return the {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want to expose a
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
*
* @return
*/
public abstract MongoClient mongoClient();
/**
* Creates a {@link MongoTemplate}.
*
* @return
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
/**
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link MongoClient}
* instance configured in {@link #mongoClient()}.
*
* @see #mongoClient()
* @see #mongoTemplate()
* @return
*/
@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(mongoClient(), getDatabaseName());
}
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
* {@link AbstractMongoConfiguration} the base package will be considered {@code com.acme} unless the method is
* overridden to implement alternate behavior.
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
* @deprecated use {@link #getMappingBasePackages()} instead.
*/
@Deprecated
@Nullable
protected String getMappingBasePackage() {
Package mappingBasePackage = getClass().getPackage();
return mappingBasePackage == null ? null : mappingBasePackage.getName();
}
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
return converter;
}
}

View File

@@ -18,13 +18,19 @@ package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
/**
* Base class for reactive Spring Data MongoDB configuration using JavaConfig.
@@ -34,25 +40,33 @@ import com.mongodb.reactivestreams.client.MongoClient;
* @since 2.0
* @see MongoConfigurationSupport
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurationSupport {
/**
* Return the Reactive Streams {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want
* to expose a {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
* to expose a {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}. <br />
* Override {@link #mongoClientSettings()} to configure connection details.
*
* @return never {@literal null}.
* @see #mongoClientSettings()
* @see #configureClientSettings(Builder)
*/
public abstract MongoClient reactiveMongoClient();
public MongoClient reactiveMongoClient() {
return createReactiveMongoClient(mongoClientSettings());
}
/**
* Creates {@link ReactiveMongoOperations}.
*
* @see #reactiveMongoDbFactory()
* @see #mappingMongoConverter(ReactiveMongoDatabaseFactory, MongoCustomConversions, MongoMappingContext)
* @return never {@literal null}.
*/
@Bean
public ReactiveMongoOperations reactiveMongoTemplate() throws Exception {
return new ReactiveMongoTemplate(reactiveMongoDbFactory(), mappingMongoConverter());
public ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory databaseFactory,
MappingMongoConverter mongoConverter) {
return new ReactiveMongoTemplate(databaseFactory, mongoConverter);
}
/**
@@ -60,7 +74,7 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat
* {@link MongoClient} instance configured in {@link #reactiveMongoClient()}.
*
* @see #reactiveMongoClient()
* @see #reactiveMongoTemplate()
* @see #reactiveMongoTemplate(ReactiveMongoDatabaseFactory, MappingMongoConverter)
* @return never {@literal null}.
*/
@Bean
@@ -70,21 +84,31 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #reactiveMongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
* {@link #mongoMappingContext(MongoCustomConversions)}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoMappingContext(MongoCustomConversions)
* @see #reactiveMongoDbFactory()
* @return never {@literal null}.
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
public MappingMongoConverter mappingMongoConverter(ReactiveMongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(reactiveMongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
converter.setCustomConversions(customConversions);
converter.setCodecRegistryProvider(databaseFactory);
return converter;
}
/**
* Create the Reactive Streams {@link MongoClient} instance with given {@link MongoClientSettings}.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClient createReactiveMongoClient(MongoClientSettings settings) {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2019 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.beans.PropertyEditorSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.ConnectionString;
/**
* Parse a {@link String} to a {@link com.mongodb.ConnectionString}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class ConnectionStringPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(@Nullable String connectionString) {
if (!StringUtils.hasText(connectionString)) {
return;
}
setValue(new ConnectionString(connectionString));
}
}

View File

@@ -42,21 +42,21 @@ public @interface EnableMongoAuditing {
/**
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
*
* @return
* @return empty {@link String} by default.
*/
String auditorAwareRef() default "";
/**
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
*
* @return
* @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
* @return {@literal true} by default.
*/
boolean modifyOnCreate() default true;
@@ -64,7 +64,7 @@ public @interface EnableMongoAuditing {
* 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
* @return empty {@link String} by default.
*/
String dateTimeProviderRef() default "";
}

View File

@@ -96,6 +96,9 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
id = StringUtils.hasText(id) ? id : DEFAULT_CONVERTER_BEAN_NAME;
String autoIndexCreation = element.getAttribute("auto-index-creation");
boolean autoIndexCreationEnabled = StringUtils.hasText(autoIndexCreation) && Boolean.valueOf(autoIndexCreation);
parserContext.pushContainingComponent(new CompositeComponentDefinition("Mapping Mongo Converter", element));
BeanDefinition conversionsDefinition = getCustomConversions(element, parserContext);
@@ -199,6 +202,11 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
@Nullable BeanDefinition conversionsDefinition, @Nullable String converterId) {
return potentiallyCreateMappingContext(element, parserContext, conversionsDefinition, converterId, false);
}
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
@Nullable BeanDefinition conversionsDefinition, @Nullable String converterId, boolean autoIndexCreation) {
String ctxRef = element.getAttribute("mapping-context-ref");
@@ -226,6 +234,8 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
}
mappingContextBuilder.addPropertyValue("autoIndexCreation", autoIndexCreation);
parseFieldNamingStrategy(element, parserContext.getReaderContext(), mappingContextBuilder);
ctxRef = converterId == null || DEFAULT_CONVERTER_BEAN_NAME.equals(converterId) ? MAPPING_CONTEXT_BEAN_NAME

View File

@@ -50,10 +50,11 @@ public class MongoClientParser implements BeanDefinitionParser {
ParsingUtils.setPropertyValue(builder, element, "port", "port");
ParsingUtils.setPropertyValue(builder, element, "host", "host");
ParsingUtils.setPropertyValue(builder, element, "credentials", "credentials");
ParsingUtils.setPropertyValue(builder, element, "credential", "credential");
ParsingUtils.setPropertyValue(builder, element, "replica-set", "replicaSet");
ParsingUtils.setPropertyValue(builder, element, "connection-string", "connectionString");
MongoParsingUtils.parseMongoClientOptions(element, builder);
MongoParsingUtils.parseReplicaSet(element, builder);
MongoParsingUtils.parseMongoClientSettings(element, builder);
String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO_BEAN_NAME;
@@ -62,22 +63,34 @@ public class MongoClientParser implements BeanDefinitionParser {
BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId);
parserContext.registerBeanComponent(mongoComponent);
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils
.getServerAddressPropertyEditorBuilder());
BeanComponentDefinition connectionStringPropertyEditor = helper
.getComponent(MongoParsingUtils.getConnectionStringPropertyEditorBuilder());
parserContext.registerBeanComponent(connectionStringPropertyEditor);
BeanComponentDefinition serverAddressPropertyEditor = helper
.getComponent(MongoParsingUtils.getServerAddressPropertyEditorBuilder());
parserContext.registerBeanComponent(serverAddressPropertyEditor);
BeanComponentDefinition writeConcernEditor = helper.getComponent(MongoParsingUtils
.getWriteConcernPropertyEditorBuilder());
BeanComponentDefinition writeConcernEditor = helper
.getComponent(MongoParsingUtils.getWriteConcernPropertyEditorBuilder());
parserContext.registerBeanComponent(writeConcernEditor);
BeanComponentDefinition readPreferenceEditor = helper.getComponent(MongoParsingUtils
.getReadPreferencePropertyEditorBuilder());
BeanComponentDefinition readConcernEditor = helper
.getComponent(MongoParsingUtils.getReadConcernPropertyEditorBuilder());
parserContext.registerBeanComponent(readConcernEditor);
BeanComponentDefinition readPreferenceEditor = helper
.getComponent(MongoParsingUtils.getReadPreferencePropertyEditorBuilder());
parserContext.registerBeanComponent(readPreferenceEditor);
BeanComponentDefinition credentialsEditor = helper.getComponent(MongoParsingUtils
.getMongoCredentialPropertyEditor());
BeanComponentDefinition credentialsEditor = helper
.getComponent(MongoParsingUtils.getMongoCredentialPropertyEditor());
parserContext.registerBeanComponent(credentialsEditor);
BeanComponentDefinition uuidRepresentationEditor = helper
.getComponent(MongoParsingUtils.getUUidRepresentationEditorBuilder());
parserContext.registerBeanComponent(uuidRepresentationEditor);
parserContext.popAndRegisterContainingComponent();
return mongoComponent.getBeanDefinition();

View File

@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.bson.UuidRepresentation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -31,11 +32,15 @@ import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingSt
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
/**
* Base class for Spring Data MongoDB to be extended for JavaConfiguration usage.
*
@@ -75,11 +80,12 @@ public abstract class MongoConfigurationSupport {
* @throws ClassNotFoundException
*/
@Bean
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions)
throws ClassNotFoundException {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());
@@ -88,14 +94,30 @@ public abstract class MongoConfigurationSupport {
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
* {@link #mongoMappingContext()}. Returns an empty {@link MongoCustomConversions} instance by default.
* {@link CustomConversions} will be registered with the
* {@link org.springframework.data.mongodb.core.convert.MappingMongoConverter} and {@link MongoMappingContext}.
* Returns an empty {@link MongoCustomConversions} instance by default.
* <p>
* <strong>NOTE:</strong> Use {@link #configureConverters(MongoConverterConfigurationAdapter)} to configure MongoDB
* native simple types and register custom {@link Converter converters}.
*
* @return must not be {@literal null}.
*/
@Bean
public CustomConversions customConversions() {
return new MongoCustomConversions(Collections.emptyList());
public MongoCustomConversions customConversions() {
return MongoCustomConversions.create(this::configureConverters);
}
/**
* Configuration hook for {@link MongoCustomConversions} creation.
*
* @param converterConfigurationAdapter never {@literal null}.
* @since 2.3
* @see MongoConverterConfigurationAdapter#useNativeDriverJavaTimeCodecs()
* @see MongoConverterConfigurationAdapter#useSpringDataJavaTimeCodecs()
*/
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
}
/**
@@ -177,11 +199,36 @@ public abstract class MongoConfigurationSupport {
* Configure whether to automatically create indices for domain types by deriving the
* {@link org.springframework.data.mongodb.core.index.IndexDefinition} from the entity or not.
*
* @return {@literal true} by default. <br />
* <strong>INFO</strong>: As of 3.x the default will be set to {@literal false}.
* @return {@literal false} by default. <br />
* <strong>INFO</strong>: As of 3.x the default is set to {@literal false}; In 2.x it was {@literal true}.
* @since 2.2
*/
protected boolean autoIndexCreation() {
return true;
return false;
}
/**
* Return the {@link MongoClientSettings} used to create the actual {@literal MongoClient}. <br />
* Override either this method, or use {@link #configureClientSettings(Builder)} to alter the setup.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClientSettings mongoClientSettings() {
MongoClientSettings.Builder builder = MongoClientSettings.builder();
builder.uuidRepresentation(UuidRepresentation.JAVA_LEGACY);
configureClientSettings(builder);
return builder.build();
}
/**
* Configure {@link MongoClientSettings} via its {@link Builder} API.
*
* @param builder never {@literal null}.
* @since 3.0
*/
protected void configureClientSettings(MongoClientSettings.Builder builder) {
// customization hook
}
}

View File

@@ -32,14 +32,12 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mongodb.core.MongoClientFactoryBean;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import com.mongodb.Mongo;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoURI;
import com.mongodb.ConnectionString;
/**
* {@link BeanDefinitionParser} to parse {@code db-factory} elements into {@link BeanDefinition}s.
@@ -84,10 +82,11 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// Common setup
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SimpleMongoClientDatabaseFactory.class);
setPropertyValue(dbFactoryBuilder, element, "write-concern", "writeConcern");
BeanDefinition mongoUri = getMongoUri(element, parserContext);
BeanDefinition mongoUri = getConnectionString(element, parserContext);
if (mongoUri != null) {
@@ -97,7 +96,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
String mongoRef = element.getAttribute("mongo-ref");
String mongoRef = element.getAttribute("mongo-client-ref");
String dbname = element.getAttribute("dbname");
// Defaulting
@@ -119,8 +119,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
}
/**
* Registers a default {@link BeanDefinition} of a {@link Mongo} instance and returns the name under which the
* {@link Mongo} instance was registered under.
* Registers a default {@link BeanDefinition} of a {@link com.mongodb.client.MongoClient} instance and returns the
* name under which the {@link com.mongodb.client.MongoClient} instance was registered under.
*
* @param element must not be {@literal null}.
* @param parserContext must not be {@literal null}.
@@ -136,8 +136,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
}
/**
* Creates a {@link BeanDefinition} for a {@link MongoURI} or {@link MongoClientURI} depending on configured
* attributes. <br />
* Creates a {@link BeanDefinition} for a {@link ConnectionString} depending on configured attributes. <br />
* Errors when configured element contains {@literal uri} or {@literal client-uri} along with other attributes except
* {@literal write-concern} and/or {@literal id}.
*
@@ -146,11 +145,19 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
* @return {@literal null} in case no client-/uri defined.
*/
@Nullable
private BeanDefinition getMongoUri(Element element, ParserContext parserContext) {
private BeanDefinition getConnectionString(Element element, ParserContext parserContext) {
boolean hasClientUri = element.hasAttribute("client-uri");
String type = null;
if (!hasClientUri && !element.hasAttribute("uri")) {
if (element.hasAttribute("client-uri")) {
type = "client-uri";
} else if (element.hasAttribute("connection-string")) {
type = "connection-string";
} else if (element.hasAttribute("uri")) {
type = "uri";
}
if (!StringUtils.hasText(type)) {
return null;
}
@@ -164,16 +171,12 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
if (element.getAttributes().getLength() > allowedAttributesCount) {
parserContext.getReaderContext().error(
"Configure either " + (hasClientUri ? "Mongo Client URI" : "Mongo URI") + " or details individually!",
parserContext.getReaderContext().error("Configure either MongoDB " + type + " or details individually!",
parserContext.extractSource(element));
}
Class<?> type = MongoClientURI.class;
String uri = hasClientUri ? element.getAttribute("client-uri") : element.getAttribute("uri");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
builder.addConstructorArgValue(uri);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConnectionString.class);
builder.addConstructorArgValue(element.getAttribute(type));
return builder.getBeanDefinition();
}

View File

@@ -24,7 +24,7 @@ import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.data.mongodb.core.MongoClientOptionsFactoryBean;
import org.springframework.data.mongodb.core.MongoClientSettingsFactoryBean;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -43,60 +43,78 @@ abstract class MongoParsingUtils {
private MongoParsingUtils() {}
/**
* Parses the mongo replica-set element.
*
* @param parserContext the parser context
* @param element the mongo element
* @param mongoBuilder the bean definition builder to populate
* @return
*/
static void parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) {
setPropertyValue(mongoBuilder, element, "replica-set", "replicaSetSeeds");
}
/**
* Parses the {@code mongo:client-options} sub-element. Populates the given attribute factory with the proper
* Parses the {@code mongo:client-settings} sub-element. Populates the given attribute factory with the proper
* attributes.
*
* @param element must not be {@literal null}.
* @param mongoClientBuilder must not be {@literal null}.
*
* @param element
* @param mongoClientBuilder
* @return
* @since 1.7
* @since 3.0
*/
public static boolean parseMongoClientOptions(Element element, BeanDefinitionBuilder mongoClientBuilder) {
public static boolean parseMongoClientSettings(Element element, BeanDefinitionBuilder mongoClientBuilder) {
Element optionsElement = DomUtils.getChildElementByTagName(element, "client-options");
if (optionsElement == null) {
Element settingsElement = DomUtils.getChildElementByTagName(element, "client-settings");
if (settingsElement == null) {
return false;
}
BeanDefinitionBuilder clientOptionsDefBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoClientOptionsFactoryBean.class);
.genericBeanDefinition(MongoClientSettingsFactoryBean.class);
setPropertyValue(clientOptionsDefBuilder, optionsElement, "description", "description");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-connections-per-host", "minConnectionsPerHost");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier",
"threadsAllowedToBlockForConnectionMultiplier");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-idle-time", "maxConnectionIdleTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-life-time", "maxConnectionLifeTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "read-preference", "readPreference");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "write-concern", "writeConcern");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-frequency", "heartbeatFrequency");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-heartbeat-frequency", "minHeartbeatFrequency");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-connect-timeout", "heartbeatConnectTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-socket-timeout", "heartbeatSocketTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "ssl", "ssl");
setPropertyReference(clientOptionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory");
setPropertyReference(clientOptionsDefBuilder, optionsElement, "encryption-settings-ref", "autoEncryptionSettings");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "server-selection-timeout", "serverSelectionTimeout");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "application-name", "applicationName");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "read-preference", "readPreference");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "read-concern", "readConcern");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "write-concern", "writeConcern");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "retry-reads", "retryReads");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "retry-writes", "retryWrites");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "uuid-representation", "uUidRepresentation");
mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition());
// SocketSettings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-connect-timeout", "socketConnectTimeoutMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-read-timeout", "socketReadTimeoutMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-receive-buffer-size", "socketReceiveBufferSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-send-buffer-size", "socketSendBufferSize");
// Server Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "server-heartbeat-frequency",
"serverHeartbeatFrequencyMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "server-min-heartbeat-frequency",
"serverMinHeartbeatFrequencyMS");
// Cluster Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-srv-host", "clusterSrvHost");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-hosts", "clusterHosts");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-connection-mode", "clusterConnectionMode");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-type", "custerRequiredClusterType");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-local-threshold", "clusterLocalThresholdMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-server-selection-timeout",
"clusterServerSelectionTimeoutMS");
// Connection Pool Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-size", "poolMaxSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-min-size", "poolMinSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-wait-time", "poolMaxWaitTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-connection-life-time",
"poolMaxConnectionLifeTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-connection-idle-time",
"poolMaxConnectionIdleTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-maintenance-initial-delay",
"poolMaintenanceInitialDelayMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-maintenance-frequency",
"poolMaintenanceFrequencyMS");
// SSL Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-enabled", "sslEnabled");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-invalid-host-name-allowed",
"sslInvalidHostNameAllowed");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-provider", "sslProvider");
// Field level encryption
setPropertyReference(clientOptionsDefBuilder, settingsElement, "encryption-settings-ref", "autoEncryptionSettings");
// and the rest
mongoClientBuilder.addPropertyValue("mongoClientSettings", clientOptionsDefBuilder.getBeanDefinition());
return true;
}
@@ -118,6 +136,24 @@ abstract class MongoParsingUtils {
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ReadConcernPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getReadConcernPropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ReadConcern", ReadConcernPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
/**
* One should only register one bean definition but want to have the convenience of using
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
@@ -125,7 +161,7 @@ abstract class MongoParsingUtils {
*/
static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() {
Map<String, String> customEditors = new ManagedMap<String, String>();
Map<String, String> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ServerAddress[]",
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
@@ -143,7 +179,7 @@ abstract class MongoParsingUtils {
*/
static BeanDefinitionBuilder getReadPreferencePropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ReadPreference", ReadPreferencePropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
@@ -169,4 +205,41 @@ abstract class MongoParsingUtils {
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ConnectionStringPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getConnectionStringPropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ConnectionString", ConnectionStringPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ConnectionStringPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getUUidRepresentationEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("org.bson.UuidRepresentation", UUidRepresentationPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2019 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.beans.PropertyEditorSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.ReadConcern;
import com.mongodb.ReadConcernLevel;
/**
* Parse a {@link String} to a {@link ReadConcern}. If it is a well know {@link String} as identified by the
* {@link ReadConcernLevel#fromString(String)}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class ReadConcernPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
public void setAsText(@Nullable String readConcernString) {
if (!StringUtils.hasText(readConcernString)) {
return;
}
setValue(new ReadConcern(ReadConcernLevel.fromString(readConcernString)));
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2020 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.beans.PropertyEditorSupport;
import org.bson.UuidRepresentation;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Parse a {@link String} to a {@link UuidRepresentation}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class UUidRepresentationPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(@Nullable String value) {
if (!StringUtils.hasText(value)) {
return;
}
setValue(UuidRepresentation.valueOf(value));
}
}

View File

@@ -34,7 +34,7 @@ import com.mongodb.WriteConcern;
public class WriteConcernPropertyEditor extends PropertyEditorSupport {
/**
* Parse a string to a List<ServerAddress>
* Parse a string to a {@link WriteConcern}.
*/
@Override
public void setAsText(@Nullable String writeConcernString) {
@@ -51,6 +51,5 @@ public class WriteConcernPropertyEditor extends PropertyEditorSupport {
// pass on the string to the constructor
setValue(new WriteConcern(writeConcernString));
}
}
}

View File

@@ -94,7 +94,6 @@ public interface BulkOperations {
* Add a list of updates to the bulk operation. For each update request, all matching documents are updated.
*
* @param updates Update operations to perform.
* @return The bulk operation.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations updateMulti(List<Pair<Query, Update>> updates);
@@ -105,7 +104,6 @@ public interface BulkOperations {
*
* @param query Update criteria.
* @param update Update operation to perform.
* @return The bulk operation.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations upsert(Query query, Update update);
@@ -115,7 +113,6 @@ public interface BulkOperations {
* else an insert.
*
* @param updates Updates/insert operations to perform.
* @return The bulk operation.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations upsert(List<Pair<Query, Update>> updates);

View File

@@ -29,7 +29,7 @@ import com.mongodb.client.MongoCollection;
* @author Grame Rocher
* @author Oliver Gierke
* @author John Brisbin
* @auhtor Christoph Strobl
* @author Christoph Strobl
* @since 1.0
*/
public interface CollectionCallback<T> {

View File

@@ -95,7 +95,7 @@ public class CollectionOptions {
/**
* Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}. <br />
* <strong>NOTE</strong> Using capped collections requires defining {@link #size(int)}.
* <strong>NOTE</strong> Using capped collections requires defining {@link #size(long)}.
*
* @return new {@link CollectionOptions}.
* @since 2.0

View File

@@ -43,9 +43,10 @@ public interface CursorPreparer extends ReadPreferenceAware {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
*
* @param cursor
* @param iterable must not be {@literal null}.
* @return never {@literal null}.
*/
FindIterable<Document> prepare(FindIterable<Document> cursor);
FindIterable<Document> prepare(FindIterable<Document> iterable);
/**
* Apply query specific settings to {@link MongoCollection} and initate a find operation returning a
@@ -53,7 +54,7 @@ public interface CursorPreparer extends ReadPreferenceAware {
*
* @param collection must not be {@literal null}.
* @param find must not be {@literal null}.
* @return
* @return never {@literal null}.
* @throws IllegalArgumentException if one of the required arguments is {@literal null}.
* @since 2.2
*/

View File

@@ -32,6 +32,7 @@ import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
@@ -62,6 +63,7 @@ import com.mongodb.client.model.*;
* @author Minsu Kim
* @author Jens Schauder
* @author Michail Nikolaev
* @author Roman Puchkovskiy
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -300,6 +302,7 @@ class DefaultBulkOperations implements BulkOperations {
Assert.state(result != null, "Result must not be null.");
models.forEach(this::maybeEmitAfterSaveEvent);
models.forEach(this::maybeInvokeAfterSaveCallback);
return result;
} finally {
@@ -421,38 +424,52 @@ class DefaultBulkOperations implements BulkOperations {
models.add(new SourceAwareWriteModelHolder(source, model));
}
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder it) {
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder holder) {
if (it.getModel() instanceof InsertOneModel) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(it.getSource(), target, collectionName));
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(it.getSource(), target, collectionName));
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
}
}
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder it) {
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder holder) {
if (it.getModel() instanceof InsertOneModel) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(it.getSource(), target, collectionName));
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(it.getSource(), target, collectionName));
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
}
}
private void maybeInvokeAfterSaveCallback(SourceAwareWriteModelHolder holder) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
}
}
private <E extends MongoMappingEvent<T>, T> E maybeEmitEvent(E event) {
if (null != bulkOperationContext.getEventPublisher()) {
bulkOperationContext.getEventPublisher().publishEvent(event);
if (bulkOperationContext.getEventPublisher() == null) {
return event;
}
bulkOperationContext.getEventPublisher().publishEvent(event);
return event;
}
@@ -475,6 +492,16 @@ class DefaultBulkOperations implements BulkOperations {
collectionName);
}
private Object maybeInvokeAfterSaveCallback(Object value, Document mappedDocument) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(AfterSaveCallback.class, value, mappedDocument,
collectionName);
}
private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
BulkWriteOptions options = new BulkWriteOptions();

View File

@@ -21,7 +21,7 @@ import java.util.List;
import org.bson.Document;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
@@ -64,7 +64,7 @@ public class DefaultIndexOperations implements IndexOperations {
* {@link DefaultIndexOperations#DefaultIndexOperations(MongoOperations, String, Class)}.
*/
@Deprecated
public DefaultIndexOperations(MongoDbFactory mongoDbFactory, String collectionName, QueryMapper queryMapper) {
public DefaultIndexOperations(MongoDatabaseFactory mongoDbFactory, String collectionName, QueryMapper queryMapper) {
this(mongoDbFactory, collectionName, queryMapper, null);
}
@@ -80,7 +80,7 @@ public class DefaultIndexOperations implements IndexOperations {
* {@link DefaultIndexOperations#DefaultIndexOperations(MongoOperations, String, Class)}.
*/
@Deprecated
public DefaultIndexOperations(MongoDbFactory mongoDbFactory, String collectionName, QueryMapper queryMapper,
public DefaultIndexOperations(MongoDatabaseFactory mongoDbFactory, String collectionName, QueryMapper queryMapper,
@Nullable Class<?> type) {
Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");

View File

@@ -15,13 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
/**
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDbFactory}.
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDatabaseFactory}.
*
* @author Mark Paluch
* @author Christoph Strobl
@@ -29,14 +29,14 @@ import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
*/
class DefaultIndexOperationsProvider implements IndexOperationsProvider {
private final MongoDbFactory mongoDbFactory;
private final MongoDatabaseFactory mongoDbFactory;
private final QueryMapper mapper;
/**
* @param mongoDbFactory must not be {@literal null}.
* @param mapper must not be {@literal null}.
*/
DefaultIndexOperationsProvider(MongoDbFactory mongoDbFactory, QueryMapper mapper) {
DefaultIndexOperationsProvider(MongoDatabaseFactory mongoDbFactory, QueryMapper mapper) {
this.mongoDbFactory = mongoDbFactory;
this.mapper = mapper;

View File

@@ -23,7 +23,7 @@ import com.mongodb.MongoException;
/**
* An interface used by {@link MongoTemplate} for processing documents returned from a MongoDB query on a per-document
* basis. Implementations of this interface perform the actual work of prcoessing each document but don't need to worry
* about exception handling. {@MongoException}s will be caught and translated by the calling MongoTemplate An
* about exception handling. {@link MongoException}s will be caught and translated by the calling MongoTemplate An
* DocumentCallbackHandler is typically stateful: It keeps the result state within the object, to be available later for
* later inspection.
*

View File

@@ -66,7 +66,7 @@ class EntityOperations {
* Creates a new {@link Entity} for the given bean.
*
* @param entity must not be {@literal null}.
* @return
* @return new instance of {@link Entity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> Entity<T> forEntity(T entity) {
@@ -89,7 +89,7 @@ class EntityOperations {
*
* @param entity must not be {@literal null}.
* @param conversionService must not be {@literal null}.
* @return
* @return new instance of {@link AdaptibleEntity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
@@ -108,6 +108,10 @@ class EntityOperations {
return AdaptibleMappedEntity.of(entity, context, conversionService);
}
/**
* @param entityClass should not be null.
* @return the {@link MongoPersistentEntity#getCollection() collection name}.
*/
public String determineCollectionName(@Nullable Class<?> entityClass) {
if (entityClass == null) {
@@ -138,7 +142,7 @@ class EntityOperations {
* {@code _id} if no identifier property can be found.
*
* @param type must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
public String getIdPropertyName(Class<?> type) {

View File

@@ -88,9 +88,9 @@ public interface ExecutableAggregationOperation {
/**
* Apply pipeline operations as specified and stream all matching elements. <br />
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor}
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable}
*
* @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed.
* @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to be closed.
* Never {@literal null}.
*/
CloseableIterator<T> stream();

View File

@@ -21,6 +21,7 @@ import java.util.stream.Stream;
import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;
@@ -43,7 +44,7 @@ import com.mongodb.client.MongoCollection;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(query(where("firstname").is("luke")))
* .matching(where("firstname").is("luke"))
* .all();
* </code>
* </pre>
@@ -117,7 +118,7 @@ public interface ExecutableFindOperation {
/**
* Stream all matching elements.
*
* @return a {@link Stream} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed. Never
* @return a {@link Stream} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to be closed. Never
* {@literal null}.
*/
Stream<T> stream();
@@ -170,6 +171,18 @@ public interface ExecutableFindOperation {
*/
TerminatingFind<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
* Set the filter query for the geoNear execution.
*
@@ -291,9 +304,21 @@ public interface ExecutableFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if resultType is {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -257,9 +257,9 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
* @see org.springframework.data.mongodb.core.CursorPreparer#prepare(com.mongodb.clientFindIterable)
*/
@Override
public FindIterable<Document> prepare(FindIterable<Document> cursor) {
public FindIterable<Document> prepare(FindIterable<Document> iterable) {
FindIterable<Document> target = delegate != null ? delegate.prepare(cursor) : cursor;
FindIterable<Document> target = delegate != null ? delegate.prepare(iterable) : iterable;
return limit.map(target::limit).orElse(target);
}

View File

@@ -19,6 +19,7 @@ import java.util.List;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
/**
@@ -30,7 +31,7 @@ import org.springframework.data.mongodb.core.query.Query;
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
* collection name for the execution.
*
*
* <pre>
* <code>
* mapReduce(Human.class)
@@ -44,6 +45,7 @@ import org.springframework.data.mongodb.core.query.Query;
* </pre>
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.1
*/
public interface ExecutableMapReduceOperation {
@@ -146,6 +148,18 @@ public interface ExecutableMapReduceOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingMapReduce<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.client.result.DeleteResult;
@@ -119,6 +120,18 @@ public interface ExecutableRemoveOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingRemove<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core;
import java.util.Optional;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
@@ -210,6 +211,18 @@ public interface ExecutableUpdateOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
UpdateWithUpdate<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -78,7 +78,7 @@ public class FindAndModifyOptions {
}
/**
* Create new {@link FindAndModifyOptions} based on option of given {@litearl source}.
* Create new {@link FindAndModifyOptions} based on option of given {@literal source}.
*
* @param source can be {@literal null}.
* @return new instance of {@link FindAndModifyOptions}.
@@ -117,8 +117,8 @@ public class FindAndModifyOptions {
/**
* Define the {@link Collation} specifying language-specific rules for string comparison.
*
* @param collation
* @return
* @param collation can be {@literal null}.
* @return this.
* @since 2.0
*/
public FindAndModifyOptions collation(@Nullable Collation collation) {
@@ -142,7 +142,7 @@ public class FindAndModifyOptions {
/**
* Get the {@link Collation} specifying language-specific rules for string comparison.
*
* @return
* @return never {@literal null}.
* @since 2.0
*/
public Optional<Collation> getCollation() {

View File

@@ -116,7 +116,7 @@ public class FindAndReplaceOptions {
/**
* Get the bit indicating to return the replacement document.
*
* @return
* @return {@literal true} if set.
*/
public boolean isReturnNew() {
return returnNew;
@@ -125,7 +125,7 @@ public class FindAndReplaceOptions {
/**
* Get the bit indicating if to create a new document if not exists.
*
* @return
* @return {@literal true} if set.
*/
public boolean isUpsert() {
return upsert;

View File

@@ -54,7 +54,7 @@ public interface FindPublisherPreparer extends ReadPreferenceAware {
*
* @param collection must not be {@literal null}.
* @param find must not be {@literal null}.
* @return
* @return never {@literal null}.
* @throws IllegalArgumentException if one of the required arguments is {@literal null}.
* @since 2.2
*/

View File

@@ -47,7 +47,7 @@ class GeoCommandStatistics {
* Creates a new {@link GeoCommandStatistics} from the given command result extracting the statistics.
*
* @param commandResult must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
public static GeoCommandStatistics from(Document commandResult) {
@@ -61,7 +61,7 @@ class GeoCommandStatistics {
* Returns the average distance reported by the command result. Mitigating a removal of the field in case the command
* didn't return any result introduced in MongoDB 3.2 RC1.
*
* @return
* @return never {@literal null}, uses {@link Double#NaN} if {@literal avgDistance} does not exist.
* @see <a href="https://jira.mongodb.org/browse/SERVER-21024">MongoDB Jira SERVER-21024</a>
*/
public double getAverageDistance() {

View File

@@ -80,7 +80,11 @@ public class MappedDocument {
}
public Bson getIdFilter() {
return Filters.eq(ID_FIELD, document.get(ID_FIELD));
return new Document(ID_FIELD, document.get(ID_FIELD));
}
public Object get(String key) {
return document.get(key);
}
public UpdateDefinition updateWithoutId() {

View File

@@ -20,7 +20,7 @@ import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
/**
@@ -34,24 +34,13 @@ import com.mongodb.client.MongoDatabase;
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
private final Object mongoClient;
/**
* @param mongoClient
* @deprecated since 2.2 in favor of {@link MongoAdmin#MongoAdmin(com.mongodb.client.MongoClient)}.
*/
@Deprecated
public MongoAdmin(MongoClient mongoClient) {
Assert.notNull(mongoClient, "MongoClient must not be null!");
this.mongoClient = mongoClient;
}
private final MongoClient mongoClient;
/**
* @param client the underlying {@link com.mongodb.client.MongoClient} used for data access.
* @since 2.2
*/
public MongoAdmin(com.mongodb.client.MongoClient client) {
public MongoAdmin(MongoClient client) {
Assert.notNull(client, "Client must not be null!");
this.mongoClient = client;
@@ -88,11 +77,6 @@ public class MongoAdmin implements MongoAdminOperations {
}
MongoDatabase getDB(String databaseName) {
if (mongoClient instanceof MongoClient) {
return ((MongoClient) mongoClient).getDatabase(databaseName);
}
return ((com.mongodb.client.MongoClient) mongoClient).getDatabase(databaseName);
return mongoClient.getDatabase(databaseName);
}
}

View File

@@ -16,70 +16,72 @@
package org.springframework.data.mongodb.core;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.UuidRepresentation;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import com.mongodb.event.ClusterListener;
/**
* Convenient factory for configuring MongoDB.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.7
* @deprecated since 2.2 - There is no replacement for this {@link org.springframework.beans.factory.FactoryBean} at
* this time. However moving forward the {@link org.springframework.beans.factory.FactoryBean} will be
* suitable to provide instances of {@link com.mongodb.client.MongoClient}.
*/
@Deprecated
public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> implements PersistenceExceptionTranslator {
private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator();
private @Nullable MongoClientOptions mongoClientOptions;
private @Nullable MongoClientSettings mongoClientSettings;
private @Nullable String host;
private @Nullable Integer port;
private List<ServerAddress> replicaSetSeeds = Collections.emptyList();
private List<MongoCredential> credentials = Collections.emptyList();
private @Nullable List<MongoCredential> credential = null;
private @Nullable ConnectionString connectionString;
private @Nullable String replicaSet = null;
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR;
/**
* Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}.
* Set the {@link MongoClientSettings} to be used when creating {@link MongoClient}.
*
* @param mongoClientOptions
*/
public void setMongoClientOptions(@Nullable MongoClientOptions mongoClientOptions) {
this.mongoClientOptions = mongoClientOptions;
public void setMongoClientSettings(@Nullable MongoClientSettings mongoClientOptions) {
this.mongoClientSettings = mongoClientOptions;
}
/**
* Set the list of credentials to be used when creating {@link MongoClient}.
*
* @param credentials can be {@literal null}.
* @param credential can be {@literal null}.
*/
public void setCredentials(@Nullable MongoCredential[] credentials) {
this.credentials = filterNonNullElementsAsList(credentials);
}
/**
* Set the list of {@link ServerAddress} to build up a replica set for.
*
* @param replicaSetSeeds can be {@literal null}.
*/
public void setReplicaSetSeeds(@Nullable ServerAddress[] replicaSetSeeds) {
this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds);
public void setCredential(@Nullable MongoCredential[] credential) {
this.credential = Arrays.asList(credential);
}
/**
@@ -100,6 +102,14 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
this.port = port;
}
public void setConnectionString(@Nullable ConnectionString connectionString) {
this.connectionString = connectionString;
}
public void setReplicaSet(@Nullable String replicaSet) {
this.replicaSet = replicaSet;
}
/**
* Configures the {@link PersistenceExceptionTranslator} to use.
*
@@ -132,12 +142,198 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
*/
@Override
protected MongoClient createInstance() throws Exception {
return createMongoClient(computeClientSetting());
}
if (mongoClientOptions == null) {
mongoClientOptions = MongoClientOptions.builder().build();
/**
* Create {@link MongoClientSettings} based on configuration and priority (lower is better).
* <ol>
* <li>{@link MongoClientFactoryBean#mongoClientSettings}</li>
* <li>{@link MongoClientFactoryBean#connectionString}</li>
* <li>default {@link MongoClientSettings}</li>
* </ol>
*
* @since 3.0
*/
protected MongoClientSettings computeClientSetting() {
if (connectionString != null && (StringUtils.hasText(host) || port != null)) {
throw new IllegalStateException("ConnectionString and host/port configuration exclude one another!");
}
return createMongoClient();
ConnectionString connectionString = this.connectionString != null ? this.connectionString
: new ConnectionString(String.format("mongodb://%s:%s", getOrDefault(host, ServerAddress.defaultHost()),
getOrDefault(port, "" + ServerAddress.defaultPort())));
Builder builder = MongoClientSettings.builder().applyConnectionString(connectionString);
builder.uuidRepresentation(UuidRepresentation.JAVA_LEGACY);
if (mongoClientSettings != null) {
MongoClientSettings defaultSettings = MongoClientSettings.builder().build();
SslSettings sslSettings = mongoClientSettings.getSslSettings();
ClusterSettings clusterSettings = mongoClientSettings.getClusterSettings();
ConnectionPoolSettings connectionPoolSettings = mongoClientSettings.getConnectionPoolSettings();
SocketSettings socketSettings = mongoClientSettings.getSocketSettings();
ServerSettings serverSettings = mongoClientSettings.getServerSettings();
builder = builder //
.applicationName(computeSettingsValue(defaultSettings.getApplicationName(),
mongoClientSettings.getApplicationName(), connectionString.getApplicationName())) //
.applyToSslSettings(settings -> {
applySettings(settings::enabled, computeSettingsValue(SslSettings::isEnabled,
defaultSettings.getSslSettings(), sslSettings, connectionString.getSslEnabled()));
applySettings(settings::invalidHostNameAllowed, (computeSettingsValue(SslSettings::isInvalidHostNameAllowed,
defaultSettings.getSslSettings(), sslSettings, connectionString.getSslInvalidHostnameAllowed())));
settings.context(sslSettings.getContext());
}).applyToClusterSettings(settings -> {
applySettings(settings::hosts,
computeSettingsValue(ClusterSettings::getHosts, defaultSettings.getClusterSettings(), clusterSettings,
connectionString.getHosts().stream().map(ServerAddress::new).collect(Collectors.toList())));
applySettings(settings::requiredReplicaSetName,
computeSettingsValue(ClusterSettings::getRequiredReplicaSetName, defaultSettings.getClusterSettings(),
clusterSettings, connectionString.getRequiredReplicaSetName()));
applySettings(settings::srvHost, computeSettingsValue(ClusterSettings::getSrvHost,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(settings::mode, computeSettingsValue(ClusterSettings::getMode,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(it -> settings.localThreshold(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ClusterSettings it) -> it.getLocalThreshold(TimeUnit.MILLISECONDS),
defaultSettings.getClusterSettings(), clusterSettings, connectionString.getLocalThreshold()));
applySettings(settings::requiredClusterType, computeSettingsValue(ClusterSettings::getRequiredClusterType,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(it -> settings.serverSelectionTimeout(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ClusterSettings it) -> it.getServerSelectionTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getClusterSettings(), clusterSettings,
connectionString.getServerSelectionTimeout()));
applySettings(settings::serverSelector, computeSettingsValue(ClusterSettings::getServerSelector,
defaultSettings.getClusterSettings(), clusterSettings, null));
List<ClusterListener> clusterListeners = computeSettingsValue(ClusterSettings::getClusterListeners,
defaultSettings.getClusterSettings(), clusterSettings, null);
if (clusterListeners != null) {
clusterListeners.forEach(settings::addClusterListener);
}
}) //
.applyToConnectionPoolSettings(settings -> {
applySettings(it -> settings.maintenanceFrequency(it, TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaintenanceFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings, null));
applySettings(it -> settings.maxConnectionIdleTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxConnectionIdleTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxConnectionIdleTime()));
applySettings(it -> settings.maxConnectionLifeTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxConnectionLifeTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxConnectionLifeTime()));
applySettings(it -> settings.maxWaitTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxWaitTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxWaitTime()));
applySettings(it -> settings.maintenanceInitialDelay(it, TimeUnit.MILLISECONDS),
computeSettingsValue(
(ConnectionPoolSettings it) -> it.getMaintenanceInitialDelay(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings, null));
applySettings(settings::minSize,
computeSettingsValue(ConnectionPoolSettings::getMinSize, defaultSettings.getConnectionPoolSettings(),
connectionPoolSettings, connectionString.getMinConnectionPoolSize()));
applySettings(settings::maxSize,
computeSettingsValue(ConnectionPoolSettings::getMaxSize, defaultSettings.getConnectionPoolSettings(),
connectionPoolSettings, connectionString.getMaxConnectionPoolSize()));
}) //
.applyToSocketSettings(settings -> {
applySettings(it -> settings.connectTimeout(it, TimeUnit.MILLISECONDS),
computeSettingsValue((SocketSettings it) -> it.getConnectTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getSocketSettings(), socketSettings, connectionString.getConnectTimeout()));
applySettings(it -> settings.readTimeout(it, TimeUnit.MILLISECONDS),
computeSettingsValue((SocketSettings it) -> it.getReadTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getSocketSettings(), socketSettings, connectionString.getSocketTimeout()));
applySettings(settings::receiveBufferSize, computeSettingsValue(SocketSettings::getReceiveBufferSize,
defaultSettings.getSocketSettings(), socketSettings, null));
applySettings(settings::sendBufferSize, computeSettingsValue(SocketSettings::getSendBufferSize,
defaultSettings.getSocketSettings(), socketSettings, null));
}) //
.applyToServerSettings(settings -> {
applySettings(it -> settings.minHeartbeatFrequency(it.intValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ServerSettings it) -> it.getMinHeartbeatFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getServerSettings(), serverSettings, null));
applySettings(it -> settings.heartbeatFrequency(it.intValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ServerSettings it) -> it.getHeartbeatFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getServerSettings(), serverSettings, connectionString.getHeartbeatFrequency()));
settings.applySettings(serverSettings);
}) //
.autoEncryptionSettings(mongoClientSettings.getAutoEncryptionSettings()) //
.codecRegistry(mongoClientSettings.getCodecRegistry()); //
applySettings(builder::readConcern, computeSettingsValue(defaultSettings.getReadConcern(),
mongoClientSettings.getReadConcern(), connectionString.getReadConcern()));
applySettings(builder::writeConcern, computeSettingsValue(defaultSettings.getWriteConcern(),
mongoClientSettings.getWriteConcern(), connectionString.getWriteConcern()));
applySettings(builder::readPreference, computeSettingsValue(defaultSettings.getReadPreference(),
mongoClientSettings.getReadPreference(), connectionString.getReadPreference()));
applySettings(builder::retryReads, computeSettingsValue(defaultSettings.getRetryReads(),
mongoClientSettings.getRetryReads(), connectionString.getRetryReads()));
applySettings(builder::retryWrites, computeSettingsValue(defaultSettings.getRetryWrites(),
mongoClientSettings.getRetryWrites(), connectionString.getRetryWritesValue()));
applySettings(builder::uuidRepresentation,
computeSettingsValue(null, mongoClientSettings.getUuidRepresentation(), UuidRepresentation.JAVA_LEGACY));
}
if (!CollectionUtils.isEmpty(credential)) {
builder = builder.credential(credential.iterator().next());
}
if (StringUtils.hasText(replicaSet)) {
builder.applyToClusterSettings((settings) -> {
settings.requiredReplicaSetName(replicaSet);
});
}
return builder.build();
}
private <T> void applySettings(Consumer<T> settingsBuilder, @Nullable T value) {
if (ObjectUtils.isEmpty(value)) {
return;
}
settingsBuilder.accept(value);
}
private <S, T> T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder,
@Nullable T connectionStringValue) {
return computeSettingsValue(function.apply(defaultValueHolder), function.apply(settingsValueHolder),
connectionStringValue);
}
private <T> T computeSettingsValue(T defaultValue, T fromSettings, T fromConnectionString) {
boolean fromSettingsIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromSettings);
boolean fromConnectionStringIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromConnectionString);
if (!fromSettingsIsDefault) {
return fromSettings;
}
return !fromConnectionStringIsDefault ? fromConnectionString : defaultValue;
}
/*
@@ -152,43 +348,11 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
}
}
private MongoClient createMongoClient() throws UnknownHostException {
if (!CollectionUtils.isEmpty(replicaSetSeeds)) {
return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions);
}
return new MongoClient(createConfiguredOrDefaultServerAddress(), credentials, mongoClientOptions);
private MongoClient createMongoClient(MongoClientSettings settings) throws UnknownHostException {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
private ServerAddress createConfiguredOrDefaultServerAddress() throws UnknownHostException {
ServerAddress defaultAddress = new ServerAddress();
return new ServerAddress(StringUtils.hasText(host) ? host : defaultAddress.getHost(),
port != null ? port.intValue() : defaultAddress.getPort());
}
/**
* Returns the given array as {@link List} with all {@literal null} elements removed.
*
* @param elements the elements to filter <T>, can be {@literal null}.
* @return a new unmodifiable {@link List#} from the given elements without {@literal null}s.
*/
private static <T> List<T> filterNonNullElementsAsList(@Nullable T[] elements) {
if (elements == null) {
return Collections.emptyList();
}
List<T> candidateElements = new ArrayList<T>();
for (T element : elements) {
if (element != null) {
candidateElements.add(element);
}
}
return Collections.unmodifiableList(candidateElements);
private String getOrDefault(Object value, String defaultValue) {
return !StringUtils.isEmpty(value) ? value.toString() : defaultValue;
}
}

View File

@@ -1,338 +0,0 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.lang.Nullable;
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.DBDecoderFactory;
import com.mongodb.DBEncoderFactory;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
/**
* A factory bean for construction of a {@link MongoClientOptions} instance.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.7
* @deprecated since 2.2 - There is no replacement for this {@link org.springframework.beans.factory.FactoryBean}.
* However moving forward there will be a dedicated factory bean for {@link com.mongodb.MongoClientSettings}
* replacing {@link MongoClientOptions}.
*/
@Deprecated
public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClientOptions> {
private static final MongoClientOptions DEFAULT_MONGO_OPTIONS = MongoClientOptions.builder().build();
// TODO: Mongo Driver 4 - use application name insetad of description if not available
private @Nullable String description = DEFAULT_MONGO_OPTIONS.getApplicationName();
private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost();
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
.getThreadsAllowedToBlockForConnectionMultiplier();
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime();
private int maxConnectionIdleTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionIdleTime();
private int maxConnectionLifeTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionLifeTime();
private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout();
private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout();
// TODO: Mongo Driver 4 - check if available
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive();
private @Nullable ReadPreference readPreference = DEFAULT_MONGO_OPTIONS.getReadPreference();
private DBDecoderFactory dbDecoderFactory = DEFAULT_MONGO_OPTIONS.getDbDecoderFactory();
private DBEncoderFactory dbEncoderFactory = DEFAULT_MONGO_OPTIONS.getDbEncoderFactory();
private @Nullable WriteConcern writeConcern = DEFAULT_MONGO_OPTIONS.getWriteConcern();
private @Nullable SocketFactory socketFactory = DEFAULT_MONGO_OPTIONS.getSocketFactory();
private boolean cursorFinalizerEnabled = DEFAULT_MONGO_OPTIONS.isCursorFinalizerEnabled();
// TODO: Mongo Driver 4 - remove this option
private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans();
private int heartbeatFrequency = DEFAULT_MONGO_OPTIONS.getHeartbeatFrequency();
private int minHeartbeatFrequency = DEFAULT_MONGO_OPTIONS.getMinHeartbeatFrequency();
private int heartbeatConnectTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatConnectTimeout();
private int heartbeatSocketTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatSocketTimeout();
private String requiredReplicaSetName = DEFAULT_MONGO_OPTIONS.getRequiredReplicaSetName();
private int serverSelectionTimeout = DEFAULT_MONGO_OPTIONS.getServerSelectionTimeout();
private boolean ssl;
private @Nullable SSLSocketFactory sslSocketFactory;
private @Nullable AutoEncryptionSettings autoEncryptionSettings;
/**
* Set the {@link MongoClient} description.
*
* @param description
*/
// TODO: Mongo Driver 4 - deprecate that one and add application name
public void setDescription(@Nullable String description) {
this.description = description;
}
/**
* Set the minimum number of connections per host.
*
* @param minConnectionsPerHost
*/
public void setMinConnectionsPerHost(int minConnectionsPerHost) {
this.minConnectionsPerHost = minConnectionsPerHost;
}
/**
* Set the number of connections allowed per host. Will block if run out. Default is 10. System property
* {@code MONGO.POOLSIZE} can override
*
* @param connectionsPerHost
*/
public void setConnectionsPerHost(int connectionsPerHost) {
this.connectionsPerHost = connectionsPerHost;
}
/**
* Set the multiplier for connectionsPerHost for # of threads that can block. Default is 5. If connectionsPerHost is
* 10, and threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block more than that and an
* exception will be thrown.
*
* @param threadsAllowedToBlockForConnectionMultiplier
*/
public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) {
this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
}
/**
* Set the max wait time of a blocking thread for a connection. Default is 12000 ms (2 minutes)
*
* @param maxWaitTime
*/
public void setMaxWaitTime(int maxWaitTime) {
this.maxWaitTime = maxWaitTime;
}
/**
* The maximum idle time for a pooled connection.
*
* @param maxConnectionIdleTime
*/
public void setMaxConnectionIdleTime(int maxConnectionIdleTime) {
this.maxConnectionIdleTime = maxConnectionIdleTime;
}
/**
* Set the maximum life time for a pooled connection.
*
* @param maxConnectionLifeTime
*/
public void setMaxConnectionLifeTime(int maxConnectionLifeTime) {
this.maxConnectionLifeTime = maxConnectionLifeTime;
}
/**
* Set the connect timeout in milliseconds. 0 is default and infinite.
*
* @param connectTimeout
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* Set the socket timeout. 0 is default and infinite.
*
* @param socketTimeout
*/
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
/**
* Set the keep alive flag, controls whether or not to have socket keep alive timeout. Defaults to false.
*
* @param socketKeepAlive
*/
public void setSocketKeepAlive(boolean socketKeepAlive) {
this.socketKeepAlive = socketKeepAlive;
}
/**
* Set the {@link ReadPreference}.
*
* @param readPreference
*/
public void setReadPreference(@Nullable ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* Set the {@link WriteConcern} that will be the default value used when asking the {@link MongoDbFactory} for a DB
* object.
*
* @param writeConcern
*/
public void setWriteConcern(@Nullable WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* @param socketFactory
*/
public void setSocketFactory(@Nullable SocketFactory socketFactory) {
this.socketFactory = socketFactory;
}
/**
* Set the frequency that the driver will attempt to determine the current state of each server in the cluster.
*
* @param heartbeatFrequency
*/
public void setHeartbeatFrequency(int heartbeatFrequency) {
this.heartbeatFrequency = heartbeatFrequency;
}
/**
* In the event that the driver has to frequently re-check a server's availability, it will wait at least this long
* since the previous check to avoid wasted effort.
*
* @param minHeartbeatFrequency
*/
public void setMinHeartbeatFrequency(int minHeartbeatFrequency) {
this.minHeartbeatFrequency = minHeartbeatFrequency;
}
/**
* Set the connect timeout for connections used for the cluster heartbeat.
*
* @param heartbeatConnectTimeout
*/
public void setHeartbeatConnectTimeout(int heartbeatConnectTimeout) {
this.heartbeatConnectTimeout = heartbeatConnectTimeout;
}
/**
* Set the socket timeout for connections used for the cluster heartbeat.
*
* @param heartbeatSocketTimeout
*/
public void setHeartbeatSocketTimeout(int heartbeatSocketTimeout) {
this.heartbeatSocketTimeout = heartbeatSocketTimeout;
}
/**
* Configures the name of the replica set.
*
* @param requiredReplicaSetName
*/
public void setRequiredReplicaSetName(String requiredReplicaSetName) {
this.requiredReplicaSetName = requiredReplicaSetName;
}
/**
* This controls if the driver should us an SSL connection. Defaults to {@literal false}.
*
* @param ssl
*/
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
/**
* Set the {@link SSLSocketFactory} to use for the {@literal SSL} connection. If none is configured here,
* {@link SSLSocketFactory#getDefault()} will be used.
*
* @param sslSocketFactory
*/
public void setSslSocketFactory(@Nullable SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
this.ssl = sslSocketFactory != null;
}
/**
* Set the {@literal server selection timeout} in msec for a 3.x MongoDB Java driver. If not set the default value of
* 30 sec will be used. A value of 0 means that it will timeout immediately if no server is available. A negative
* value means to wait indefinitely.
*
* @param serverSelectionTimeout in msec.
*/
public void setServerSelectionTimeout(int serverSelectionTimeout) {
this.serverSelectionTimeout = serverSelectionTimeout;
}
/**
* Set the {@link AutoEncryptionSettings} to be used.
*
* @param autoEncryptionSettings can be {@literal null}.
* @since 2.2
*/
public void setAutoEncryptionSettings(@Nullable AutoEncryptionSettings autoEncryptionSettings) {
this.autoEncryptionSettings = autoEncryptionSettings;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
*/
@SuppressWarnings("ConstantConditions")
@Override
protected MongoClientOptions createInstance() throws Exception {
SocketFactory socketFactoryToUse = ssl
? (sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault())
: this.socketFactory;
return MongoClientOptions.builder() //
.alwaysUseMBeans(this.alwaysUseMBeans) //
.connectionsPerHost(this.connectionsPerHost) //
.connectTimeout(connectTimeout) //
.cursorFinalizerEnabled(cursorFinalizerEnabled) //
.dbDecoderFactory(dbDecoderFactory) //
.dbEncoderFactory(dbEncoderFactory) //
.applicationName(description) // TODO: Mongo Driver 4 - use application name if description not available
.heartbeatConnectTimeout(heartbeatConnectTimeout) //
.heartbeatFrequency(heartbeatFrequency) //
.heartbeatSocketTimeout(heartbeatSocketTimeout) //
.maxConnectionIdleTime(maxConnectionIdleTime) //
.maxConnectionLifeTime(maxConnectionLifeTime) //
.maxWaitTime(maxWaitTime) //
.minConnectionsPerHost(minConnectionsPerHost) //
.minHeartbeatFrequency(minHeartbeatFrequency) //
.readPreference(readPreference) //
.requiredReplicaSetName(requiredReplicaSetName) //
.serverSelectionTimeout(serverSelectionTimeout) //
.sslEnabled(ssl) //
.autoEncryptionSettings(autoEncryptionSettings) //
.socketFactory(socketFactoryToUse) // TODO: Mongo Driver 4 -
.socketKeepAlive(socketKeepAlive) // TODO: Mongo Driver 4 - remove if not available
.socketTimeout(socketTimeout) //
.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) //
.writeConcern(writeConcern).build();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<?> getObjectType() {
return MongoClientOptions.class;
}
}

View File

@@ -0,0 +1,486 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.bson.UuidRepresentation;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ClusterType;
import com.mongodb.connection.StreamFactoryFactory;
/**
* A factory bean for construction of a {@link MongoClientSettings} instance to be used with a MongoDB driver.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoClientSettings> {
private static final MongoClientSettings DEFAULT_MONGO_SETTINGS = MongoClientSettings.builder().build();
private CodecRegistry codecRegistry = DEFAULT_MONGO_SETTINGS.getCodecRegistry();
private StreamFactoryFactory streamFactoryFactory = DEFAULT_MONGO_SETTINGS.getStreamFactoryFactory();
private ReadPreference readPreference = DEFAULT_MONGO_SETTINGS.getReadPreference();
private ReadConcern readConcern = DEFAULT_MONGO_SETTINGS.getReadConcern();
private @Nullable Boolean retryReads = null;
private WriteConcern writeConcern = DEFAULT_MONGO_SETTINGS.getWriteConcern();
private @Nullable Boolean retryWrites = null;
private @Nullable String applicationName = null;
private @Nullable UuidRepresentation uUidRepresentation = null;
// --> Socket Settings
private int socketConnectTimeoutMS = DEFAULT_MONGO_SETTINGS.getSocketSettings()
.getConnectTimeout(TimeUnit.MILLISECONDS);
private int socketReadTimeoutMS = DEFAULT_MONGO_SETTINGS.getSocketSettings().getReadTimeout(TimeUnit.MILLISECONDS);
private int socketReceiveBufferSize = DEFAULT_MONGO_SETTINGS.getSocketSettings().getReceiveBufferSize();
private int socketSendBufferSize = DEFAULT_MONGO_SETTINGS.getSocketSettings().getSendBufferSize();
// --> Cluster Settings
private @Nullable String clusterSrvHost = DEFAULT_MONGO_SETTINGS.getClusterSettings().getSrvHost();
private List<ServerAddress> clusterHosts = Collections.emptyList();
private @Nullable ClusterConnectionMode clusterConnectionMode = null;
private ClusterType custerRequiredClusterType = DEFAULT_MONGO_SETTINGS.getClusterSettings().getRequiredClusterType();
private String clusterRequiredReplicaSetName = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getRequiredReplicaSetName();
private long clusterLocalThresholdMS = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getLocalThreshold(TimeUnit.MILLISECONDS);
private long clusterServerSelectionTimeoutMS = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getServerSelectionTimeout(TimeUnit.MILLISECONDS);
// --> ConnectionPoolSettings
private int poolMaxSize = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings().getMaxSize();
private int poolMinSize = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings().getMinSize();
private long poolMaxWaitTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxWaitTime(TimeUnit.MILLISECONDS);
private long poolMaxConnectionLifeTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxConnectionLifeTime(TimeUnit.MILLISECONDS);
private long poolMaxConnectionIdleTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxConnectionIdleTime(TimeUnit.MILLISECONDS);
private long poolMaintenanceInitialDelayMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaintenanceInitialDelay(TimeUnit.MILLISECONDS);
private long poolMaintenanceFrequencyMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaintenanceFrequency(TimeUnit.MILLISECONDS);
// --> SSL Settings
private boolean sslEnabled = DEFAULT_MONGO_SETTINGS.getSslSettings().isEnabled();
private boolean sslInvalidHostNameAllowed = DEFAULT_MONGO_SETTINGS.getSslSettings().isInvalidHostNameAllowed();
private String sslProvider = DEFAULT_MONGO_SETTINGS.getSslSettings().isEnabled()
? DEFAULT_MONGO_SETTINGS.getSslSettings().getContext().getProvider().getName()
: "";
// encryption and retry
private @Nullable AutoEncryptionSettings autoEncryptionSettings;
/**
* @param socketConnectTimeoutMS in msec
* @see com.mongodb.connection.SocketSettings.Builder#connectTimeout(int, TimeUnit)
*/
public void setSocketConnectTimeoutMS(int socketConnectTimeoutMS) {
this.socketConnectTimeoutMS = socketConnectTimeoutMS;
}
/**
* @param socketReadTimeoutMS in msec
* @see com.mongodb.connection.SocketSettings.Builder#readTimeout(int, TimeUnit)
*/
public void setSocketReadTimeoutMS(int socketReadTimeoutMS) {
this.socketReadTimeoutMS = socketReadTimeoutMS;
}
/**
* @param socketReceiveBufferSize
* @see com.mongodb.connection.SocketSettings.Builder#receiveBufferSize(int)
*/
public void setSocketReceiveBufferSize(int socketReceiveBufferSize) {
this.socketReceiveBufferSize = socketReceiveBufferSize;
}
/**
* @param socketSendBufferSize
* @see com.mongodb.connection.SocketSettings.Builder#sendBufferSize(int)
*/
public void setSocketSendBufferSize(int socketSendBufferSize) {
this.socketSendBufferSize = socketSendBufferSize;
}
// --> Server Settings
private long serverHeartbeatFrequencyMS = DEFAULT_MONGO_SETTINGS.getServerSettings()
.getHeartbeatFrequency(TimeUnit.MILLISECONDS);
private long serverMinHeartbeatFrequencyMS = DEFAULT_MONGO_SETTINGS.getServerSettings()
.getMinHeartbeatFrequency(TimeUnit.MILLISECONDS);
/**
* @param serverHeartbeatFrequencyMS in msec
* @see com.mongodb.connection.ServerSettings.Builder#heartbeatFrequency(long, TimeUnit)
*/
public void setServerHeartbeatFrequencyMS(long serverHeartbeatFrequencyMS) {
this.serverHeartbeatFrequencyMS = serverHeartbeatFrequencyMS;
}
/**
* @param serverMinHeartbeatFrequencyMS in msec
* @see com.mongodb.connection.ServerSettings.Builder#minHeartbeatFrequency(long, TimeUnit)
*/
public void setServerMinHeartbeatFrequencyMS(long serverMinHeartbeatFrequencyMS) {
this.serverMinHeartbeatFrequencyMS = serverMinHeartbeatFrequencyMS;
}
// --> Cluster Settings
/**
* @param clusterSrvHost
* @see com.mongodb.connection.ClusterSettings.Builder#srvHost(String)
*/
public void setClusterSrvHost(String clusterSrvHost) {
this.clusterSrvHost = clusterSrvHost;
}
/**
* @param clusterHosts
* @see com.mongodb.connection.ClusterSettings.Builder#hosts(List)
*/
public void setClusterHosts(ServerAddress[] clusterHosts) {
this.clusterHosts = Arrays.asList(clusterHosts);
}
/**
* ????
*
* @param clusterConnectionMode
* @see com.mongodb.connection.ClusterSettings.Builder#mode(ClusterConnectionMode)
*/
public void setClusterConnectionMode(ClusterConnectionMode clusterConnectionMode) {
this.clusterConnectionMode = clusterConnectionMode;
}
/**
* @param custerRequiredClusterType
* @see com.mongodb.connection.ClusterSettings.Builder#requiredClusterType(ClusterType)
*/
public void setCusterRequiredClusterType(ClusterType custerRequiredClusterType) {
this.custerRequiredClusterType = custerRequiredClusterType;
}
/**
* @param clusterRequiredReplicaSetName
* @see com.mongodb.connection.ClusterSettings.Builder#requiredReplicaSetName(String)
*/
public void setClusterRequiredReplicaSetName(String clusterRequiredReplicaSetName) {
this.clusterRequiredReplicaSetName = clusterRequiredReplicaSetName;
}
/**
* @param clusterLocalThresholdMS in msec
* @see com.mongodb.connection.ClusterSettings.Builder#localThreshold(long, TimeUnit)
*/
public void setClusterLocalThresholdMS(long clusterLocalThresholdMS) {
this.clusterLocalThresholdMS = clusterLocalThresholdMS;
}
/**
* @param clusterServerSelectionTimeoutMS in msec
* @see com.mongodb.connection.ClusterSettings.Builder#serverSelectionTimeout(long, TimeUnit)
*/
public void setClusterServerSelectionTimeoutMS(long clusterServerSelectionTimeoutMS) {
this.clusterServerSelectionTimeoutMS = clusterServerSelectionTimeoutMS;
}
// --> ConnectionPoolSettings
/**
* @param poolMaxSize
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxSize(int)
*/
public void setPoolMaxSize(int poolMaxSize) {
this.poolMaxSize = poolMaxSize;
}
/**
* @param poolMinSize
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#minSize(int)
*/
public void setPoolMinSize(int poolMinSize) {
this.poolMinSize = poolMinSize;
}
/**
* @param poolMaxWaitTimeMS in mesec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxWaitTime(long, TimeUnit)
*/
public void setPoolMaxWaitTimeMS(long poolMaxWaitTimeMS) {
this.poolMaxWaitTimeMS = poolMaxWaitTimeMS;
}
/**
* @param poolMaxConnectionLifeTimeMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxConnectionLifeTime(long, TimeUnit)
*/
public void setPoolMaxConnectionLifeTimeMS(long poolMaxConnectionLifeTimeMS) {
this.poolMaxConnectionLifeTimeMS = poolMaxConnectionLifeTimeMS;
}
/**
* @param poolMaxConnectionIdleTimeMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxConnectionIdleTime(long, TimeUnit)
*/
public void setPoolMaxConnectionIdleTimeMS(long poolMaxConnectionIdleTimeMS) {
this.poolMaxConnectionIdleTimeMS = poolMaxConnectionIdleTimeMS;
}
/**
* @param poolMaintenanceInitialDelayMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maintenanceInitialDelay(long, TimeUnit)
*/
public void setPoolMaintenanceInitialDelayMS(long poolMaintenanceInitialDelayMS) {
this.poolMaintenanceInitialDelayMS = poolMaintenanceInitialDelayMS;
}
/**
* @param poolMaintenanceFrequencyMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maintenanceFrequency(long, TimeUnit)
*/
public void setPoolMaintenanceFrequencyMS(long poolMaintenanceFrequencyMS) {
this.poolMaintenanceFrequencyMS = poolMaintenanceFrequencyMS;
}
// --> SSL Settings
/**
* @param sslEnabled
* @see com.mongodb.connection.SslSettings.Builder#enabled(boolean)
*/
public void setSslEnabled(Boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
/**
* @param sslInvalidHostNameAllowed
* @see com.mongodb.connection.SslSettings.Builder#invalidHostNameAllowed(boolean)
*/
public void setSslInvalidHostNameAllowed(Boolean sslInvalidHostNameAllowed) {
this.sslInvalidHostNameAllowed = sslInvalidHostNameAllowed;
}
/**
* @param sslProvider
* @see com.mongodb.connection.SslSettings.Builder#context(SSLContext)
* @see SSLContext#getInstance(String)
*/
public void setSslProvider(String sslProvider) {
this.sslProvider = sslProvider;
}
// encryption and retry
/**
* @param applicationName
* @see MongoClientSettings.Builder#applicationName(String)
*/
public void setApplicationName(@Nullable String applicationName) {
this.applicationName = applicationName;
}
/**
* @param retryReads
* @see MongoClientSettings.Builder#retryReads(boolean)
*/
public void setRetryReads(@Nullable Boolean retryReads) {
this.retryReads = retryReads;
}
/**
* @param readConcern
* @see MongoClientSettings.Builder#readConcern(ReadConcern)
*/
public void setReadConcern(ReadConcern readConcern) {
this.readConcern = readConcern;
}
/**
* @param writeConcern
* @see MongoClientSettings.Builder#writeConcern(WriteConcern)
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* @param retryWrites
* @see MongoClientSettings.Builder#retryWrites(boolean)
*/
public void setRetryWrites(@Nullable Boolean retryWrites) {
this.retryWrites = retryWrites;
}
/**
* @param readPreference
* @see MongoClientSettings.Builder#readPreference(ReadPreference)
*/
public void setReadPreference(ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* @param streamFactoryFactory
* @see MongoClientSettings.Builder#streamFactoryFactory(StreamFactoryFactory)
*/
public void setStreamFactoryFactory(StreamFactoryFactory streamFactoryFactory) {
this.streamFactoryFactory = streamFactoryFactory;
}
/**
* @param codecRegistry
* @see MongoClientSettings.Builder#codecRegistry(CodecRegistry)
*/
public void setCodecRegistry(CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
/**
* @param uUidRepresentation
*/
public void setuUidRepresentation(@Nullable UuidRepresentation uUidRepresentation) {
this.uUidRepresentation = uUidRepresentation;
}
/**
* @param autoEncryptionSettings can be {@literal null}.
* @see MongoClientSettings.Builder#autoEncryptionSettings(AutoEncryptionSettings)
*/
public void setAutoEncryptionSettings(@Nullable AutoEncryptionSettings autoEncryptionSettings) {
this.autoEncryptionSettings = autoEncryptionSettings;
}
@Override
public Class<?> getObjectType() {
return MongoClientSettings.class;
}
@Override
protected MongoClientSettings createInstance() {
Builder builder = MongoClientSettings.builder() //
.readPreference(readPreference) //
.writeConcern(writeConcern) //
.readConcern(readConcern) //
.codecRegistry(codecRegistry) //
.applicationName(applicationName) //
.autoEncryptionSettings(autoEncryptionSettings) //
.applyToClusterSettings((settings) -> {
settings.serverSelectionTimeout(clusterServerSelectionTimeoutMS, TimeUnit.MILLISECONDS);
if (clusterConnectionMode != null) {
settings.mode(clusterConnectionMode);
}
settings.requiredReplicaSetName(clusterRequiredReplicaSetName);
if (!CollectionUtils.isEmpty(clusterHosts)) {
settings.hosts(clusterHosts);
}
settings.localThreshold(clusterLocalThresholdMS, TimeUnit.MILLISECONDS);
// settings.maxWaitQueueSize(clusterMaxWaitQueueSize);
settings.requiredClusterType(custerRequiredClusterType);
if (StringUtils.hasText(clusterSrvHost)) {
settings.srvHost(clusterSrvHost);
}
}) //
.applyToConnectionPoolSettings((settings) -> {
settings.minSize(poolMinSize);
settings.maxSize(poolMaxSize);
settings.maxConnectionIdleTime(poolMaxConnectionIdleTimeMS, TimeUnit.MILLISECONDS);
settings.maxWaitTime(poolMaxWaitTimeMS, TimeUnit.MILLISECONDS);
settings.maxConnectionLifeTime(poolMaxConnectionLifeTimeMS, TimeUnit.MILLISECONDS);
// settings.maxWaitQueueSize(poolMaxWaitQueueSize);
settings.maintenanceFrequency(poolMaintenanceFrequencyMS, TimeUnit.MILLISECONDS);
settings.maintenanceInitialDelay(poolMaintenanceInitialDelayMS, TimeUnit.MILLISECONDS);
}) //
.applyToServerSettings((settings) -> {
settings.minHeartbeatFrequency(serverMinHeartbeatFrequencyMS, TimeUnit.MILLISECONDS);
settings.heartbeatFrequency(serverHeartbeatFrequencyMS, TimeUnit.MILLISECONDS);
}) //
.applyToSocketSettings((settings) -> {
settings.connectTimeout(socketConnectTimeoutMS, TimeUnit.MILLISECONDS);
settings.readTimeout(socketReadTimeoutMS, TimeUnit.MILLISECONDS);
settings.receiveBufferSize(socketReceiveBufferSize);
settings.sendBufferSize(socketSendBufferSize);
}) //
.applyToSslSettings((settings) -> {
settings.enabled(sslEnabled);
if (sslEnabled) {
settings.invalidHostNameAllowed(sslInvalidHostNameAllowed);
try {
settings.context(
StringUtils.hasText(sslProvider) ? SSLContext.getInstance(sslProvider) : SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
});
if (streamFactoryFactory != null) {
builder = builder.streamFactoryFactory(streamFactoryFactory);
}
if (retryReads != null) {
builder = builder.retryReads(retryReads);
}
if (retryWrites != null) {
builder = builder.retryWrites(retryWrites);
}
if (uUidRepresentation != null) {
builder.uuidRepresentation(uUidRepresentation);
}
return builder.build();
}
}

View File

@@ -16,10 +16,9 @@
package org.springframework.data.mongodb.core;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.WriteResult;
import com.mongodb.WriteConcernResult;
/**
* Mongo-specific {@link DataIntegrityViolationException}.
@@ -30,18 +29,18 @@ public class MongoDataIntegrityViolationException extends DataIntegrityViolation
private static final long serialVersionUID = -186980521176764046L;
private final WriteResult writeResult;
private final WriteConcernResult writeResult;
private final MongoActionOperation actionOperation;
/**
* Creates a new {@link MongoDataIntegrityViolationException} using the given message and {@link WriteResult}.
* Creates a new {@link MongoDataIntegrityViolationException} using the given message and {@link WriteConcernResult}.
*
* @param message the exception message
* @param writeResult the {@link WriteResult} that causes the exception, must not be {@literal null}.
* @param writeResult the {@link WriteConcernResult} that causes the exception, must not be {@literal null}.
* @param actionOperation the {@link MongoActionOperation} that caused the exception, must not be {@literal null}.
*/
public MongoDataIntegrityViolationException(String message, WriteResult writeResult,
MongoActionOperation actionOperation) {
public MongoDataIntegrityViolationException(String message, WriteConcernResult writeResult,
MongoActionOperation actionOperation) {
super(message);
@@ -53,11 +52,11 @@ public class MongoDataIntegrityViolationException extends DataIntegrityViolation
}
/**
* Returns the {@link WriteResult} that caused the exception.
* Returns the {@link WriteConcernResult} that caused the exception.
*
* @return the writeResult
*/
public WriteResult getWriteResult() {
public WriteConcernResult getWriteResult() {
return writeResult;
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright 2018-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import lombok.Value;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.ClientSessionOptions;
import com.mongodb.WriteConcern;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Common base class for usage with both {@link com.mongodb.client.MongoClients} defining common properties such as
* database name and exception translator.
* <p/>
* Not intended to be used directly.
*
* @author Christoph Strobl
* @author Mark Paluch
* @param <C> Client type.
* @since 3.0
* @see SimpleMongoClientDatabaseFactory
*/
public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFactory {
private final C mongoClient;
private final String databaseName;
private final boolean mongoInstanceCreated;
private final PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern writeConcern;
/**
* Create a new {@link MongoDatabaseFactorySupport} object given {@code mongoClient}, {@code databaseName},
* {@code mongoInstanceCreated} and {@link PersistenceExceptionTranslator}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoInstanceCreated {@literal true} if the client instance was created by a subclass of
* {@link MongoDatabaseFactorySupport} to close the client on {@link #destroy()}.
* @param exceptionTranslator must not be {@literal null}.
*/
protected MongoDatabaseFactorySupport(C mongoClient, String databaseName, boolean mongoInstanceCreated,
PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(mongoClient, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!");
Assert.isTrue(databaseName.matches("[^/\\\\.$\"\\s]+"),
"Database name must not contain slashes, dots, spaces, quotes, or dollar signs!");
this.mongoClient = mongoClient;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = exceptionTranslator;
}
/**
* Configures the {@link WriteConcern} to be used on the {@link MongoDatabase} instance being created.
*
* @param writeConcern the writeConcern to set
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase()
*/
public MongoDatabase getMongoDatabase() throws DataAccessException {
return getMongoDatabase(getDefaultDatabaseName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase(java.lang.String)
*/
@Override
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty!");
MongoDatabase db = doGetMongoDatabase(dbName);
if (writeConcern == null) {
return db;
}
return db.withWriteConcern(writeConcern);
}
/**
* Get the actual {@link MongoDatabase} from the client.
*
* @param dbName must not be {@literal null} or empty.
* @return
*/
protected abstract MongoDatabase doGetMongoDatabase(String dbName);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if (mongoInstanceCreated) {
closeClient();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.Session)
*/
public MongoDatabaseFactory withSession(ClientSession session) {
return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
}
/**
* Close the client instance.
*/
protected abstract void closeClient();
/**
* @return the Mongo client object.
*/
protected C getMongoClient() {
return mongoClient;
}
/**
* @return the database name.
*/
protected String getDefaultDatabaseName() {
return databaseName;
}
/**
* {@link ClientSession} bound {@link MongoDatabaseFactory} decorating the database with a
* {@link SessionAwareMethodInterceptor}.
*
* @author Christoph Strobl
* @since 2.1
*/
@Value
static class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory {
ClientSession session;
MongoDatabaseFactory delegate;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase()
*/
@Override
public MongoDatabase getMongoDatabase() throws DataAccessException {
return proxyMongoDatabase(delegate.getMongoDatabase());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase(java.lang.String)
*/
@Override
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
return proxyMongoDatabase(delegate.getMongoDatabase(dbName));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return delegate.getSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.ClientSession)
*/
@Override
public MongoDatabaseFactory withSession(ClientSession session) {
return delegate.withSession(session);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
*/
@Override
public boolean isTransactionActive() {
return session != null && session.hasActiveTransaction();
}
private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
MongoCollection<?> collection) {
return createProxyInstance(session, collection, MongoCollection.class);
}
private <T> T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class<T> targetType) {
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.setInterfaces(targetType);
factory.setOpaque(true);
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
}
}

View File

@@ -15,26 +15,11 @@
*/
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;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.WriteConcern;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Common base class for usage with both {@link com.mongodb.client.MongoClients} and {@link com.mongodb.MongoClient}
* defining common properties such as database name and exception translator.
* Common base class for usage with both {@link com.mongodb.client.MongoClients} defining common properties such as
* database name and exception translator.
* <p/>
* Not intended to be used directly.
*
@@ -42,22 +27,16 @@ import com.mongodb.client.MongoDatabase;
* @author Mark Paluch
* @param <C> Client type.
* @since 2.1
* @see SimpleMongoDbFactory
* @see SimpleMongoClientDbFactory
* @see SimpleMongoClientDatabaseFactory
* @deprecated since 3.0, use {@link MongoDatabaseFactorySupport} instead.
*/
public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {
private final C mongoClient;
private final String databaseName;
private final boolean mongoInstanceCreated;
private final PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern writeConcern;
@Deprecated
public abstract class MongoDbFactorySupport<C> extends MongoDatabaseFactorySupport<C> {
/**
* Create a new {@link MongoDbFactorySupport} object given {@code mongoClient}, {@code databaseName},
* {@code mongoInstanceCreated} and {@link PersistenceExceptionTranslator}.
*
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoInstanceCreated {@literal true} if the client instance was created by a subclass of
@@ -66,207 +45,6 @@ public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {
*/
protected MongoDbFactorySupport(C mongoClient, String databaseName, boolean mongoInstanceCreated,
PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(mongoClient, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!");
Assert.isTrue(databaseName.matches("[^/\\\\.$\"\\s]+"),
"Database name must not contain slashes, dots, spaces, quotes, or dollar signs!");
this.mongoClient = mongoClient;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = exceptionTranslator;
super(mongoClient, databaseName, mongoInstanceCreated, exceptionTranslator);
}
/**
* Configures the {@link WriteConcern} to be used on the {@link MongoDatabase} instance being created.
*
* @param writeConcern the writeConcern to set
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb()
*/
public MongoDatabase getDb() throws DataAccessException {
return getDb(databaseName);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
*/
@Override
public MongoDatabase getDb(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty!");
MongoDatabase db = doGetMongoDatabase(dbName);
if (writeConcern == null) {
return db;
}
return db.withWriteConcern(writeConcern);
}
/**
* Get the actual {@link MongoDatabase} from the client.
*
* @param dbName must not be {@literal null} or empty.
* @return
*/
protected abstract MongoDatabase doGetMongoDatabase(String dbName);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if (mongoInstanceCreated) {
closeClient();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.Session)
*/
public MongoDbFactory withSession(ClientSession session) {
return new MongoDbFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
}
/**
* Close the client instance.
*/
protected abstract void closeClient();
/**
* @return the Mongo client object.
*/
protected C getMongoClient() {
return mongoClient;
}
/**
* @return the database name.
*/
protected String getDefaultDatabaseName() {
return databaseName;
}
/**
* {@link ClientSession} bound {@link MongoDbFactory} decorating the database with a
* {@link SessionAwareMethodInterceptor}.
*
* @author Christoph Strobl
* @since 2.1
*/
@Value
static class ClientSessionBoundMongoDbFactory implements MongoDbFactory {
ClientSession session;
MongoDbFactory delegate;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb()
*/
@Override
public MongoDatabase getDb() throws DataAccessException {
return proxyMongoDatabase(delegate.getDb());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
*/
@Override
public MongoDatabase getDb(String dbName) throws DataAccessException {
return proxyMongoDatabase(delegate.getDb(dbName));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
return delegate.getLegacyDb();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return delegate.getSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.ClientSession)
*/
@Override
public MongoDbFactory withSession(ClientSession session) {
return delegate.withSession(session);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
*/
@Override
public boolean isTransactionActive() {
return session != null && session.hasActiveTransaction();
}
private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
MongoCollection<?> collection) {
return createProxyInstance(session, collection, MongoCollection.class);
}
private <T> T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class<T> targetType) {
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.setInterfaces(targetType);
factory.setOpaque(true);
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy());
}
}
}

View File

@@ -29,7 +29,6 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PermissionDeniedDataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.ClientSessionException;
import org.springframework.data.mongodb.MongoTransactionException;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
@@ -37,7 +36,6 @@ import org.springframework.data.mongodb.util.MongoDbErrorCodes;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import com.mongodb.BulkWriteException;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoException;
import com.mongodb.MongoServerException;
@@ -112,10 +110,6 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof BulkWriteException) {
return new BulkOperationException(ex.getMessage(), (BulkWriteException) ex);
}
// All other MongoExceptions
if (ex instanceof MongoException) {
@@ -135,6 +129,7 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
} else if (MongoDbErrorCodes.isTransactionFailureCode(code)) {
return new MongoTransactionException(ex.getMessage(), ex);
}
return new UncategorizedMongoDbException(ex.getMessage(), ex);
}

View File

@@ -48,7 +48,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.Cursor;
import com.mongodb.ReadPreference;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
@@ -80,7 +79,7 @@ public interface MongoOperations extends FluentMongoOperations {
* The collection name used for the specified class by this template.
*
* @param entityClass must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
String getCollectionName(Class<?> entityClass);
@@ -224,9 +223,10 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} backed by a Mongo DB
* {@link Cursor}.
* {@link com.mongodb.client.FindIterable}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to
* be closed.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification. Must not be {@literal null}.
@@ -239,9 +239,10 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} and collection backed
* by a Mongo DB {@link Cursor}.
* by a Mongo DB {@link com.mongodb.client.FindIterable}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to
* be closed.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification. Must not be {@literal null}.
@@ -360,9 +361,9 @@ public interface MongoOperations extends FluentMongoOperations {
IndexOperations indexOps(Class<?> entityClass);
/**
* Returns the {@link ScriptOperations} that can be performed on {@link com.mongodb.DB} level.
* Returns the {@link ScriptOperations} that can be performed on {@link com.mongodb.client.MongoDatabase} level.
*
* @return
* @return never {@literal null}.
* @since 1.7
* @deprecated since 2.2. The {@code eval} command has been removed without replacement in MongoDB Server 4.2.0.
*/
@@ -518,11 +519,11 @@ public interface MongoOperations extends FluentMongoOperations {
<O> AggregationResults<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class. The name of the inputCollection is derived from the inputType of
* the aggregation.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class. The name of the inputCollection is
* derived from the inputType of the aggregation.
* <p>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -537,11 +538,11 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, String collectionName, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class and are returned as stream. The name of the inputCollection is
* derived from the inputType of the aggregation.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class and are returned as stream. The name
* of the inputCollection is derived from the inputType of the aggregation.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -555,10 +556,10 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -574,10 +575,10 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(Aggregation aggregation, Class<?> inputType, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -1373,10 +1374,9 @@ public interface MongoOperations extends FluentMongoOperations {
* the provided updated document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #updateFirst(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* <br />
* support. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
* Use {@link #findAndModify(Query, UpdateDefinition, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
@@ -1557,7 +1557,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Returns the underlying {@link MongoConverter}.
*
* @return
* @return never {@literal null}.
*/
MongoConverter getConverter();
}

View File

@@ -29,9 +29,7 @@ import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.conversions.Bson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,24 +52,23 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoDatabaseUtils;
import org.springframework.data.mongodb.MongoDbFactory;
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.MappedDocument.MappedUpdate;
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
import org.springframework.data.mongodb.core.QueryOperations.DeleteContext;
import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
import org.springframework.data.mongodb.core.QueryOperations.QueryContext;
import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
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.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.Fields;
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;
@@ -91,20 +88,12 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCre
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
import org.springframework.data.mongodb.core.mapping.event.*;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Meta;
@@ -114,6 +103,7 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Optionals;
@@ -128,11 +118,6 @@ import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.Cursor;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
@@ -141,6 +126,7 @@ import com.mongodb.client.ClientSession;
import com.mongodb.client.DistinctIterable;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MapReduceIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
@@ -173,8 +159,8 @@ import com.mongodb.client.result.UpdateResult;
* @author Andreas Zink
* @author Cimon Lucas
* @author Michael J. Simons
* @author Roman Puchkovskiy
*/
@SuppressWarnings("deprecation")
public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
@@ -193,7 +179,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final MongoConverter mongoConverter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final MongoDbFactory mongoDbFactory;
private final MongoDatabaseFactory mongoDbFactory;
private final PersistenceExceptionTranslator exceptionTranslator;
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
@@ -201,6 +187,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final SpelAwareProxyProjectionFactory projectionFactory;
private final EntityOperations operations;
private final PropertyOperations propertyOperations;
private final QueryOperations queryOperations;
private @Nullable WriteConcern writeConcern;
private WriteConcernResolver writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
@@ -213,18 +200,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private SessionSynchronization sessionSynchronization = SessionSynchronization.ON_ACTUAL_TRANSACTION;
/**
* Constructor used for a basic template configuration.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @deprecated since 2.2 in favor of {@link #MongoTemplate(com.mongodb.client.MongoClient, String)}.
*/
@Deprecated
public MongoTemplate(MongoClient mongoClient, String databaseName) {
this(new SimpleMongoDbFactory(mongoClient, databaseName), (MongoConverter) null);
}
/**
* Constructor used for a basic template configuration.
*
@@ -232,8 +207,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param databaseName must not be {@literal null} or empty.
* @since 2.1
*/
public MongoTemplate(com.mongodb.client.MongoClient mongoClient, String databaseName) {
this(new SimpleMongoClientDbFactory(mongoClient, databaseName), (MongoConverter) null);
public MongoTemplate(MongoClient mongoClient, String databaseName) {
this(new SimpleMongoClientDatabaseFactory(mongoClient, databaseName), (MongoConverter) null);
}
/**
@@ -241,7 +216,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*
* @param mongoDbFactory must not be {@literal null}.
*/
public MongoTemplate(MongoDbFactory mongoDbFactory) {
public MongoTemplate(MongoDatabaseFactory mongoDbFactory) {
this(mongoDbFactory, (MongoConverter) null);
}
@@ -251,7 +226,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param mongoDbFactory must not be {@literal null}.
* @param mongoConverter
*/
public MongoTemplate(MongoDbFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) {
public MongoTemplate(MongoDatabaseFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) {
Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");
@@ -264,6 +239,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
this.projectionFactory = new SpelAwareProxyProjectionFactory();
this.operations = new EntityOperations(this.mongoConverter.getMappingContext());
this.propertyOperations = new PropertyOperations(this.mongoConverter.getMappingContext());
this.queryOperations = new QueryOperations(queryMapper, updateMapper, operations, propertyOperations,
mongoDbFactory);
// We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this.mongoConverter.getMappingContext();
@@ -281,7 +258,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
}
private MongoTemplate(MongoDbFactory dbFactory, MongoTemplate that) {
private MongoTemplate(MongoDatabaseFactory dbFactory, MongoTemplate that) {
this.mongoDbFactory = dbFactory;
this.exceptionTranslator = that.exceptionTranslator;
@@ -302,6 +279,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
this.mappingContext = that.mappingContext;
this.operations = that.operations;
this.propertyOperations = that.propertyOperations;
this.queryOperations = that.queryOperations;
}
/**
@@ -316,8 +294,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
/**
* Configures the {@link WriteConcern} to be used with the template. If none is configured the {@link WriteConcern}
* configured on the {@link MongoDbFactory} will apply. If you configured a {@link Mongo} instance no
* {@link WriteConcern} will be used.
* configured on the {@link MongoDatabaseFactory} will apply.
*
* @param writeConcern
*/
@@ -449,8 +426,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityType);
Document mappedFields = getMappedFieldsObject(query.getFieldsObject(), persistentEntity, returnType);
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), persistentEntity);
QueryContext queryContext = queryOperations.createQueryContext(query);
Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
Document mappedFields = queryContext.getMappedFields(persistentEntity, returnType, projectionFactory);
FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
col -> col.find(mappedQuery, Document.class).projection(mappedFields));
@@ -524,8 +503,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* specification, must not be {@literal null}.
* @param collectionName name of the collection to retrieve the objects from
* @param documentCallbackHandler the handler that will extract results, one document at a time
* @param preparer allows for customization of the {@link DBCursor} used when iterating over the result set, (apply
* limits, skips and so on).
* @param preparer allows for customization of the {@link FindIterable} used when iterating over the result set,
* (apply limits, skips and so on).
*/
protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler documentCallbackHandler,
@Nullable CursorPreparer preparer) {
@@ -845,10 +824,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
Assert.notNull(collectionName, "CollectionName must not be null!");
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), getPersistentEntity(entityClass));
QueryContext queryContext = queryOperations.createQueryContext(query);
Document mappedQuery = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
return execute(collectionName, new ExistsCallback(mappedQuery,
operations.forType(entityClass).getCollation(query).map(Collation::toMongoCollation).orElse(null)));
return execute(collectionName,
new ExistsCallback(mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
}
// Find methods that take a Query to express the query and that return a List of objects.
@@ -921,13 +901,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(resultClass, "ResultClass must not be null!");
MongoPersistentEntity<?> entity = entityClass != Object.class ? getPersistentEntity(entityClass) : null;
DistinctQueryContext distinctQueryContext = queryOperations.distinctQueryContext(query, field);
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);
String mappedFieldName = queryMapper.getMappedFields(new Document(field, 1), entity).keySet().iterator().next();
Class<T> mongoDriverCompatibleType = getMongoDbFactory().getCodecFor(resultClass) //
.map(Codec::getEncoderClass) //
.orElse((Class<T>) BsonValue.class);
Document mappedQuery = distinctQueryContext.getMappedQuery(entity);
String mappedFieldName = distinctQueryContext.getMappedFieldName(entity);
Class<T> mongoDriverCompatibleType = distinctQueryContext.getDriverCompatibleClass(resultClass);
MongoIterable<?> result = execute(collectionName, (collection) -> {
@@ -942,12 +920,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
DistinctIterable<T> iterable = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
distinctQueryContext.applyCollation(entityClass, iterable::collation);
return operations.forType(entityClass) //
.getCollation(query) //
.map(Collation::toMongoCollation) //
.map(iterable::collation) //
.orElse(iterable);
return iterable;
});
if (resultClass == Object.class || mongoDriverCompatibleType != resultClass) {
@@ -956,7 +931,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
result = result.map((source) -> converter.mapValueToTargetType(source,
getMostSpecificConversionTargetType(resultClass, entityClass, field), dbRefResolver));
distinctQueryContext.getMostSpecificConversionTargetType(resultClass, entityClass), dbRefResolver));
}
try {
@@ -966,32 +941,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
}
/**
* @param userType must not be {@literal null}.
* @param domainType must not be {@literal null}.
* @param field must not be {@literal null}.
* @return the most specific conversion target type depending on user preference and domain type property.
* @since 2.1
*/
private static Class<?> getMostSpecificConversionTargetType(Class<?> userType, Class<?> domainType, String field) {
Class<?> conversionTargetType = userType;
try {
Class<?> propertyType = PropertyPath.from(field, domainType).getLeafProperty().getLeafType();
// use the more specific type but favor UserType over property one
if (ClassUtils.isAssignable(userType, propertyType)) {
conversionTargetType = propertyType;
}
} catch (PropertyReferenceException e) {
// just don't care about it as we default to Object.class anyway.
}
return conversionTargetType;
}
@Override
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass) {
return geoNear(near, entityClass, getCollectionName(entityClass));
@@ -1106,10 +1055,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.isTrue(query.getSkip() <= 0, "Query must not define skip.");
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
QueryContext queryContext = queryOperations.createQueryContext(query);
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);
Document mappedFields = queryMapper.getMappedFields(query.getFieldsObject(), entity);
Document mappedSort = queryMapper.getMappedSort(query.getSortObject(), entity);
Document mappedQuery = queryContext.getMappedQuery(entity);
Document mappedFields = queryContext.getMappedFields(entity, resultType, projectionFactory);
Document mappedSort = queryContext.getMappedSort(entity);
replacement = maybeCallBeforeConvert(replacement, collectionName);
Document mappedReplacement = operations.forEntity(replacement).toMappedDocument(this.mongoConverter).getDocument();
@@ -1117,9 +1067,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
operations.forType(entityType).getCollation(query).map(Collation::toMongoCollation).orElse(null), entityType,
mappedReplacement, options, resultType);
T saved = doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, resultType);
if (saved != null) {
maybeEmitEvent(new AfterSaveEvent<>(saved, mappedReplacement, collectionName));
return maybeCallAfterSave(saved, mappedReplacement, collectionName);
}
return saved;
}
// Find methods that take a Query to express the query and that return a single object that is also removed from the
@@ -1165,23 +1121,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(query, "Query must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
CountOptions options = new CountOptions();
query.getCollation().map(Collation::toMongoCollation).ifPresent(options::collation);
CountContext countContext = queryOperations.countQueryContext(query);
if (query.getLimit() > 0) {
options.limit(query.getLimit());
}
if (query.getSkip() > 0) {
options.skip((int) query.getSkip());
}
if (StringUtils.hasText(query.getHint())) {
options.hint(Document.parse(query.getHint()));
}
CountOptions options = countContext.getCountOptions(entityClass);
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
Document document = queryMapper.getMappedObject(query.getQueryObject(),
Optional.ofNullable(entityClass).map(it -> mappingContext.getPersistentEntity(entityClass)));
return doCount(collectionName, document, options);
return doCount(collectionName, mappedQuery, options);
}
@SuppressWarnings("ConstantConditions")
@@ -1224,7 +1169,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
protected void ensureNotIterable(@Nullable Object o) {
if (null != o) {
if (o != null) {
if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) {
throw new IllegalArgumentException("Cannot use a collection here.");
}
@@ -1233,7 +1178,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
/**
* Prepare the collection before any processing is done using it. This allows a convenient way to apply settings like
* slaveOk() etc. Can be overridden in sub-classes.
* withCodecRegistry() etc. Can be overridden in sub-classes.
*
* @param collection
*/
@@ -1292,8 +1237,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
T saved = populateIdIfNecessary(initialized, id);
maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
return saved;
return maybeCallAfterSave(saved, dbDoc, collectionName);
}
@Override
@@ -1386,8 +1330,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (i < ids.size()) {
T saved = populateIdIfNecessary(obj, ids.get(i));
maybeEmitEvent(new AfterSaveEvent<>(saved, documentList.get(i), collectionName));
savedObjects.add(saved);
Document doc = documentList.get(i);
maybeEmitEvent(new AfterSaveEvent<>(saved, doc, collectionName));
savedObjects.add(maybeCallAfterSave(saved, doc, collectionName));
} else {
savedObjects.add(obj);
}
@@ -1457,7 +1402,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
maybeEmitEvent(new AfterSaveEvent<>(toSave, mapped.getDocument(), collectionName));
return toSave;
return maybeCallAfterSave(toSave, mapped.getDocument(), collectionName);
}
protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
@@ -1478,7 +1423,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
T saved = populateIdIfNecessary(objectToSave, id);
maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
return saved;
return maybeCallAfterSave(saved, dbDoc, collectionName);
}
@SuppressWarnings("ConstantConditions")
@@ -1538,23 +1483,38 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
return execute(collectionName, collection -> {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
MappedDocument mapped = MappedDocument.of(dbDoc);
MongoCollection<Document> collectionToUse = writeConcernToUse == null //
? collection //
: collection.withWriteConcern(writeConcernToUse);
if (!mapped.hasId()) {
if (writeConcernToUse == null) {
collection.insertOne(dbDoc);
} else {
collection.withWriteConcern(writeConcernToUse).insertOne(dbDoc);
}
} else if (writeConcernToUse == null) {
collection.replaceOne(mapped.getIdFilter(), dbDoc, new ReplaceOptions().upsert(true));
collectionToUse.insertOne(dbDoc);
} else {
collection.withWriteConcern(writeConcernToUse).replaceOne(mapped.getIdFilter(), dbDoc,
new ReplaceOptions().upsert(true));
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
UpdateContext updateContext = queryOperations.replaceSingleContext(mapped, true);
Document replacement = updateContext.getMappedUpdate(entity);
Document filter = updateContext.getMappedQuery(entity);
if (updateContext.requiresShardKey(filter, entity)) {
if (entity.getShardKey().isImmutable()) {
filter = updateContext.applyShardKey(entity, filter, null);
} else {
filter = updateContext.applyShardKey(entity, filter,
collection.find(filter, Document.class).projection(updateContext.getMappedShardKey(entity)).first());
}
}
collectionToUse.replaceOne(filter, replacement, new ReplaceOptions().upsert(true));
}
return mapped.getId();
});
@@ -1629,33 +1589,20 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass);
increaseVersionForUpdateIfNecessary(entity, update);
UpdateOptions opts = new UpdateOptions();
opts.upsert(upsert);
UpdateContext updateContext = multi ? queryOperations.updateContext(update, query, upsert)
: queryOperations.updateSingleContext(update, query, upsert);
updateContext.increaseVersionForUpdateIfNecessary(entity);
if (update.hasArrayFilters()) {
opts.arrayFilters(update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()));
}
Document queryObj = updateContext.getMappedQuery(entity);
UpdateOptions opts = updateContext.getUpdateOptions(entityClass);
Document queryObj = new Document();
if (updateContext.isAggregationUpdate()) {
if (query != null) {
queryObj.putAll(queryMapper.getMappedObject(query.getQueryObject(), entity));
}
if (multi && update.isIsolated() && !queryObj.containsKey("$isolated")) {
queryObj.put("$isolated", 1);
}
if (update instanceof AggregationUpdate) {
AggregationOperationContext context = entityClass != null
? new RelaxedTypeBasedAggregationOperationContext(entityClass, mappingContext, queryMapper)
: Aggregation.DEFAULT_CONTEXT;
List<Document> pipeline = new AggregationUtil(queryMapper, mappingContext)
.createPipeline((AggregationUpdate) update, context);
List<Document> pipeline = updateContext.getUpdatePipeline(entityClass);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass,
update.getUpdateObject(), queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
return execute(collectionName, collection -> {
@@ -1664,44 +1611,42 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
serializeToJsonSafely(queryObj), serializeToJsonSafely(pipeline), collectionName);
}
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName,
entityClass, update.getUpdateObject(), queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
collection = writeConcernToUse != null ? collection.withWriteConcern(writeConcernToUse) : collection;
return multi ? collection.updateMany(queryObj, pipeline, opts) : collection.updateOne(queryObj, pipeline, opts);
});
}
Document updateObj = updateContext.getMappedUpdate(entity);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass,
updateObj, queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
return execute(collectionName, collection -> {
operations.forType(entityClass) //
.getCollation(query) //
.map(Collation::toMongoCollation) //
.ifPresent(opts::collation);
Document updateObj = update instanceof MappedUpdate ? update.getUpdateObject()
: updateMapper.getMappedObject(update.getUpdateObject(), entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Calling update using query: {} and update: {} in collection: {}", serializeToJsonSafely(queryObj),
serializeToJsonSafely(updateObj), collectionName);
}
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass,
updateObj, queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
collection = writeConcernToUse != null ? collection.withWriteConcern(writeConcernToUse) : collection;
if (!UpdateMapper.isUpdateObject(updateObj)) {
ReplaceOptions replaceOptions = new ReplaceOptions();
replaceOptions.collation(opts.getCollation());
replaceOptions.upsert(opts.isUpsert());
Document filter = new Document(queryObj);
return collection.replaceOne(queryObj, updateObj, replaceOptions);
if (updateContext.requiresShardKey(filter, entity)) {
if (entity.getShardKey().isImmutable()) {
filter = updateContext.applyShardKey(entity, filter, null);
} else {
filter = updateContext.applyShardKey(entity, filter,
collection.find(filter, Document.class).projection(updateContext.getMappedShardKey(entity)).first());
}
}
ReplaceOptions replaceOptions = updateContext.getReplaceOptions(entityClass);
return collection.replaceOne(filter, updateObj, replaceOptions);
} else {
return multi ? collection.updateMany(queryObj, updateObj, opts)
: collection.updateOne(queryObj, updateObj, opts);
@@ -1709,17 +1654,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
});
}
private void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity<?> persistentEntity,
UpdateDefinition update) {
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName();
if (!update.modifies(versionFieldName)) {
update.inc(versionFieldName);
}
}
}
@Override
public DeleteResult remove(Object object) {
@@ -1764,7 +1698,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.hasText(collectionName, "Collection name must not be null or empty!");
MongoPersistentEntity<?> entity = getPersistentEntity(entityClass);
Document queryObject = queryMapper.getMappedObject(query.getQueryObject(), entity);
DeleteContext deleteContext = multi ? queryOperations.deleteQueryContext(query)
: queryOperations.deleteSingleContext(query);
Document queryObject = deleteContext.getMappedQuery(entity);
DeleteOptions options = deleteContext.getDeleteOptions(entityClass);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass,
null, queryObject);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
return execute(collectionName, collection -> {
@@ -1772,18 +1715,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Document removeQuery = queryObject;
DeleteOptions options = new DeleteOptions();
operations.forType(entityClass) //
.getCollation(query) //
.map(Collation::toMongoCollation) //
.ifPresent(options::collation);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass,
null, queryObject);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Remove using query: {} in collection: {}.",
new Object[] { serializeToJsonSafely(removeQuery), collectionName });
@@ -1892,7 +1823,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (query.getMeta().getMaxTimeMsec() != null) {
mapReduce = mapReduce.maxTime(query.getMeta().getMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
mapReduce = mapReduce.sort(getMappedSortObject(query, domainType));
Document mappedSort = getMappedSortObject(query, domainType);
if (mappedSort != null && !mappedSort.isEmpty()) {
mapReduce = mapReduce.sort(getMappedSortObject(query, domainType));
}
mapReduce = mapReduce
.filter(queryMapper.getMappedObject(query.getQueryObject(), mappingContext.getPersistentEntity(domainType)));
@@ -2190,7 +2125,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
List<Document> rawResult = new ArrayList<>();
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation) aggregation).getInputType()
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation<?>) aggregation).getInputType()
: null;
Optional<Collation> collation = Optionals.firstNonEmpty(options::getCollation,
@@ -2211,6 +2146,17 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
aggregateIterable = aggregateIterable.maxTime(options.getMaxTime().toMillis(), TimeUnit.MILLISECONDS);
}
if (options.isSkipResults()) {
// toCollection only allowed for $out and $merge if those are the last stages
if (aggregation.getPipeline().isOutOrMerge()) {
aggregateIterable.toCollection();
} else {
aggregateIterable.first();
}
return new AggregationResults<>(Collections.emptyList(), new Document());
}
MongoIterable<O> iterable = aggregateIterable.map(val -> {
rawResult.add(val);
@@ -2378,33 +2324,49 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
protected <E extends MongoMappingEvent<T>, T> E maybeEmitEvent(E event) {
if (null != eventPublisher) {
if (eventPublisher != null) {
eventPublisher.publishEvent(event);
}
return event;
}
@SuppressWarnings("unchecked")
protected <T> T maybeCallBeforeConvert(T object, String collection) {
if (null != entityCallbacks) {
if (entityCallbacks != null) {
return entityCallbacks.callback(BeforeConvertCallback.class, object, collection);
}
return object;
}
@SuppressWarnings("unchecked")
protected <T> T maybeCallBeforeSave(T object, Document document, String collection) {
if (null != entityCallbacks) {
if (entityCallbacks != null) {
return entityCallbacks.callback(BeforeSaveCallback.class, object, document, collection);
}
return object;
}
protected <T> T maybeCallAfterSave(T object, Document document, String collection) {
if (entityCallbacks != null) {
return entityCallbacks.callback(AfterSaveCallback.class, object, document, collection);
}
return object;
}
protected <T> T maybeCallAfterConvert(T object, Document document, String collection) {
if (entityCallbacks != null) {
return entityCallbacks.callback(AfterConvertCallback.class, object, document, collection);
}
return object;
}
/**
* Create the specified collection using the provided options
*
@@ -2491,8 +2453,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Class<T> entityClass) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
Document mappedFields = queryMapper.getMappedObject(fields, entity);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
Document mappedFields = queryContext.getMappedFields(entity, entityClass, projectionFactory);
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findOne using query: {} fields: {} for class: {} in collection: {}", serializeToJsonSafely(query),
@@ -2527,8 +2491,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @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 DBCursor} used when iterating over the result set, (apply
* limits, skips and so on).
* @param preparer allows for customization of the {@link FindIterable} used when iterating over the result set,
* (apply limits, skips and so on).
* @return the {@link List} of converted objects.
*/
protected <T> List<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
@@ -2542,8 +2506,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Document mappedFields = queryMapper.getMappedFields(fields, entity);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
Document mappedFields = queryContext.getMappedFields(entity, entityClass, projectionFactory);
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: {} fields: {} for class: {} in collection: {}",
@@ -2565,8 +2530,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
Document mappedFields = getMappedFieldsObject(fields, entity, targetClass);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
Document mappedFields = queryContext.getMappedFields(entity, targetClass, projectionFactory);
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: {} fields: {} for class: {} in collection: {}",
@@ -2682,21 +2648,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
increaseVersionForUpdateIfNecessary(entity, update);
UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false);
updateContext.increaseVersionForUpdateIfNecessary(entity);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
Object mappedUpdate;
if (update instanceof AggregationUpdate) {
AggregationOperationContext context = entityClass != null
? new RelaxedTypeBasedAggregationOperationContext(entityClass, mappingContext, queryMapper)
: Aggregation.DEFAULT_CONTEXT;
mappedUpdate = new AggregationUtil(queryMapper, mappingContext).createPipeline((Aggregation) update, context);
} else {
mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity);
}
Document mappedQuery = updateContext.getMappedQuery(entity);
Object mappedUpdate = updateContext.isAggregationUpdate() ? updateContext.getUpdatePipeline(entityClass)
: updateContext.getMappedUpdate(entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
@@ -2797,16 +2754,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* Internal method using callback to do queries against the datastore that requires reading a collection of objects.
* It will take the following steps
* <ol>
* <li>Execute the given {@link ConnectionCallback} for a {@link DBCursor}.</li>
* <li>Prepare that {@link DBCursor} with the given {@link CursorPreparer} (will be skipped if {@link CursorPreparer}
* is {@literal null}</li>
* <li>Iterate over the {@link DBCursor} and applies the given {@link DocumentCallback} to each of the
* <li>Execute the given {@link ConnectionCallback} for a {@link FindIterable}.</li>
* <li>Prepare that {@link FindIterable} with the given {@link CursorPreparer} (will be skipped if
* {@link CursorPreparer} is {@literal null}</li>
* <li>Iterate over the {@link FindIterable} and applies the given {@link DocumentCallback} to each of the
* {@link Document}s collecting the actual result {@link List}.</li>
* <ol>
*
* @param <T>
* @param collectionCallback the callback to retrieve the {@link DBCursor} with
* @param preparer the {@link CursorPreparer} to potentially modify the {@link DBCursor} before iterating over it
* @param collectionCallback the callback to retrieve the {@link FindIterable} with
* @param preparer the {@link CursorPreparer} to potentially modify the {@link FindIterable} before iterating over it
* @param objectCallback the {@link DocumentCallback} to transform {@link Document}s into the actual domain type
* @param collectionName the collection to be queried
* @return
@@ -2878,7 +2835,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return type != null ? mappingContext.getPersistentEntity(type) : null;
}
private static MongoConverter getDefaultMongoConverter(MongoDbFactory factory) {
private static MongoConverter getDefaultMongoConverter(MongoDatabaseFactory factory) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MongoCustomConversions conversions = new MongoCustomConversions(Collections.emptyList());
@@ -2904,23 +2861,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return queryMapper.getMappedSort(query.getSortObject(), mappingContext.getPersistentEntity(type));
}
private Document getMappedFieldsObject(Document fields, @Nullable MongoPersistentEntity<?> entity,
Class<?> targetType) {
if (entity == null) {
return fields;
}
Document projectedFields = propertyOperations.computeFieldsForProjection(projectionFactory, fields,
entity.getType(), targetType);
if (ObjectUtils.nullSafeEquals(fields, projectedFields)) {
return queryMapper.getMappedFields(projectedFields, entity);
}
return queryMapper.getMappedFields(projectedFields, mappingContext.getRequiredPersistentEntity(targetType));
}
/**
* Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
* exception if the conversation failed. Thus allows safe re-throwing of the return value.
@@ -2939,7 +2879,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
/**
* Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification
* {@link Document} and executes that against the {@link DBCollection}.
* {@link Document} and executes that against the {@link MongoCollection}.
*
* @author Oliver Gierke
* @author Thomas Risberg
@@ -2980,7 +2920,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
/**
* Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification
* {@link Document} and executes that against the {@link DBCollection}.
* {@link Document} and executes that against the {@link MongoCollection}.
*
* @author Oliver Gierke
* @author Thomas Risberg
@@ -3037,7 +2977,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
/**
* Simple {@link CollectionCallback} that takes a query {@link Document} plus an optional fields specification
* {@link Document} and executes that against the {@link DBCollection}.
* {@link Document} and executes that against the {@link MongoCollection}.
*
* @author Thomas Risberg
*/
@@ -3182,6 +3122,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Roman Puchkovskiy
*/
@RequiredArgsConstructor
private class ReadDocumentCallback<T> implements DocumentCallback<T> {
@@ -3191,16 +3132,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final String collectionName;
@Nullable
public T doWith(@Nullable Document object) {
public T doWith(@Nullable Document document) {
if (null != object) {
maybeEmitEvent(new AfterLoadEvent<>(object, type, collectionName));
T source = null;
if (document != null) {
maybeEmitEvent(new AfterLoadEvent<>(document, type, collectionName));
source = reader.read(type, document);
}
T source = reader.read(type, object);
if (null != source) {
maybeEmitEvent(new AfterConvertEvent<>(object, source, collectionName));
if (source != null) {
maybeEmitEvent(new AfterConvertEvent<>(document, source, collectionName));
source = maybeCallAfterConvert(source, document, collectionName);
}
return source;
@@ -3229,24 +3172,23 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/
@SuppressWarnings("unchecked")
@Nullable
public T doWith(@Nullable Document object) {
public T doWith(@Nullable Document document) {
if (object == null) {
if (document == null) {
return null;
}
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
: targetType;
if (null != object) {
maybeEmitEvent(new AfterLoadEvent<>(object, targetType, collectionName));
}
maybeEmitEvent(new AfterLoadEvent<>(document, targetType, collectionName));
Object source = reader.read(typeToRead, object);
Object source = reader.read(typeToRead, document);
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
if (null != result) {
maybeEmitEvent(new AfterConvertEvent<>(object, result, collectionName));
if (result != null) {
maybeEmitEvent(new AfterConvertEvent<>(document, result, collectionName));
result = maybeCallAfterConvert(result, document, collectionName);
}
return (T) result;
@@ -3268,9 +3210,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.CursorPreparer#prepare(com.mongodb.DBCursor)
*/
public FindIterable<Document> prepare(FindIterable<Document> cursor) {
public FindIterable<Document> prepare(FindIterable<Document> iterable) {
FindIterable<Document> cursorToUse = cursor;
FindIterable<Document> cursorToUse = iterable;
operations.forType(type).getCollation(query) //
.map(Collation::toMongoCollation) //
@@ -3295,7 +3237,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
if (StringUtils.hasText(query.getHint())) {
cursorToUse = cursorToUse.hint(Document.parse(query.getHint()));
String hint = query.getHint();
if (BsonUtils.isJsonDocument(hint)) {
cursorToUse = cursorToUse.hint(BsonUtils.parse(hint, mongoDbFactory));
} else {
cursorToUse = cursorToUse.hintString(hint);
}
}
if (meta.hasValues()) {
@@ -3304,14 +3253,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
cursorToUse = cursorToUse.comment(meta.getComment());
}
if (meta.getSnapshot()) {
cursorToUse = cursorToUse.snapshot(meta.getSnapshot());
}
if (meta.getMaxScan() != null) {
cursorToUse = cursorToUse.maxScan(meta.getMaxScan());
}
if (meta.getMaxTimeMsec() != null) {
cursorToUse = cursorToUse.maxTime(meta.getMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
@@ -3330,6 +3271,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
case PARTIAL:
cursorToUse = cursorToUse.partial(true);
break;
case SECONDARY_READS:
case SLAVE_OK:
break;
default:
@@ -3347,7 +3289,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@Override
public ReadPreference getReadPreference() {
return query.getMeta().getFlags().contains(CursorOption.SLAVE_OK) ? ReadPreference.primaryPreferred() : null;
return (query.getMeta().getFlags().contains(CursorOption.SECONDARY_READS)
|| query.getMeta().getFlags().contains(CursorOption.SLAVE_OK)) ? ReadPreference.primaryPreferred() : null;
}
}
@@ -3396,7 +3339,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
/**
* A {@link CloseableIterator} that is backed by a MongoDB {@link Cursor}.
* A {@link CloseableIterator} that is backed by a MongoDB {@link MongoCollection}.
*
* @author Thomas Darimont
* @since 1.7
@@ -3409,7 +3352,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private DocumentCallback<T> objectReadCallback;
/**
* Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link Cursor}.
* Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link MongoCollection}.
*
* @param cursor
* @param exceptionTranslator
@@ -3476,7 +3419,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
}
public MongoDbFactory getMongoDbFactory() {
public MongoDatabaseFactory getMongoDbFactory() {
return mongoDbFactory;
}

View File

@@ -0,0 +1,763 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;
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.core.MappedDocument.MappedUpdate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.ShardKey;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
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.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateOptions;
/**
* {@link QueryOperations} centralizes common operations required before an operation is actually ready to be executed.
* This involves mapping {@link Query queries} into their respective MongoDB representation, computing execution options
* for {@literal count}, {@literal remove}, and other methods.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
class QueryOperations {
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final EntityOperations entityOperations;
private final PropertyOperations propertyOperations;
private final CodecRegistryProvider codecRegistryProvider;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final AggregationUtil aggregationUtil;
private final Map<Class<?>, Document> mappedShardKey = new ConcurrentHashMap<>(1);
/**
* Create a new instance of {@link QueryOperations}.
*
* @param queryMapper must not be {@literal null}.
* @param updateMapper must not be {@literal null}.
* @param entityOperations must not be {@literal null}.
* @param propertyOperations must not be {@literal null}.
* @param codecRegistryProvider must not be {@literal null}.
*/
QueryOperations(QueryMapper queryMapper, UpdateMapper updateMapper, EntityOperations entityOperations,
PropertyOperations propertyOperations, CodecRegistryProvider codecRegistryProvider) {
this.queryMapper = queryMapper;
this.updateMapper = updateMapper;
this.entityOperations = entityOperations;
this.propertyOperations = propertyOperations;
this.codecRegistryProvider = codecRegistryProvider;
this.mappingContext = queryMapper.getMappingContext();
this.aggregationUtil = new AggregationUtil(queryMapper, mappingContext);
}
/**
* Create a new {@link QueryContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
QueryContext createQueryContext(Query query) {
return new QueryContext(query);
}
/**
* Create a new {@link DistinctQueryContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link DistinctQueryContext}.
*/
DistinctQueryContext distinctQueryContext(Query query, String fieldName) {
return new DistinctQueryContext(query, fieldName);
}
/**
* Create a new {@link CountContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link CountContext}.
*/
CountContext countQueryContext(Query query) {
return new CountContext(query);
}
/**
* Create a new {@link UpdateContext} instance affecting multiple documents.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateContext(UpdateDefinition updateDefinition, Query query, boolean upsert) {
return new UpdateContext(updateDefinition, query, true, upsert);
}
/**
* Create a new {@link UpdateContext} instance affecting a single document.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateSingleContext(UpdateDefinition updateDefinition, Query query, boolean upsert) {
return new UpdateContext(updateDefinition, query, false, upsert);
}
/**
* Create a new {@link UpdateContext} instance affecting a single document.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateSingleContext(UpdateDefinition updateDefinition, Document query, boolean upsert) {
return new UpdateContext(updateDefinition, query, false, upsert);
}
/**
* @param replacement the {@link MappedDocument mapped replacement} document.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext replaceSingleContext(MappedDocument replacement, boolean upsert) {
return new UpdateContext(replacement, upsert);
}
/**
* Create a new {@link DeleteContext} instance removing all matching documents.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
DeleteContext deleteQueryContext(Query query) {
return new DeleteContext(query, true);
}
/**
* Create a new {@link DeleteContext} instance only the first matching document.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
DeleteContext deleteSingleContext(Query query) {
return new DeleteContext(query, false);
}
/**
* {@link QueryContext} encapsulates common tasks required to convert a {@link Query} into its MongoDB document
* representation, mapping fieldnames, as well as determinging and applying {@link Collation collations}.
*
* @author Christoph Strobl
*/
class QueryContext {
private final Query query;
/**
* 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}.
*/
private QueryContext(@Nullable Query query) {
this.query = query != null ? query : new Query();
}
/**
* @return never {@literal null}.
*/
Query getQuery() {
return query;
}
/**
* Extract the raw {@link Query#getQueryObject() unmapped document} from the {@link Query}.
*
* @return
*/
Document getQueryObject() {
return query.getQueryObject();
}
/**
* Get the already mapped MongoDB query representation.
*
* @param domainType can be {@literal null}.
* @param entityLookup the {@link Function lookup} used to provide the {@link MongoPersistentEntity} for the
* given{@literal domainType}
* @param <T>
* @return never {@literal null}.
*/
<T> Document getMappedQuery(@Nullable Class<T> domainType,
Function<Class<T>, MongoPersistentEntity<?>> entityLookup) {
return getMappedQuery(domainType == null ? null : entityLookup.apply(domainType));
}
/**
* Get the already mapped MongoDB query representation.
*
* @param entity the Entity to map field names to. Can be {@literal null}.
* @param <T>
* @return never {@literal null}.
*/
<T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> entity) {
return queryMapper.getMappedObject(getQueryObject(), entity);
}
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType,
ProjectionFactory projectionFactory) {
Document fields = query.getFieldsObject();
Document mappedFields = fields;
if (entity == null) {
return mappedFields;
}
Document projectedFields = propertyOperations.computeFieldsForProjection(projectionFactory, fields,
entity.getType(), targetType);
if (ObjectUtils.nullSafeEquals(fields, projectedFields)) {
mappedFields = queryMapper.getMappedFields(projectedFields, entity);
} else {
mappedFields = queryMapper.getMappedFields(projectedFields,
mappingContext.getRequiredPersistentEntity(targetType));
}
if (entity != null && entity.hasTextScoreProperty() && !query.getQueryObject().containsKey("$text")) {
mappedFields.remove(entity.getTextScoreProperty().getFieldName());
}
return mappedFields;
}
/**
* Get the already mapped {@link Query#getSortObject() sort} option.
*
* @param entity the Entity to map field names to. Can be {@literal null}.
* @return never {@literal null}.
*/
Document getMappedSort(@Nullable MongoPersistentEntity<?> entity) {
return queryMapper.getMappedSort(query.getSortObject(), entity);
}
/**
* Apply the {@link com.mongodb.client.model.Collation} if present extracted from the {@link Query} or fall back to
* the {@literal domain types} default {@link org.springframework.data.mongodb.core.mapping.Document#collation()
* collation}.
*
* @param domainType can be {@literal null}.
* @param consumer must not be {@literal null}.
*/
void applyCollation(@Nullable Class<?> domainType, Consumer<com.mongodb.client.model.Collation> consumer) {
getCollation(domainType).ifPresent(consumer::accept);
}
/**
* Get the {@link com.mongodb.client.model.Collation} extracted from the {@link Query} if present or fall back to
* the {@literal domain types} default {@link org.springframework.data.mongodb.core.mapping.Document#collation()
* collation}.
*
* @param domainType can be {@literal null}.
* @return never {@literal null}.
*/
Optional<com.mongodb.client.model.Collation> getCollation(@Nullable Class<?> domainType) {
return entityOperations.forType(domainType).getCollation(query) //
.map(Collation::toMongoCollation);
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal distinct} queries.
*
* @author Christoph Strobl
*/
class DistinctQueryContext extends QueryContext {
private final String fieldName;
/**
* Create a new {@link DistinctQueryContext} instance.
*
* @param query can be {@literal null}.
* @param fieldName must not be {@literal null}.
*/
private DistinctQueryContext(@Nullable Object query, String fieldName) {
super(query instanceof Document ? new BasicQuery((Document) query) : (Query) query);
this.fieldName = fieldName;
}
@Override
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType, ProjectionFactory projectionFactory) {
return getMappedFields(entity);
}
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity) {
return queryMapper.getMappedFields(new Document(fieldName, 1), entity);
}
/**
* Get the mapped field name to project to.
*
* @param entity can be {@literal null}.
* @return never {@literal null}.
*/
String getMappedFieldName(@Nullable MongoPersistentEntity<?> entity) {
return getMappedFields(entity).keySet().iterator().next();
}
/**
* Get the MongoDB native representation of the given {@literal type}.
*
* @param type must not be {@literal null}.
* @param <T>
* @return never {@literal null}.
*/
@SuppressWarnings("unchecked")
<T> Class<T> getDriverCompatibleClass(Class<T> type) {
return codecRegistryProvider.getCodecFor(type) //
.map(Codec::getEncoderClass) //
.orElse((Class<T>) BsonValue.class);
}
/**
* Get the most speficic read target type based on the user {@literal requestedTargetType} an the property type
* based on meta information extracted from the {@literal domainType}.
*
* @param requestedTargetType must not be {@literal null}.
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
Class<?> getMostSpecificConversionTargetType(Class<?> requestedTargetType, Class<?> domainType) {
Class<?> conversionTargetType = requestedTargetType;
try {
Class<?> propertyType = PropertyPath.from(fieldName, domainType).getLeafProperty().getLeafType();
// use the more specific type but favor UserType over property one
if (ClassUtils.isAssignable(requestedTargetType, propertyType)) {
conversionTargetType = propertyType;
}
} catch (PropertyReferenceException e) {
// just don't care about it as we default to Object.class anyway.
}
return conversionTargetType;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal count} queries.
*
* @author Christoph Strobl
*/
class CountContext extends QueryContext {
/**
* Creates a new {@link CountContext} instance.
*
* @param query can be {@literal null}.
*/
CountContext(@Nullable Query query) {
super(query);
}
/**
* Get the {@link CountOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
CountOptions getCountOptions(@Nullable Class<?> domainType) {
return getCountOptions(domainType, null);
}
/**
* Get the {@link CountOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
CountOptions getCountOptions(@Nullable Class<?> domainType, @Nullable Consumer<CountOptions> callback) {
CountOptions options = new CountOptions();
Query query = getQuery();
applyCollation(domainType, options::collation);
if (query.getLimit() > 0) {
options.limit(query.getLimit());
}
if (query.getSkip() > 0) {
options.skip((int) query.getSkip());
}
if (StringUtils.hasText(query.getHint())) {
String hint = query.getHint();
if (BsonUtils.isJsonDocument(hint)) {
options.hint(BsonUtils.parse(hint, codecRegistryProvider));
} else {
options.hintString(hint);
}
}
if (callback != null) {
callback.accept(options);
}
return options;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal delete} queries.
*
* @author Christoph Strobl
*/
class DeleteContext extends QueryContext {
private final boolean multi;
/**
* Crate a new {@link DeleteContext} instance.
*
* @param query can be {@literal null}.
* @param multi use {@literal true} to remove all matching documents, {@literal false} for just the first one.
*/
DeleteContext(@Nullable Query query, boolean multi) {
super(query);
this.multi = multi;
}
/**
* Get the {@link DeleteOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
DeleteOptions getDeleteOptions(@Nullable Class<?> domainType) {
return getDeleteOptions(domainType, null);
}
/**
* Get the {@link DeleteOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
DeleteOptions getDeleteOptions(@Nullable Class<?> domainType, @Nullable Consumer<DeleteOptions> callback) {
DeleteOptions options = new DeleteOptions();
applyCollation(domainType, options::collation);
if (callback != null) {
callback.accept(options);
}
return options;
}
/**
* @return {@literal true} if all matching documents shall be deleted.
*/
boolean isMulti() {
return multi;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal updates}.
*/
class UpdateContext extends QueryContext {
private final boolean multi;
private final boolean upsert;
private final @Nullable UpdateDefinition update;
private final @Nullable MappedDocument mappedDocument;
/**
* Create a new {@link UpdateContext} instance.
*
* @param update must not be {@literal null}.
* @param query must not be {@literal null}.
* @param multi use {@literal true} to update all matching documents.
* @param upsert use {@literal true} to insert a new document if none match.
*/
UpdateContext(UpdateDefinition update, Document query, boolean multi, boolean upsert) {
this(update, new BasicQuery(query), multi, upsert);
}
/**
* Create a new {@link UpdateContext} instance.
*
* @param update must not be {@literal null}.
* @param query can be {@literal null}.
* @param multi use {@literal true} to update all matching documents.
* @param upsert use {@literal true} to insert a new document if none match.
*/
UpdateContext(UpdateDefinition update, @Nullable Query query, boolean multi, boolean upsert) {
super(query);
this.multi = multi;
this.upsert = upsert;
this.update = update;
this.mappedDocument = null;
}
UpdateContext(MappedDocument update, boolean upsert) {
super(new BasicQuery(new Document(BsonUtils.asMap(update.getIdFilter()))));
this.multi = false;
this.upsert = upsert;
this.mappedDocument = update;
this.update = null;
}
/**
* Get the {@link UpdateOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
UpdateOptions getUpdateOptions(@Nullable Class<?> domainType) {
return getUpdateOptions(domainType, null);
}
/**
* Get the {@link UpdateOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
UpdateOptions getUpdateOptions(@Nullable Class<?> domainType, @Nullable Consumer<UpdateOptions> callback) {
UpdateOptions options = new UpdateOptions();
options.upsert(upsert);
if (update != null && update.hasArrayFilters()) {
options
.arrayFilters(update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()));
}
applyCollation(domainType, options::collation);
if (callback != null) {
callback.accept(options);
}
return options;
}
/**
* Get the {@link ReplaceOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
ReplaceOptions getReplaceOptions(@Nullable Class<?> domainType) {
return getReplaceOptions(domainType, null);
}
/**
* Get the {@link ReplaceOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
ReplaceOptions getReplaceOptions(@Nullable Class<?> domainType, @Nullable Consumer<ReplaceOptions> callback) {
UpdateOptions updateOptions = getUpdateOptions(domainType);
ReplaceOptions options = new ReplaceOptions();
options.collation(updateOptions.getCollation());
options.upsert(updateOptions.isUpsert());
if (callback != null) {
callback.accept(options);
}
return options;
}
@Override
<T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> domainType) {
Document mappedQuery = super.getMappedQuery(domainType);
if (multi && update.isIsolated() && !mappedQuery.containsKey("$isolated")) {
mappedQuery.put("$isolated", 1);
}
return mappedQuery;
}
<T> Document applyShardKey(MongoPersistentEntity<T> domainType, Document filter, @Nullable Document existing) {
Document shardKeySource = existing != null ? existing
: mappedDocument != null ? mappedDocument.getDocument() : getMappedUpdate(domainType);
Document filterWithShardKey = new Document(filter);
getMappedShardKeyFields(domainType).forEach(key -> filterWithShardKey.putIfAbsent(key, shardKeySource.get(key)));
return filterWithShardKey;
}
boolean requiresShardKey(Document filter, @Nullable MongoPersistentEntity<?> domainType) {
return !multi && domainType != null && domainType.isSharded() && !shardedById(domainType)
&& !filter.keySet().containsAll(getMappedShardKeyFields(domainType));
}
/**
* @return {@literal true} if the {@link MongoPersistentEntity#getShardKey() shard key} is the entities
* {@literal id} property.
* @since 3.0
*/
private boolean shardedById(MongoPersistentEntity<?> domainType) {
ShardKey shardKey = domainType.getShardKey();
if (shardKey.size() != 1) {
return false;
}
String key = shardKey.getPropertyNames().iterator().next();
if ("_id".equals(key)) {
return true;
}
MongoPersistentProperty idProperty = domainType.getIdProperty();
return idProperty != null && idProperty.getName().equals(key);
}
Set<String> getMappedShardKeyFields(MongoPersistentEntity<?> entity) {
return getMappedShardKey(entity).keySet();
}
Document getMappedShardKey(MongoPersistentEntity<?> entity) {
return mappedShardKey.computeIfAbsent(entity.getType(),
key -> queryMapper.getMappedFields(entity.getShardKey().getDocument(), entity));
}
/**
* Get the already mapped aggregation pipeline to use with an {@link #isAggregationUpdate()}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
AggregationOperationContext context = domainType != null
? new RelaxedTypeBasedAggregationOperationContext(domainType, mappingContext, queryMapper)
: Aggregation.DEFAULT_CONTEXT;
return aggregationUtil.createPipeline((AggregationUpdate) update, context);
}
/**
* Get the already mapped update {@link Document}.
*
* @param entity
* @return
*/
Document getMappedUpdate(@Nullable MongoPersistentEntity<?> entity) {
if (update != null) {
return update instanceof MappedUpdate ? update.getUpdateObject()
: updateMapper.getMappedObject(update.getUpdateObject(), entity);
}
return mappedDocument.getDocument();
}
/**
* Increase a potential {@link MongoPersistentEntity#getVersionProperty() version property} prior to update if not
* already done in the actual {@link UpdateDefinition}
*
* @param persistentEntity can be {@literal null}.
*/
void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity<?> persistentEntity) {
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName();
if (!update.modifies(versionFieldName)) {
update.inc(versionFieldName);
}
}
}
/**
* @return {@literal true} if the update holds an aggregation pipeline.
*/
boolean isAggregationUpdate() {
return update instanceof AggregationUpdate;
}
/**
* @return {@literal true} if all matching documents should be updated.
*/
boolean isMulti() {
return multi;
}
}
}

View File

@@ -19,6 +19,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -38,13 +39,14 @@ import org.springframework.data.mongodb.core.query.Query;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(query(where("firstname").is("luke")))
* .matching(where("firstname").is("luke"))
* .all();
* </code>
* </pre>
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Juergen Zimmermann
* @since 2.0
*/
public interface ReactiveFindOperation {
@@ -144,6 +146,18 @@ public interface ReactiveFindOperation {
*/
TerminatingFind<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
* Set the filter query for the geoNear execution.
*
@@ -259,9 +273,21 @@ public interface ReactiveFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if resultType is {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -19,6 +19,7 @@ import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
/**
@@ -30,7 +31,7 @@ import org.springframework.data.mongodb.core.query.Query;
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
* collection name for the execution.
*
*
* <pre>
* <code>
* mapReduce(Human.class)
@@ -146,6 +147,18 @@ public interface ReactiveMapReduceOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingMapReduce<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -22,7 +22,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.MongoClientSettings;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;

View File

@@ -15,192 +15,17 @@
*/
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.List;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.util.Assert;
import com.mongodb.MongoCredential;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import com.mongodb.connection.StreamFactoryFactory;
import com.mongodb.MongoClientSettings;
/**
* A factory bean for construction of a {@link MongoClientSettings} instance to be used with the async MongoDB driver.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.0
* @deprecated since 3.0 - Use {@link MongoClientSettingsFactoryBean} instead.
*/
public class ReactiveMongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoClientSettings> {
@Deprecated
public class ReactiveMongoClientSettingsFactoryBean extends MongoClientSettingsFactoryBean {
private static final MongoClientSettings DEFAULT_MONGO_SETTINGS = MongoClientSettings.builder().build();
private ReadPreference readPreference = DEFAULT_MONGO_SETTINGS.getReadPreference();
private WriteConcern writeConcern = DEFAULT_MONGO_SETTINGS.getWriteConcern();
private ReadConcern readConcern = DEFAULT_MONGO_SETTINGS.getReadConcern();
private List<MongoCredential> credentialList = new ArrayList<>();
private StreamFactoryFactory streamFactoryFactory = DEFAULT_MONGO_SETTINGS.getStreamFactoryFactory();
private CodecRegistry codecRegistry = DEFAULT_MONGO_SETTINGS.getCodecRegistry();
private ClusterSettings clusterSettings = DEFAULT_MONGO_SETTINGS.getClusterSettings();
private SocketSettings socketSettings = DEFAULT_MONGO_SETTINGS.getSocketSettings();
private SocketSettings heartbeatSocketSettings = DEFAULT_MONGO_SETTINGS.getHeartbeatSocketSettings();
private ConnectionPoolSettings connectionPoolSettings = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings();
private ServerSettings serverSettings = DEFAULT_MONGO_SETTINGS.getServerSettings();
private SslSettings sslSettings = DEFAULT_MONGO_SETTINGS.getSslSettings();
/**
* Set the {@link ReadPreference}.
*
* @param readPreference
*/
public void setReadPreference(ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* Set the {@link WriteConcern}.
*
* @param writeConcern
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* Set the {@link ReadConcern}.
*
* @param readConcern
*/
public void setReadConcern(ReadConcern readConcern) {
this.readConcern = readConcern;
}
/**
* Set the List of {@link MongoCredential}s.
*
* @param credentialList must not be {@literal null}.
*/
public void setCredentialList(List<MongoCredential> credentialList) {
Assert.notNull(credentialList, "CredendialList must not be null!");
this.credentialList.addAll(credentialList);
}
/**
* Adds the {@link MongoCredential} to the list of credentials.
*
* @param mongoCredential must not be {@literal null}.
*/
public void addMongoCredential(MongoCredential mongoCredential) {
Assert.notNull(mongoCredential, "MongoCredential must not be null!");
this.credentialList.add(mongoCredential);
}
/**
* Set the {@link StreamFactoryFactory}.
*
* @param streamFactoryFactory
*/
public void setStreamFactoryFactory(StreamFactoryFactory streamFactoryFactory) {
this.streamFactoryFactory = streamFactoryFactory;
}
/**
* Set the {@link CodecRegistry}.
*
* @param codecRegistry
*/
public void setCodecRegistry(CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
/**
* Set the {@link ClusterSettings}.
*
* @param clusterSettings
*/
public void setClusterSettings(ClusterSettings clusterSettings) {
this.clusterSettings = clusterSettings;
}
/**
* Set the {@link SocketSettings}.
*
* @param socketSettings
*/
public void setSocketSettings(SocketSettings socketSettings) {
this.socketSettings = socketSettings;
}
/**
* Set the heartbeat {@link SocketSettings}.
*
* @param heartbeatSocketSettings
*/
public void setHeartbeatSocketSettings(SocketSettings heartbeatSocketSettings) {
this.heartbeatSocketSettings = heartbeatSocketSettings;
}
/**
* Set the {@link ConnectionPoolSettings}.
*
* @param connectionPoolSettings
*/
public void setConnectionPoolSettings(ConnectionPoolSettings connectionPoolSettings) {
this.connectionPoolSettings = connectionPoolSettings;
}
/**
* Set the {@link ServerSettings}.
*
* @param serverSettings
*/
public void setServerSettings(ServerSettings serverSettings) {
this.serverSettings = serverSettings;
}
/**
* Set the {@link SslSettings}.
*
* @param sslSettings
*/
public void setSslSettings(SslSettings sslSettings) {
this.sslSettings = sslSettings;
}
@Override
public Class<?> getObjectType() {
return MongoClientSettings.class;
}
@Override
protected MongoClientSettings createInstance() throws Exception {
return MongoClientSettings.builder() //
.readPreference(readPreference) //
.writeConcern(writeConcern) //
.readConcern(readConcern) //
.credentialList(credentialList) //
.streamFactoryFactory(streamFactoryFactory) //
.codecRegistry(codecRegistry) //
.clusterSettings(clusterSettings) //
.socketSettings(socketSettings) //
.heartbeatSocketSettings(heartbeatSocketSettings) //
.connectionPoolSettings(connectionPoolSettings) //
.serverSettings(serverSettings) //
.sslSettings(sslSettings) //
.build();
}
}

View File

@@ -66,6 +66,7 @@ import com.mongodb.reactivestreams.client.MongoCollection;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
* @see Flux
* @see Mono
@@ -298,7 +299,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* @param collectionName name of the collection.
* @return an existing collection or one created on first server interaction.
*/
MongoCollection<Document> getCollection(String collectionName);
Mono<MongoCollection<Document>> getCollection(String collectionName);
/**
* Check to see if a collection with a name indicated by the entity class exists.
@@ -1238,7 +1239,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* domain type information. Use {@link #updateFirst(Query, UpdateDefinition, Class, String)} to get full type specific
* support. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
* Use {@link #findAndModify(Query, UpdateDefinition, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
@@ -1571,7 +1572,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
/**
* Returns the underlying {@link MongoConverter}.
*
* @return
* @return never {@literal null}.
*/
MongoConverter getConverter();
@@ -1579,7 +1580,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* The collection name used for the specified class by this template.
*
* @param entityClass must not be {@literal null}.
* @return
* @return never {@literal null}.
* @since 2.1
*/
String getCollectionName(Class<?> entityClass);

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.client.result.DeleteResult;
@@ -106,6 +107,18 @@ public interface ReactiveRemoveOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingRemove<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
interface ReactiveRemove<T> extends RemoveWithCollection<T> {}

View File

@@ -15,12 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import com.mongodb.client.result.UpdateResult;
@@ -171,6 +172,18 @@ public interface ReactiveUpdateOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
UpdateWithUpdate<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -21,7 +21,6 @@ import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
import org.springframework.data.mongodb.core.script.NamedMongoScript;
import org.springframework.lang.Nullable;
import com.mongodb.DB;
/**
* Script operations on {@link com.mongodb.DB} level. Allows interaction with server side JavaScript functions.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2020 the original author or authors.
* Copyright 2018-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,66 +18,63 @@ package org.springframework.data.mongodb.core;
import org.springframework.beans.factory.DisposableBean;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.ConnectionString;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
/**
* Factory to create {@link MongoDatabase} instances from a {@link MongoClient} instance.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author George Moraitis
* @author Mark Paluch
* @deprecated since 2.2 in favor of {@link SimpleMongoClientDbFactory}.
* @since 3.0
*/
@Deprecated
public class SimpleMongoDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {
public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySupport<MongoClient>
implements DisposableBean {
/**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}.
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance for the given {@code connectionString}.
*
* @param uri coordinates for a database connection. Must contain a database name and must not be {@literal null}.
* @since 1.7
* @param connectionString connection coordinates for a database connection. Must contain a database name and must not
* be {@literal null} or empty.
* @see <a href="https://docs.mongodb.com/manual/reference/connection-string/">MongoDB Connection String reference</a>
*/
public SimpleMongoDbFactory(MongoClientURI uri) {
this(new MongoClient(uri), uri.getDatabase(), true);
public SimpleMongoClientDatabaseFactory(String connectionString) {
this(new ConnectionString(connectionString));
}
/**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}.
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
*
* @param connectionString connection coordinates for a database connection. Must contain also a database name and not
* be {@literal null}.
*/
public SimpleMongoClientDatabaseFactory(ConnectionString connectionString) {
this(MongoClients.create(connectionString), connectionString.getDatabase(), true);
}
/**
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @since 1.7
*/
public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) {
public SimpleMongoClientDatabaseFactory(MongoClient mongoClient, String databaseName) {
this(mongoClient, databaseName, false);
}
/**
* @param mongoClient
* @param databaseName
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoInstanceCreated
* @since 1.7
*/
private SimpleMongoDbFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
SimpleMongoClientDatabaseFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
super(mongoClient, databaseName, mongoInstanceCreated, new MongoExceptionTranslator());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
return getMongoClient().getDB(getDefaultDatabaseName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -87,7 +84,7 @@ public class SimpleMongoDbFactory extends MongoDbFactorySupport<MongoClient> imp
return getMongoClient().startSession(options);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#closeClient()
*/
@@ -96,7 +93,7 @@ public class SimpleMongoDbFactory extends MongoDbFactorySupport<MongoClient> imp
getMongoClient().close();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#doGetMongoDatabase(java.lang.String)
*/

View File

@@ -15,12 +15,7 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.beans.factory.DisposableBean;
import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
import com.mongodb.DB;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
@@ -30,8 +25,10 @@ import com.mongodb.client.MongoDatabase;
*
* @author Christoph Strobl
* @since 2.1
* @deprecated since 3.0, use {@link SimpleMongoClientDatabaseFactory} instead.
*/
public class SimpleMongoClientDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {
@Deprecated
public class SimpleMongoClientDbFactory extends SimpleMongoClientDatabaseFactory {
/**
* Creates a new {@link SimpleMongoClientDbFactory} instance for the given {@code connectionString}.
@@ -72,45 +69,6 @@ public class SimpleMongoClientDbFactory extends MongoDbFactorySupport<MongoClien
* @param mongoInstanceCreated
*/
private SimpleMongoClientDbFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
super(mongoClient, databaseName, mongoInstanceCreated, new MongoExceptionTranslator());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
throw new UnsupportedOperationException(String.format(
"%s does not support legacy DBObject API! Please consider using SimpleMongoDbFactory for that purpose.",
MongoClient.class));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return getMongoClient().startSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#closeClient()
*/
@Override
protected void closeClient() {
getMongoClient().close();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#doGetMongoDatabase(java.lang.String)
*/
@Override
protected MongoDatabase doGetMongoDatabase(String dbName) {
return getMongoClient().getDatabase(dbName);
super(mongoClient, databaseName, mongoInstanceCreated);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core;
import lombok.Value;
import reactor.core.publisher.Mono;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.dao.DataAccessException;
@@ -41,6 +42,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
*/
public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, ReactiveMongoDatabaseFactory {
@@ -99,7 +101,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getMongoDatabase()
*/
public MongoDatabase getMongoDatabase() throws DataAccessException {
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
return getMongoDatabase(databaseName);
}
@@ -107,12 +109,16 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getMongoDatabase(java.lang.String)
*/
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty.");
MongoDatabase db = mongo.getDatabase(dbName);
return writeConcern != null ? db.withWriteConcern(writeConcern) : db;
return Mono.fromSupplier(() -> {
MongoDatabase db = mongo.getDatabase(dbName);
return writeConcern != null ? db.withWriteConcern(writeConcern) : db;
});
}
/**
@@ -135,6 +141,15 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getCodecRegistry()
*/
@Override
public CodecRegistry getCodecRegistry() {
return this.mongo.getDatabase(databaseName).getCodecRegistry();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -171,8 +186,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase()
*/
@Override
public MongoDatabase getMongoDatabase() throws DataAccessException {
return decorateDatabase(delegate.getMongoDatabase());
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
return delegate.getMongoDatabase().map(this::decorateDatabase);
}
/*
@@ -180,8 +195,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase(java.lang.String)
*/
@Override
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
return decorateDatabase(delegate.getMongoDatabase(dbName));
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
return delegate.getMongoDatabase(dbName).map(this::decorateDatabase);
}
/*
@@ -193,6 +208,15 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getCodecRegistry()
*/
@Override
public CodecRegistry getCodecRegistry() {
return delegate.getCodecRegistry();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -242,7 +266,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy());
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.data.mongodb.core;
/**
* Enum to represent how strict the check of {@link com.mongodb.WriteResult} shall be. It can either be skipped entirely
* (use {@link #NONE}) or cause an exception to be thrown {@link #EXCEPTION}.
* Enum to represent how strict the check of {@link com.mongodb.WriteConcernResult} shall be. It can either be skipped
* entirely (use {@link #NONE}) or cause an exception to be thrown {@link #EXCEPTION}.
*
* @author Thomas Risberg
* @author Oliver Gierke

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -100,14 +101,14 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return value;
}
protected List<Object> append(Object value) {
protected List<Object> append(Object value, Expand expandList) {
if (this.value instanceof List) {
List<Object> clone = new ArrayList<Object>((List) this.value);
if (value instanceof List) {
clone.addAll((List) value);
if (value instanceof Collection && Expand.EXPAND_VALUES.equals(expandList)) {
clone.addAll((Collection<?>) value);
} else {
clone.add(value);
}
@@ -117,6 +118,17 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return Arrays.asList(this.value, value);
}
/**
* Expand a nested list of values to single entries or keep the list.
*/
protected enum Expand {
EXPAND_VALUES, KEEP_SOURCE
}
protected List<Object> append(Object value) {
return append(value, Expand.EXPAND_VALUES);
}
@SuppressWarnings("unchecked")
protected java.util.Map<String, Object> append(String key, Object value) {

View File

@@ -34,7 +34,7 @@ public class AccumulatorOperators {
* Take the numeric value referenced by given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link AccumulatorOperatorFactory}.
*/
public static AccumulatorOperatorFactory valueOf(String fieldReference) {
return new AccumulatorOperatorFactory(fieldReference);
@@ -44,7 +44,7 @@ public class AccumulatorOperators {
* Take the numeric value referenced resulting from given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link AccumulatorOperatorFactory}.
*/
public static AccumulatorOperatorFactory valueOf(AggregationExpression expression) {
return new AccumulatorOperatorFactory(expression);
@@ -86,7 +86,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates and
* returns the sum.
*
* @return
* @return new instance of {@link Sum}.
*/
public Sum sum() {
return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression);
@@ -96,7 +96,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
* average value.
*
* @return
* @return new instance of {@link Avg}.
*/
public Avg avg() {
return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression);
@@ -106,7 +106,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
* maximum value.
*
* @return
* @return new instance of {@link Max}.
*/
public Max max() {
return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression);
@@ -116,7 +116,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
* minimum value.
*
* @return
* @return new instance of {@link Min}.
*/
public Min min() {
return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression);
@@ -126,7 +126,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates the
* population standard deviation of the input values.
*
* @return
* @return new instance of {@link StdDevPop}.
*/
public StdDevPop stdDevPop() {
return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression);
@@ -136,7 +136,7 @@ public class AccumulatorOperators {
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and calculates the
* sample standard deviation of the input values.
*
* @return
* @return new instance of {@link StdDevSamp}.
*/
public StdDevSamp stdDevSamp() {
return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression);
@@ -167,7 +167,7 @@ public class AccumulatorOperators {
* Creates new {@link Sum}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Sum}.
*/
public static Sum sumOf(String fieldReference) {
@@ -179,7 +179,7 @@ public class AccumulatorOperators {
* Creates new {@link Sum}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Sum}.
*/
public static Sum sumOf(AggregationExpression expression) {
@@ -192,7 +192,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Sum}.
*/
public Sum and(String fieldReference) {
@@ -205,7 +205,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Sum}.
*/
public Sum and(AggregationExpression expression) {
@@ -264,7 +264,7 @@ public class AccumulatorOperators {
* Creates new {@link Avg}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Avg}.
*/
public static Avg avgOf(String fieldReference) {
@@ -276,7 +276,7 @@ public class AccumulatorOperators {
* Creates new {@link Avg}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Avg}.
*/
public static Avg avgOf(AggregationExpression expression) {
@@ -289,7 +289,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Avg}.
*/
public Avg and(String fieldReference) {
@@ -302,7 +302,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Avg}.
*/
public Avg and(AggregationExpression expression) {
@@ -347,7 +347,7 @@ public class AccumulatorOperators {
* Creates new {@link Max}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Max}.
*/
public static Max maxOf(String fieldReference) {
@@ -359,7 +359,7 @@ public class AccumulatorOperators {
* Creates new {@link Max}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Max}.
*/
public static Max maxOf(AggregationExpression expression) {
@@ -372,7 +372,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Max}.
*/
public Max and(String fieldReference) {
@@ -385,7 +385,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Max}.
*/
public Max and(AggregationExpression expression) {
@@ -430,7 +430,7 @@ public class AccumulatorOperators {
* Creates new {@link Min}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Min}.
*/
public static Min minOf(String fieldReference) {
@@ -442,7 +442,7 @@ public class AccumulatorOperators {
* Creates new {@link Min}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Min}.
*/
public static Min minOf(AggregationExpression expression) {
@@ -455,7 +455,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Min}.
*/
public Min and(String fieldReference) {
@@ -468,7 +468,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Min}.
*/
public Min and(AggregationExpression expression) {
@@ -513,7 +513,7 @@ public class AccumulatorOperators {
* Creates new {@link StdDevPop}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link StdDevPop}.
*/
public static StdDevPop stdDevPopOf(String fieldReference) {
@@ -525,7 +525,7 @@ public class AccumulatorOperators {
* Creates new {@link StdDevPop} with all previously added arguments appending the given one.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link StdDevPop}.
*/
public static StdDevPop stdDevPopOf(AggregationExpression expression) {
@@ -538,7 +538,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link StdDevPop}.
*/
public StdDevPop and(String fieldReference) {
@@ -551,7 +551,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link StdDevPop}.
*/
public StdDevPop and(AggregationExpression expression) {
@@ -596,7 +596,7 @@ public class AccumulatorOperators {
* Creates new {@link StdDevSamp}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link StdDevSamp}.
*/
public static StdDevSamp stdDevSampOf(String fieldReference) {
@@ -608,7 +608,7 @@ public class AccumulatorOperators {
* Creates new {@link StdDevSamp}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link StdDevSamp}.
*/
public static StdDevSamp stdDevSampOf(AggregationExpression expression) {
@@ -621,7 +621,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link StdDevSamp}.
*/
public StdDevSamp and(String fieldReference) {
@@ -634,7 +634,7 @@ public class AccumulatorOperators {
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link StdDevSamp}.
*/
public StdDevSamp and(AggregationExpression expression) {

View File

@@ -0,0 +1,204 @@
/*
* Copyright 2020 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.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder.ValueAppender;
import org.springframework.lang.Nullable;
/**
* Adds new fields to documents. {@code $addFields} outputs documents that contain all existing fields from the input
* documents and newly added fields.
*
* <pre class="code">
* AddFieldsOperation.addField("totalHomework").withValue("A+").and().addField("totalQuiz").withValue("B-")
* </pre>
*
* @author Christoph Strobl
* @since 3.0
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/">MongoDB Aggregation
* Framework: $addFields</a>
*/
public class AddFieldsOperation extends DocumentEnhancingOperation {
/**
* Create new instance of {@link AddFieldsOperation} adding map keys as exposed fields.
*
* @param source must not be {@literal null}.
*/
private AddFieldsOperation(Map<Object, Object> source) {
super(source);
}
/**
* Create new instance of {@link AddFieldsOperation}
*
* @param field must not be {@literal null}.
* @param value can be {@literal null}.
*/
public AddFieldsOperation(Object field, @Nullable Object value) {
this(Collections.singletonMap(field, value));
}
/**
* Define the {@link AddFieldsOperation} via {@link AddFieldsOperationBuilder}.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public static AddFieldsOperationBuilder builder() {
return new AddFieldsOperationBuilder();
}
/**
* Concatenate another field to add.
*
* @param field must not be {@literal null}.
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public static ValueAppender addField(String field) {
return new AddFieldsOperationBuilder().addField(field);
}
/**
* Append the value for a specific field to the operation.
*
* @param field the target field to add.
* @param value the value to assign.
* @return new instance of {@link AddFieldsOperation}.
*/
public AddFieldsOperation addField(Object field, Object value) {
LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap());
target.put(field, value);
return new AddFieldsOperation(target);
}
/**
* Concatenate additional fields to add.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public AddFieldsOperationBuilder and() {
return new AddFieldsOperationBuilder(getValueMap());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.DocumentEnhancingOperation#mongoOperator()
*/
@Override
protected String mongoOperator() {
return "$addFields";
}
/**
* @author Christoph Strobl
* @since 3.0
*/
public static class AddFieldsOperationBuilder {
private final Map<Object, Object> valueMap;
private AddFieldsOperationBuilder() {
this.valueMap = new LinkedHashMap<>();
}
private AddFieldsOperationBuilder(Map<Object, Object> source) {
this.valueMap = new LinkedHashMap<>(source);
}
public AddFieldsOperationBuilder addFieldWithValue(String field, @Nullable Object value) {
return addField(field).withValue(value);
}
public AddFieldsOperationBuilder addFieldWithValueOf(String field, Object value) {
return addField(field).withValueOf(value);
}
/**
* Define the field to add.
*
* @param field must not be {@literal null}.
* @return new instance of {@link ValueAppender}.
*/
public ValueAppender addField(String field) {
return new ValueAppender() {
@Override
public AddFieldsOperationBuilder withValue(Object value) {
valueMap.put(field, value);
return AddFieldsOperationBuilder.this;
}
@Override
public AddFieldsOperationBuilder withValueOf(Object value) {
valueMap.put(field, value instanceof String ? Fields.fields((String) value) : value);
return AddFieldsOperationBuilder.this;
}
@Override
public AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values) {
valueMap.put(field, new ExpressionProjection(operation, values));
return AddFieldsOperationBuilder.this;
}
};
}
public AddFieldsOperation build() {
return new AddFieldsOperation(valueMap);
}
/**
* @author Christoph Strobl
* @since 3.0
*/
public interface ValueAppender {
/**
* Define the value to assign as is.
*
* @param value can be {@literal null}.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValue(@Nullable Object value);
/**
* Define the value to assign. Plain {@link String} values are treated as {@link Field field references}.
*
* @param value must not be {@literal null}.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValueOf(Object value);
/**
* Adds a generic projection for the current field.
*
* @param operation the operation key, e.g. {@code $add}.
* @param values the values to be set for the projection operation.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values);
}
}
}

View File

@@ -23,9 +23,11 @@ import java.util.List;
import org.bson.Document;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.FacetOperation.FacetOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder;
import org.springframework.data.mongodb.core.aggregation.MergeOperation.MergeOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootOperationBuilder;
import org.springframework.data.mongodb.core.query.Criteria;
@@ -94,7 +96,7 @@ public class Aggregation {
public static final AggregationOperationContext DEFAULT_CONTEXT = AggregationOperationRenderer.DEFAULT_CONTEXT;
public static final AggregationOptions DEFAULT_OPTIONS = newAggregationOptions().build();
protected final List<AggregationOperation> operations;
protected final AggregationPipeline pipeline;
private final AggregationOptions options;
/**
@@ -117,7 +119,7 @@ public class Aggregation {
/**
* Creates a new {@link AggregationUpdate} from the given {@link AggregationOperation}s.
*
*
* @param operations can be {@literal empty} but must not be {@literal null}.
* @return new instance of {@link AggregationUpdate}.
* @since 3.0
@@ -131,13 +133,13 @@ public class Aggregation {
* supported in MongoDB version 2.6+.
*
* @param options must not be {@literal null}.
* @return
* @return new instance of {@link Aggregation}.
* @since 1.6
*/
public Aggregation withOptions(AggregationOptions options) {
Assert.notNull(options, "AggregationOptions must not be null.");
return new Aggregation(this.operations, options);
return new Aggregation(this.pipeline.getOperations(), options);
}
/**
@@ -200,21 +202,10 @@ public class Aggregation {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
Assert.notNull(options, "AggregationOptions must not be null!");
// check $out is the last operation if it exists
for (AggregationOperation aggregationOperation : aggregationOperations) {
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation, aggregationOperations)) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
}
this.operations = aggregationOperations;
this.pipeline = new AggregationPipeline(aggregationOperations);
this.options = options;
}
private boolean isLast(AggregationOperation aggregationOperation, List<AggregationOperation> aggregationOperations) {
return aggregationOperations.indexOf(aggregationOperation) == aggregationOperations.size() - 1;
}
/**
* Get the {@link AggregationOptions}.
*
@@ -234,11 +225,25 @@ public class Aggregation {
return "_id";
}
/**
* Obtain an {@link AddFieldsOperationBuilder builder} instance to create a new {@link AddFieldsOperation}.
* <p/>
* Starting in version 4.2, MongoDB adds a new aggregation pipeline stage {@link AggregationUpdate#set $set} that is
* an alias for {@code $addFields}.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
* @see AddFieldsOperation
* @since 3.0
*/
public static AddFieldsOperationBuilder addFields() {
return AddFieldsOperation.builder();
}
/**
* Creates a new {@link ProjectionOperation} including the given fields.
*
* @param fields must not be {@literal null}.
* @return
* @return new instance of {@link ProjectionOperation}.
*/
public static ProjectionOperation project(String... fields) {
return project(fields(fields));
@@ -248,7 +253,7 @@ public class Aggregation {
* Creates a new {@link ProjectionOperation} including the given {@link Fields}.
*
* @param fields must not be {@literal null}.
* @return
* @return new instance of {@link ProjectionOperation}.
*/
public static ProjectionOperation project(Fields fields) {
return new ProjectionOperation(fields);
@@ -271,7 +276,7 @@ public class Aggregation {
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
*
* @param field must not be {@literal null} or empty.
* @return
* @return new instance of {@link UnwindOperation}.
*/
public static UnwindOperation unwind(String field) {
return new UnwindOperation(field(field));
@@ -281,7 +286,7 @@ public class Aggregation {
* Factory method to create a new {@link ReplaceRootOperation} for the field with the given name.
*
* @param fieldName must not be {@literal null} or empty.
* @return
* @return new instance of {@link ReplaceRootOperation}.
* @since 1.10
*/
public static ReplaceRootOperation replaceRoot(String fieldName) {
@@ -293,7 +298,7 @@ public class Aggregation {
* {@link AggregationExpression}.
*
* @param aggregationExpression must not be {@literal null}.
* @return
* @return new instance of {@link ReplaceRootOperation}.
* @since 1.10
*/
public static ReplaceRootOperation replaceRoot(AggregationExpression aggregationExpression) {
@@ -359,7 +364,7 @@ public class Aggregation {
* Creates a new {@link GroupOperation} for the given fields.
*
* @param fields must not be {@literal null}.
* @return
* @return new instance of {@link GroupOperation}.
*/
public static GroupOperation group(String... fields) {
return group(fields(fields));
@@ -380,7 +385,7 @@ public class Aggregation {
* {@link GraphLookupOperation} given {@literal fromCollection}.
*
* @param fromCollection must not be {@literal null} or empty.
* @return
* @return new instance of {@link StartWithBuilder} for creating a {@link GraphLookupOperation}.
* @since 1.10
*/
public static StartWithBuilder graphLookup(String fromCollection) {
@@ -391,7 +396,7 @@ public class Aggregation {
* Factory method to create a new {@link SortOperation} for the given {@link Sort}.
*
* @param sort must not be {@literal null}.
* @return
* @return new instance of {@link SortOperation}.
*/
public static SortOperation sort(Sort sort) {
return new SortOperation(sort);
@@ -402,7 +407,7 @@ public class Aggregation {
*
* @param direction must not be {@literal null}.
* @param fields must not be {@literal null}.
* @return
* @return new instance of {@link SortOperation}.
*/
public static SortOperation sort(Direction direction, String... fields) {
return new SortOperation(Sort.by(direction, fields));
@@ -412,7 +417,7 @@ public class Aggregation {
* Creates a new {@link SortByCountOperation} given {@literal groupByField}.
*
* @param field must not be {@literal null} or empty.
* @return
* @return new instance of {@link SortByCountOperation}.
* @since 2.1
*/
public static SortByCountOperation sortByCount(String field) {
@@ -423,7 +428,7 @@ public class Aggregation {
* Creates a new {@link SortByCountOperation} given {@link AggregationExpression group and sort expression}.
*
* @param groupAndSortExpression must not be {@literal null}.
* @return
* @return new instance of {@link SortByCountOperation}.
* @since 2.1
*/
public static SortByCountOperation sortByCount(AggregationExpression groupAndSortExpression) {
@@ -434,9 +439,10 @@ public class Aggregation {
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param elementsToSkip must not be less than zero.
* @return
* @return new instance of {@link SkipOperation}.
* @deprecated prepare to get this one removed in favor of {@link #skip(long)}.
*/
@Deprecated
public static SkipOperation skip(int elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
@@ -445,7 +451,7 @@ public class Aggregation {
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param elementsToSkip must not be less than zero.
* @return
* @return new instance of {@link SkipOperation}.
*/
public static SkipOperation skip(long elementsToSkip) {
return new SkipOperation(elementsToSkip);
@@ -455,7 +461,7 @@ public class Aggregation {
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
*
* @param maxElements must not be less than zero.
* @return
* @return new instance of {@link LimitOperation}.
*/
public static LimitOperation limit(long maxElements) {
return new LimitOperation(maxElements);
@@ -465,7 +471,7 @@ public class Aggregation {
* Creates a new {@link SampleOperation} to select the specified number of documents from its input randomly.
*
* @param sampleSize must not be less than zero.
* @return
* @return new instance of {@link SampleOperation}.
* @since 2.0
*/
public static SampleOperation sample(long sampleSize) {
@@ -476,7 +482,7 @@ public class Aggregation {
* Creates a new {@link MatchOperation} using the given {@link Criteria}.
*
* @param criteria must not be {@literal null}.
* @return
* @return new instance of {@link MatchOperation}.
*/
public static MatchOperation match(Criteria criteria) {
return new MatchOperation(criteria);
@@ -486,13 +492,37 @@ public class Aggregation {
* Creates a new {@link MatchOperation} using the given {@link CriteriaDefinition}.
*
* @param criteria must not be {@literal null}.
* @return
* @return new instance of {@link MatchOperation}.
* @since 1.10
*/
public static MatchOperation match(CriteriaDefinition criteria) {
return new MatchOperation(criteria);
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null} or empty.
* @return new instance of {@link GeoNearOperation}.
* @since 1.7
*/
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
return new GeoNearOperation(query, distanceField);
}
/**
* Obtain a {@link MergeOperationBuilder builder} instance to create a new {@link MergeOperation}.
*
* @return new instance of {@link MergeOperationBuilder}.
* @see MergeOperation
* @since 3.0
*/
public static MergeOperationBuilder merge() {
return MergeOperation.builder();
}
/**
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation in
* the pipeline.
@@ -501,7 +531,7 @@ public class Aggregation {
* collection in the current database if one does not already exist. The collection is not visible until the
* aggregation completes. If the aggregation fails, MongoDB does not create the collection. Must not be
* {@literal null}.
* @return
* @return new instance of {@link OutOperation}.
*/
public static OutOperation out(String outCollectionName) {
return new OutOperation(outCollectionName);
@@ -511,7 +541,7 @@ public class Aggregation {
* Creates a new {@link BucketOperation} given {@literal groupByField}.
*
* @param groupByField must not be {@literal null} or empty.
* @return
* @return new instance of {@link BucketOperation}.
* @since 1.10
*/
public static BucketOperation bucket(String groupByField) {
@@ -522,7 +552,7 @@ public class Aggregation {
* Creates a new {@link BucketOperation} given {@link AggregationExpression group-by expression}.
*
* @param groupByExpression must not be {@literal null}.
* @return
* @return new instance of {@link BucketOperation}.
* @since 1.10
*/
public static BucketOperation bucket(AggregationExpression groupByExpression) {
@@ -534,7 +564,7 @@ public class Aggregation {
*
* @param groupByField must not be {@literal null} or empty.
* @param buckets number of buckets, must be a positive integer.
* @return
* @return new instance of {@link BucketAutoOperation}.
* @since 1.10
*/
public static BucketAutoOperation bucketAuto(String groupByField, int buckets) {
@@ -546,7 +576,7 @@ public class Aggregation {
*
* @param groupByExpression must not be {@literal null}.
* @param buckets number of buckets, must be a positive integer.
* @return
* @return new instance of {@link BucketAutoOperation}.
* @since 1.10
*/
public static BucketAutoOperation bucketAuto(AggregationExpression groupByExpression, int buckets) {
@@ -556,7 +586,7 @@ public class Aggregation {
/**
* Creates a new {@link FacetOperation}.
*
* @return
* @return new instance of {@link FacetOperation}.
* @since 1.10
*/
public static FacetOperation facet() {
@@ -567,7 +597,7 @@ public class Aggregation {
* Creates a new {@link FacetOperationBuilder} given {@link Aggregation}.
*
* @param aggregationOperations the sub-pipeline, must not be {@literal null}.
* @return
* @return new instance of {@link FacetOperation}.
* @since 1.10
*/
public static FacetOperationBuilder facet(AggregationOperation... aggregationOperations) {
@@ -612,11 +642,31 @@ public class Aggregation {
return new CountOperationBuilder();
}
/**
* Creates a new {@link RedactOperation} that can restrict the content of a document based on information stored
* within the document itself.
*
* <pre class="code">
*
* Aggregation.redact(ConditionalOperators.when(Criteria.where("level").is(5)) //
* .then(RedactOperation.PRUNE) //
* .otherwise(RedactOperation.DESCEND));
* </pre>
*
* @param condition Any {@link AggregationExpression} that resolves to {@literal $$DESCEND}, {@literal $$PRUNE}, or
* {@literal $$KEEP}. Must not be {@literal null}.
* @return new instance of {@link RedactOperation}. Never {@literal null}.
* @since 3.0
*/
public static RedactOperation redact(AggregationExpression condition) {
return new RedactOperation(condition);
}
/**
* Creates a new {@link Fields} instance for the given field names.
*
* @param fields must not be {@literal null}.
* @return
* @return new instance of {@link Fields}.
* @see Fields#fields(String...)
*/
public static Fields fields(String... fields) {
@@ -628,29 +678,16 @@ public class Aggregation {
*
* @param name must not be {@literal null} or empty.
* @param target must not be {@literal null} or empty.
* @return
* @return new instance of {@link Fields}.
*/
public static Fields bind(String name, String target) {
return Fields.from(field(name, target));
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null} or empty.
* @return
* @since 1.7
*/
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
return new GeoNearOperation(query, distanceField);
}
/**
* Returns a new {@link AggregationOptions.Builder}.
*
* @return
* @return new instance of {@link AggregationOptions.Builder}.
* @since 1.6
*/
public static AggregationOptions.Builder newAggregationOptions() {
@@ -665,7 +702,15 @@ public class Aggregation {
* @since 2.1
*/
public List<Document> toPipeline(AggregationOperationContext rootContext) {
return AggregationOperationRenderer.toDocument(operations, rootContext);
return pipeline.toDocuments(rootContext);
}
/**
* @return the {@link AggregationPipeline}.
* @since 3.0.2
*/
public AggregationPipeline getPipeline() {
return pipeline;
}
/**
@@ -712,7 +757,7 @@ public class Aggregation {
* otherwise.
*
* @param fieldRef may be {@literal null}.
* @return
* @return {@literal true} if the given field refers to a {@link SystemVariable}.
*/
public static boolean isReferingToSystemVariable(@Nullable String fieldRef) {

View File

@@ -31,8 +31,8 @@ public interface AggregationExpression {
* Turns the {@link AggregationExpression} into a {@link Document} within the given
* {@link AggregationOperationContext}.
*
* @param context
* @return
* @param context must not be {@literal null}.
* @return the MongoDB native ({@link Document}) form of the expression.
*/
Document toDocument(AggregationOperationContext context);
}

View File

@@ -53,7 +53,7 @@ interface AggregationExpressionTransformer
* @param currentNode must not be {@literal null}.
* @param parentNode may be {@literal null}.
* @param previousOperationObject may be {@literal null}.
* @param aggregationContext must not be {@literal null}.
* @param context must not be {@literal null}.
*/
public AggregationExpressionTransformationContext(T currentNode, @Nullable ExpressionNode parentNode,
@Nullable Document previousOperationObject, AggregationOperationContext context) {
@@ -67,7 +67,7 @@ interface AggregationExpressionTransformer
/**
* Returns the underlying {@link AggregationOperationContext}.
*
* @return
* @return never {@literal null}.
*/
public AggregationOperationContext getAggregationContext() {
return aggregationContext;
@@ -76,7 +76,7 @@ interface AggregationExpressionTransformer
/**
* Returns the {@link FieldReference} for the current {@link ExpressionNode}.
*
* @return
* @return never {@literal null}.
*/
public FieldReference getFieldReference() {
return aggregationContext.getReference(getCurrentNode().getName());

View File

@@ -41,7 +41,7 @@ public enum AggregationFunctionExpressions {
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.
*
* @param parameters must not be {@literal null}
* @return
* @return new instance of {@link AggregationExpression}.
*/
public AggregationExpression of(Object... parameters) {

View File

@@ -54,4 +54,15 @@ public interface AggregationOperation {
default List<Document> toPipelineStages(AggregationOperationContext context) {
return Collections.singletonList(toDocument(context));
}
/**
* Return the MongoDB operator that is used for this {@link AggregationOperation}. Aggregation operations should
* implement this method to avoid document rendering.
*
* @return the operator used for this {@link AggregationOperation}.
* @since 3.0.2
*/
default String getOperator() {
return toDocument(Aggregation.DEFAULT_CONTEXT).keySet().iterator().next();
}
}

View File

@@ -20,7 +20,6 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import org.bson.Document;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.lang.Nullable;
@@ -58,20 +57,20 @@ public interface AggregationOperationContext {
Document getMappedObject(Document document, @Nullable Class<?> type);
/**
* Returns a {@link FieldReference} for the given field or {@literal null} if the context does not expose the given
* field.
* Returns a {@link FieldReference} for the given field.
*
* @param field must not be {@literal null}.
* @return
* @return the {@link FieldReference} for the given {@link Field}.
* @throws IllegalArgumentException if the context does not expose a field with the given name
*/
FieldReference getReference(Field field);
/**
* Returns the {@link FieldReference} for the field with the given name or {@literal null} if the context does not
* expose a field with the given name.
* Returns the {@link FieldReference} for the field with the given name.
*
* @param name must not be {@literal null} or empty.
* @return
* @return the {@link FieldReference} for the field with given {@literal name}.
* @throws IllegalArgumentException if the context does not expose a field with the given name
*/
FieldReference getReference(String name);
@@ -103,4 +102,16 @@ public interface AggregationOperationContext {
.map(PropertyDescriptor::getName) //
.toArray(String[]::new));
}
/**
* 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.
*
* @return a more relaxed {@link AggregationOperationContext}.
* @since 3.0
*/
default AggregationOperationContext continueOnMissingFieldReference() {
return this;
}
}

View File

@@ -23,8 +23,6 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
* aggregation options can be found in the MongoDB reference documentation
@@ -54,6 +52,7 @@ public class AggregationOptions {
private final Optional<Collation> collation;
private final Optional<String> comment;
private Duration maxTime = Duration.ZERO;
private ResultOptions resultOptions = ResultOptions.READ;
/**
* Creates a new {@link AggregationOptions}.
@@ -72,7 +71,7 @@ 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
*/
@@ -87,7 +86,7 @@ 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
@@ -115,7 +114,7 @@ public class AggregationOptions {
}
/**
* Creates new {@link AggregationOptions} given {@link DBObject} containing aggregation options.
* Creates new {@link AggregationOptions} given {@link Document} containing aggregation options.
*
* @param document must not be {@literal null}.
* @return the {@link AggregationOptions}.
@@ -153,7 +152,7 @@ public class AggregationOptions {
* Enables writing to temporary files. When set to true, aggregation stages can write data to the _tmp subdirectory in
* the dbPath directory.
*
* @return
* @return {@literal true} if enabled.
*/
public boolean isAllowDiskUse() {
return allowDiskUse;
@@ -162,7 +161,7 @@ public class AggregationOptions {
/**
* Specifies to return the information on the processing of the pipeline.
*
* @return
* @return {@literal true} if enabled.
*/
public boolean isExplain() {
return explain;
@@ -187,7 +186,7 @@ public class AggregationOptions {
/**
* Specify a document that contains options that control the creation of the cursor object.
*
* @return
* @return never {@literal null}.
*/
public Optional<Document> getCursor() {
return cursor;
@@ -196,7 +195,7 @@ public class AggregationOptions {
/**
* Get collation settings for string comparison.
*
* @return
* @return never {@literal null}.
* @since 2.0
*/
public Optional<Collation> getCollation() {
@@ -206,7 +205,7 @@ public class AggregationOptions {
/**
* Get the comment for the aggregation.
*
* @return
* @return never {@literal null}.
* @since 2.2
*/
public Optional<String> getComment() {
@@ -221,6 +220,15 @@ public class AggregationOptions {
return maxTime;
}
/**
* @return {@literal true} to skip results when running an aggregation. Useful in combination with {@code $merge} or
* {@code $out}.
* @since 3.0.2
*/
public boolean isSkipResults() {
return ResultOptions.SKIP.equals(resultOptions);
}
/**
* Returns a new potentially adjusted copy for the given {@code aggregationCommandObject} with the configuration
* applied.
@@ -258,7 +266,7 @@ public class AggregationOptions {
/**
* Returns a {@link Document} representation of this {@link AggregationOptions}.
*
* @return
* @return never {@literal null}.
*/
public Document toDocument() {
@@ -278,7 +286,7 @@ public class AggregationOptions {
}
/**
* @return
* @return {@literal true} if {@link #maxTime} is set to a positive value.
* @since 3.0
*/
public boolean hasExecutionTimeLimit() {
@@ -311,12 +319,13 @@ public class AggregationOptions {
private @Nullable Collation collation;
private @Nullable String comment;
private @Nullable Duration maxTime;
private @Nullable ResultOptions resultOptions;
/**
* Defines whether to off-load intensive sort-operations to disk.
*
* @param allowDiskUse
* @return
* @param allowDiskUse use {@literal true} to allow disk use during the aggregation.
* @return this.
*/
public Builder allowDiskUse(boolean allowDiskUse) {
@@ -327,8 +336,8 @@ public class AggregationOptions {
/**
* Defines whether to get the execution plan for the aggregation instead of the actual results.
*
* @param explain
* @return
* @param explain use {@literal true} to enable explain feature.
* @return this.
*/
public Builder explain(boolean explain) {
@@ -339,8 +348,8 @@ public class AggregationOptions {
/**
* Additional options to the aggregation.
*
* @param cursor
* @return
* @param cursor must not be {@literal null}.
* @return this.
*/
public Builder cursor(Document cursor) {
@@ -351,8 +360,8 @@ public class AggregationOptions {
/**
* Define the initial cursor batch size.
*
* @param batchSize
* @return
* @param batchSize use a positive int.
* @return this.
* @since 2.0
*/
public Builder cursorBatchSize(int batchSize) {
@@ -365,7 +374,7 @@ public class AggregationOptions {
* Define collation settings for string comparison.
*
* @param collation can be {@literal null}.
* @return
* @return this.
* @since 2.0
*/
public Builder collation(@Nullable Collation collation) {
@@ -378,7 +387,7 @@ public class AggregationOptions {
* Define a comment to describe the execution.
*
* @param comment can be {@literal null}.
* @return
* @return this.
* @since 2.2
*/
public Builder comment(@Nullable String comment) {
@@ -391,7 +400,7 @@ public class AggregationOptions {
* 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
*/
@@ -401,10 +410,24 @@ public class AggregationOptions {
return this;
}
/**
* Run the aggregation, but do NOT read the aggregation result from the store. <br />
* If the expected result of the aggregation is rather large, eg. when using an {@literal $out} operation, this
* option allows to execute the aggregation without having the cursor return the operation result.
*
* @return this.
* @since 3.0.2
*/
public Builder skipOutput() {
this.resultOptions = ResultOptions.SKIP;
return this;
}
/**
* Returns a new {@link AggregationOptions} instance with the given configuration.
*
* @return
* @return new instance of {@link AggregationOptions}.
*/
public AggregationOptions build() {
@@ -412,8 +435,26 @@ public class AggregationOptions {
if (maxTime != null) {
options.maxTime = maxTime;
}
if (resultOptions != null) {
options.resultOptions = resultOptions;
}
return options;
}
}
/**
* @since 3.0
*/
private enum ResultOptions {
/**
* Just do it!, and do not read the operation result.
*/
SKIP,
/**
* Read the aggregation result from the cursor.
*/
READ;
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2020 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.Collections;
import java.util.List;
import org.bson.Document;
import org.springframework.util.Assert;
/**
* The {@link AggregationPipeline} holds the collection of {@link AggregationOperation aggregation stages}.
*
* @author Christoph Strobl
* @since 3.0.2
*/
public class AggregationPipeline {
private final List<AggregationOperation> pipeline;
/**
* Create an empty pipeline
*/
public AggregationPipeline() {
this(new ArrayList<>());
}
/**
* Create a new pipeline with given {@link AggregationOperation stages}.
*
* @param aggregationOperations must not be {@literal null}.
*/
public AggregationPipeline(List<AggregationOperation> aggregationOperations) {
pipeline = new ArrayList<>(aggregationOperations);
}
/**
* Append the given {@link AggregationOperation stage} to the pipeline.
*
* @param aggregationOperation must not be {@literal null}.
* @return this.
*/
public AggregationPipeline add(AggregationOperation aggregationOperation) {
Assert.notNull(aggregationOperation, "AggregationOperation must not be null!");
pipeline.add(aggregationOperation);
return this;
}
/**
* Get the list of {@link AggregationOperation aggregation stages}.
*
* @return never {@literal null}.
*/
public List<AggregationOperation> getOperations() {
return Collections.unmodifiableList(pipeline);
}
List<Document> toDocuments(AggregationOperationContext context) {
verify();
return AggregationOperationRenderer.toDocument(pipeline, context);
}
/**
* @return {@literal true} if the last aggregation stage is either {@literal $out} or {@literal $merge}.
*/
public boolean isOutOrMerge() {
if (pipeline.isEmpty()) {
return false;
}
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 aggregationOperation : pipeline) {
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation)) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
if (aggregationOperation instanceof MergeOperation && !isLast(aggregationOperation)) {
throw new IllegalArgumentException("The $merge operator must be the last stage in the pipeline.");
}
}
}
private boolean isLast(AggregationOperation aggregationOperation) {
return pipeline.indexOf(aggregationOperation) == pipeline.size() - 1;
}
}

View File

@@ -59,7 +59,7 @@ public class AggregationResults<T> implements Iterable<T> {
/**
* Returns the aggregation results.
*
* @return
* @return the list of already mapped results or an empty one if none found.
*/
public List<T> getMappedResults() {
return mappedResults;
@@ -68,7 +68,7 @@ public class AggregationResults<T> implements Iterable<T> {
/**
* Returns the unique mapped result. Assumes no result or exactly one.
*
* @return
* @return the single already mapped result object or raise an error if more than one found.
* @throws IllegalArgumentException in case more than one result is available.
*/
@Nullable
@@ -88,7 +88,7 @@ public class AggregationResults<T> implements Iterable<T> {
/**
* Returns the server that has been used to perform the aggregation.
*
* @return
* @return can be {@literal null}.
*/
@Nullable
public String getServerUsed() {
@@ -98,7 +98,7 @@ public class AggregationResults<T> implements Iterable<T> {
/**
* Returns the raw result that was returned by the server.
*
* @return
* @return the raw response.
* @since 1.6
*/
public Document getRawResults() {

View File

@@ -56,7 +56,7 @@ public class AggregationSpELExpression implements AggregationExpression {
*
* @param expressionString must not be {@literal null}.
* @param parameters can be empty.
* @return
* @return new instance of {@link AggregationSpELExpression}.
*/
public static AggregationSpELExpression expressionOf(String expressionString, Object... parameters) {

View File

@@ -79,7 +79,7 @@ import org.springframework.util.Assert;
public class AggregationUpdate extends Aggregation implements UpdateDefinition {
private boolean isolated = false;
private Set<String> keysTouched = new HashSet<>();
private final Set<String> keysTouched = new HashSet<>();
/**
* Create new {@link AggregationUpdate}.
@@ -139,7 +139,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition {
setOperation.getFields().forEach(it -> {
keysTouched.add(it.getName());
});
operations.add(setOperation);
pipeline.add(setOperation);
return this;
}
@@ -155,7 +155,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition {
Assert.notNull(unsetOperation, "UnsetOperation must not be null!");
operations.add(unsetOperation);
pipeline.add(unsetOperation);
keysTouched.addAll(unsetOperation.removedFieldNames());
return this;
}
@@ -164,7 +164,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition {
* {@code $replaceWith} replaces the input document with the specified document. The operation replaces all existing
* fields in the input document, including the <strong>_id</strong> field.
*
* @param replaceWithOperation
* @param replaceWithOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">$replaceWith Aggregation
* Reference</a>
@@ -172,7 +172,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition {
public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) {
Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null!");
operations.add(replaceWithOperation);
pipeline.add(replaceWithOperation);
return this;
}
@@ -219,8 +219,8 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition {
/**
* Short for {@link #unset(UnsetOperation)}.
*
* @param keys
* @return
* @param keys the fields to remove.
* @return this.
*/
public AggregationUpdate unset(String... keys) {

View File

@@ -32,9 +32,9 @@ interface AggregationUtils {
* Converts the given {@link Range} into an array of values.
*
* @param range must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
public static List<Long> toRangeValues(Range<Long> range) {
static List<Long> toRangeValues(Range<Long> range) {
Assert.notNull(range, "Range must not be null!");

View File

@@ -38,7 +38,7 @@ public class ArithmeticOperators {
* Take the field referenced by given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ArithmeticOperatorFactory}.
*/
public static ArithmeticOperatorFactory valueOf(String fieldReference) {
return new ArithmeticOperatorFactory(fieldReference);
@@ -48,7 +48,7 @@ public class ArithmeticOperators {
* Take the value resulting from the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ArithmeticOperatorFactory}.
*/
public static ArithmeticOperatorFactory valueOf(AggregationExpression expression) {
return new ArithmeticOperatorFactory(expression);
@@ -89,7 +89,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that returns the absolute value of the associated number.
*
* @return
* @return new instance of {@link Abs}.
*/
public Abs abs() {
return usesFieldRef() ? Abs.absoluteValueOf(fieldReference) : Abs.absoluteValueOf(expression);
@@ -100,7 +100,7 @@ public class ArithmeticOperators {
* number.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public Add add(String fieldReference) {
@@ -113,7 +113,7 @@ public class ArithmeticOperators {
* {@link AggregationExpression} to the associated number.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public Add add(AggregationExpression expression) {
@@ -125,7 +125,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that adds the given {@literal value} to the associated number.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public Add add(Number value) {
@@ -141,7 +141,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that returns the smallest integer greater than or equal to the
* assoicated number.
*
* @return
* @return new instance of {@link Ceil}.
*/
public Ceil ceil() {
return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression);
@@ -152,7 +152,7 @@ public class ArithmeticOperators {
* {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Divide}.
*/
public Divide divideBy(String fieldReference) {
@@ -165,7 +165,7 @@ public class ArithmeticOperators {
* {@literal expression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Divide}.
*/
public Divide divideBy(AggregationExpression expression) {
@@ -176,8 +176,8 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that divides the associated number by given {@literal value}.
*
* @param value
* @return
* @param value must not be {@literal null}.
* @return new instance of {@link Divide}.
*/
public Divide divideBy(Number value) {
@@ -192,7 +192,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that raises Eulers number (i.e. e ) on the associated number.
*
* @return
* @return new instance of {@link Exp}.
*/
public Exp exp() {
return usesFieldRef() ? Exp.expValueOf(fieldReference) : Exp.expValueOf(expression);
@@ -202,7 +202,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that returns the largest integer less than or equal to the associated
* number.
*
* @return
* @return new instance of {@link Floor}.
*/
public Floor floor() {
return usesFieldRef() ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression);
@@ -212,7 +212,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that calculates the natural logarithm ln (i.e loge) of the assoicated
* number.
*
* @return
* @return new instance of {@link Ln}.
*/
public Ln ln() {
return usesFieldRef() ? Ln.lnValueOf(fieldReference) : Ln.lnValueOf(expression);
@@ -223,7 +223,7 @@ public class ArithmeticOperators {
* referenced via {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Log}.
*/
public Log log(String fieldReference) {
@@ -236,7 +236,7 @@ public class ArithmeticOperators {
* extracted by given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Log}.
*/
public Log log(AggregationExpression expression) {
@@ -249,7 +249,7 @@ public class ArithmeticOperators {
* {@literal base}.
*
* @param base must not be {@literal null}.
* @return
* @return new instance of {@link Log}.
*/
public Log log(Number base) {
@@ -264,7 +264,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that calculates the log base 10 for the associated number.
*
* @return
* @return new instance of {@link Log10}.
*/
public Log10 log10() {
return usesFieldRef() ? Log10.log10ValueOf(fieldReference) : Log10.log10ValueOf(expression);
@@ -275,7 +275,7 @@ public class ArithmeticOperators {
* remainder.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public Mod mod(String fieldReference) {
@@ -288,7 +288,7 @@ public class ArithmeticOperators {
* remainder.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public Mod mod(AggregationExpression expression) {
@@ -301,7 +301,7 @@ public class ArithmeticOperators {
* remainder.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public Mod mod(Number value) {
@@ -317,7 +317,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that multiplies the associated number with another.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(String fieldReference) {
@@ -329,7 +329,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that multiplies the associated number with another.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(AggregationExpression expression) {
@@ -341,7 +341,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that multiplies the associated number with another.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(Number value) {
@@ -357,7 +357,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that raises the associated number to the specified exponent.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Pow}.
*/
public Pow pow(String fieldReference) {
@@ -369,7 +369,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that raises the associated number to the specified exponent.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Pow}.
*/
public Pow pow(AggregationExpression expression) {
@@ -381,7 +381,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that raises the associated number to the specified exponent.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Pow}.
*/
public Pow pow(Number value) {
@@ -396,7 +396,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that calculates the square root of the associated number.
*
* @return
* @return new instance of {@link Sqrt}.
*/
public Sqrt sqrt() {
return usesFieldRef() ? Sqrt.sqrtOf(fieldReference) : Sqrt.sqrtOf(expression);
@@ -406,7 +406,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that subtracts value of given from the associated number.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Subtract}.
*/
public Subtract subtract(String fieldReference) {
@@ -418,7 +418,7 @@ public class ArithmeticOperators {
* Creates new {@link AggregationExpression} that subtracts value of given from the associated number.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Subtract}.
*/
public Subtract subtract(AggregationExpression expression) {
@@ -429,8 +429,8 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that subtracts value from the associated number.
*
* @param value
* @return
* @param value must not be {@literal null}.
* @return new instance of {@link Subtract}.
*/
public Subtract subtract(Number value) {
@@ -445,7 +445,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that truncates a number to its integer.
*
* @return
* @return new instance of {@link Trunc}.
*/
public Trunc trunc() {
return usesFieldRef() ? Trunc.truncValueOf(fieldReference) : Trunc.truncValueOf(expression);
@@ -454,7 +454,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that calculates and returns the sum of numeric values.
*
* @return
* @return new instance of {@link Sum}.
*/
public Sum sum() {
return usesFieldRef() ? AccumulatorOperators.Sum.sumOf(fieldReference)
@@ -464,7 +464,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that returns the average value of the numeric values.
*
* @return
* @return new instance of {@link Avg}.
*/
public Avg avg() {
return usesFieldRef() ? AccumulatorOperators.Avg.avgOf(fieldReference)
@@ -474,7 +474,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that returns the maximum value.
*
* @return
* @return new instance of {@link Max}.
*/
public Max max() {
return usesFieldRef() ? AccumulatorOperators.Max.maxOf(fieldReference)
@@ -484,7 +484,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that returns the minimum value.
*
* @return
* @return new instance of {@link Min}.
*/
public Min min() {
return usesFieldRef() ? AccumulatorOperators.Min.minOf(fieldReference)
@@ -494,7 +494,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that calculates the population standard deviation of the input values.
*
* @return
* @return new instance of {@link StdDevPop}.
*/
public StdDevPop stdDevPop() {
return usesFieldRef() ? AccumulatorOperators.StdDevPop.stdDevPopOf(fieldReference)
@@ -504,7 +504,7 @@ public class ArithmeticOperators {
/**
* Creates new {@link AggregationExpression} that calculates the sample standard deviation of the input values.
*
* @return
* @return new instance of {@link StdDevSamp}.
*/
public StdDevSamp stdDevSamp() {
return usesFieldRef() ? AccumulatorOperators.StdDevSamp.stdDevSampOf(fieldReference)
@@ -557,7 +557,7 @@ public class ArithmeticOperators {
* Creates new {@link Abs}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Abs}.
*/
public static Abs absoluteValueOf(String fieldReference) {
@@ -569,7 +569,7 @@ public class ArithmeticOperators {
* Creates new {@link Abs}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Abs}.
*/
public static Abs absoluteValueOf(AggregationExpression expression) {
@@ -581,7 +581,7 @@ public class ArithmeticOperators {
* Creates new {@link Abs}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Abs}.
*/
public static Abs absoluteValueOf(Number value) {
@@ -610,7 +610,7 @@ public class ArithmeticOperators {
* Creates new {@link Add}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public static Add valueOf(String fieldReference) {
@@ -622,7 +622,7 @@ public class ArithmeticOperators {
* Creates new {@link Add}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public static Add valueOf(AggregationExpression expression) {
@@ -634,7 +634,7 @@ public class ArithmeticOperators {
* Creates new {@link Add}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Add}.
*/
public static Add valueOf(Number value) {
@@ -642,18 +642,36 @@ public class ArithmeticOperators {
return new Add(Collections.singletonList(value));
}
/**
* Add the value stored at the given field.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Add}.
*/
public Add add(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Add(append(Fields.field(fieldReference)));
}
/**
* Add the evaluation result of the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Add}.
*/
public Add add(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Add(append(expression));
}
/**
* Add the given value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Add}.
*/
public Add add(Number value) {
return new Add(append(value));
}
@@ -679,7 +697,7 @@ public class ArithmeticOperators {
* Creates new {@link Ceil}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Ceil}.
*/
public static Ceil ceilValueOf(String fieldReference) {
@@ -691,7 +709,7 @@ public class ArithmeticOperators {
* Creates new {@link Ceil}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Ceil}.
*/
public static Ceil ceilValueOf(AggregationExpression expression) {
@@ -703,7 +721,7 @@ public class ArithmeticOperators {
* Creates new {@link Ceil}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Ceil}.
*/
public static Ceil ceilValueOf(Number value) {
@@ -732,7 +750,7 @@ public class ArithmeticOperators {
* Creates new {@link Divide}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Divide}.
*/
public static Divide valueOf(String fieldReference) {
@@ -744,7 +762,7 @@ public class ArithmeticOperators {
* Creates new {@link Divide}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Divide}.
*/
public static Divide valueOf(AggregationExpression expression) {
@@ -756,7 +774,7 @@ public class ArithmeticOperators {
* Creates new {@link Divide}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Divide}.
*/
public static Divide valueOf(Number value) {
@@ -764,18 +782,36 @@ public class ArithmeticOperators {
return new Divide(Collections.singletonList(value));
}
/**
* Divide by the value stored at the given field.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Divide}.
*/
public Divide divideBy(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Divide(append(Fields.field(fieldReference)));
}
/**
* Divide by the evaluation results of the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Divide}.
*/
public Divide divideBy(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Divide(append(expression));
}
/**
* Divide by the given value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Divide}.
*/
public Divide divideBy(Number value) {
return new Divide(append(value));
}
@@ -801,7 +837,7 @@ public class ArithmeticOperators {
* Creates new {@link Exp}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Exp}.
*/
public static Exp expValueOf(String fieldReference) {
@@ -813,7 +849,7 @@ public class ArithmeticOperators {
* Creates new {@link Exp}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Exp}.
*/
public static Exp expValueOf(AggregationExpression expression) {
@@ -825,7 +861,7 @@ public class ArithmeticOperators {
* Creates new {@link Exp}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Exp}.
*/
public static Exp expValueOf(Number value) {
@@ -854,7 +890,7 @@ public class ArithmeticOperators {
* Creates new {@link Floor}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Floor}.
*/
public static Floor floorValueOf(String fieldReference) {
@@ -866,7 +902,7 @@ public class ArithmeticOperators {
* Creates new {@link Floor}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Floor}.
*/
public static Floor floorValueOf(AggregationExpression expression) {
@@ -878,7 +914,7 @@ public class ArithmeticOperators {
* Creates new {@link Floor}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Floor}.
*/
public static Floor floorValueOf(Number value) {
@@ -907,7 +943,7 @@ public class ArithmeticOperators {
* Creates new {@link Ln}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Ln}.
*/
public static Ln lnValueOf(String fieldReference) {
@@ -919,7 +955,7 @@ public class ArithmeticOperators {
* Creates new {@link Ln}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Ln}.
*/
public static Ln lnValueOf(AggregationExpression expression) {
@@ -931,7 +967,7 @@ public class ArithmeticOperators {
* Creates new {@link Ln}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Ln}.
*/
public static Ln lnValueOf(Number value) {
@@ -960,7 +996,7 @@ public class ArithmeticOperators {
* Creates new {@link Min}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Log}.
*/
public static Log valueOf(String fieldReference) {
@@ -972,7 +1008,7 @@ public class ArithmeticOperators {
* Creates new {@link Log}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Log}.
*/
public static Log valueOf(AggregationExpression expression) {
@@ -992,18 +1028,36 @@ public class ArithmeticOperators {
return new Log(Collections.singletonList(value));
}
/**
* Use the value stored at the given field as log base.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Log}.
*/
public Log log(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Log(append(Fields.field(fieldReference)));
}
/**
* Use the evaluated value of the given {@link AggregationExpression} as log base.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Log}.
*/
public Log log(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Log(append(expression));
}
/**
* Use the given value as log base.
*
* @param base must not be {@literal null}.
* @return new instance of {@link Log}.
*/
public Log log(Number base) {
return new Log(append(base));
}
@@ -1029,7 +1083,7 @@ public class ArithmeticOperators {
* Creates new {@link Log10}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Log10}.
*/
public static Log10 log10ValueOf(String fieldReference) {
@@ -1041,7 +1095,7 @@ public class ArithmeticOperators {
* Creates new {@link Log10}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Log10}.
*/
public static Log10 log10ValueOf(AggregationExpression expression) {
@@ -1053,7 +1107,7 @@ public class ArithmeticOperators {
* Creates new {@link Log10}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Log10}.
*/
public static Log10 log10ValueOf(Number value) {
@@ -1082,7 +1136,7 @@ public class ArithmeticOperators {
* Creates new {@link Mod}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public static Mod valueOf(String fieldReference) {
@@ -1094,7 +1148,7 @@ public class ArithmeticOperators {
* Creates new {@link Mod}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public static Mod valueOf(AggregationExpression expression) {
@@ -1106,7 +1160,7 @@ public class ArithmeticOperators {
* Creates new {@link Mod}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Mod}.
*/
public static Mod valueOf(Number value) {
@@ -1114,18 +1168,36 @@ public class ArithmeticOperators {
return new Mod(Collections.singletonList(value));
}
/**
* Use the value stored at the given field as mod base.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Mod}.
*/
public Mod mod(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Mod(append(Fields.field(fieldReference)));
}
/**
* Use evaluated value of the given {@link AggregationExpression} as mod base.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Mod}.
*/
public Mod mod(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Mod(append(expression));
}
/**
* Use the given value as mod base.
*
* @param base must not be {@literal null}.
* @return new instance of {@link Mod}.
*/
public Mod mod(Number base) {
return new Mod(append(base));
}
@@ -1151,7 +1223,7 @@ public class ArithmeticOperators {
* Creates new {@link Multiply}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public static Multiply valueOf(String fieldReference) {
@@ -1163,7 +1235,7 @@ public class ArithmeticOperators {
* Creates new {@link Multiply}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public static Multiply valueOf(AggregationExpression expression) {
@@ -1175,7 +1247,7 @@ public class ArithmeticOperators {
* Creates new {@link Multiply}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Multiply}.
*/
public static Multiply valueOf(Number value) {
@@ -1183,18 +1255,36 @@ public class ArithmeticOperators {
return new Multiply(Collections.singletonList(value));
}
/**
* Multiply by the value stored at the given field.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Multiply(append(Fields.field(fieldReference)));
}
/**
* Multiply by the evaluated value of the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Multiply(append(expression));
}
/**
* Multiply by the given value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Multiply}.
*/
public Multiply multiplyBy(Number value) {
return new Multiply(append(value));
}
@@ -1252,18 +1342,36 @@ public class ArithmeticOperators {
return new Pow(Collections.singletonList(value));
}
/**
* Pow by the value stored at the given field.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Pow pow(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Pow(append(Fields.field(fieldReference)));
}
/**
* Pow by the evaluated value of the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Pow pow(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Pow(append(expression));
}
/**
* Pow by the given value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Pow pow(Number value) {
return new Pow(append(value));
}
@@ -1289,7 +1397,7 @@ public class ArithmeticOperators {
* Creates new {@link Sqrt}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Sqrt}.
*/
public static Sqrt sqrtOf(String fieldReference) {
@@ -1301,7 +1409,7 @@ public class ArithmeticOperators {
* Creates new {@link Sqrt}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Sqrt}.
*/
public static Sqrt sqrtOf(AggregationExpression expression) {
@@ -1313,7 +1421,7 @@ public class ArithmeticOperators {
* Creates new {@link Sqrt}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Sqrt}.
*/
public static Sqrt sqrtOf(Number value) {
@@ -1342,7 +1450,7 @@ public class ArithmeticOperators {
* Creates new {@link Subtract}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Subtract}.
*/
public static Subtract valueOf(String fieldReference) {
@@ -1354,7 +1462,7 @@ public class ArithmeticOperators {
* Creates new {@link Subtract}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Subtract}.
*/
public static Subtract valueOf(AggregationExpression expression) {
@@ -1366,7 +1474,7 @@ public class ArithmeticOperators {
* Creates new {@link Subtract}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Subtract}.
*/
public static Subtract valueOf(Number value) {
@@ -1374,18 +1482,36 @@ public class ArithmeticOperators {
return new Subtract(Collections.singletonList(value));
}
/**
* Subtract the value stored at the given field.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Subtract subtract(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Subtract(append(Fields.field(fieldReference)));
}
/**
* Subtract the evaluated value of the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Subtract subtract(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Subtract(append(expression));
}
/**
* Subtract the given value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Pow}.
*/
public Subtract subtract(Number value) {
return new Subtract(append(value));
}
@@ -1411,7 +1537,7 @@ public class ArithmeticOperators {
* Creates new {@link Trunc}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Trunc}.
*/
public static Trunc truncValueOf(String fieldReference) {
@@ -1423,7 +1549,7 @@ public class ArithmeticOperators {
* Creates new {@link Trunc}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Trunc}.
*/
public static Trunc truncValueOf(AggregationExpression expression) {
@@ -1435,7 +1561,7 @@ public class ArithmeticOperators {
* Creates new {@link Trunc}.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Trunc}.
*/
public static Trunc truncValueOf(Number value) {
@@ -1502,7 +1628,7 @@ public class ArithmeticOperators {
/**
* The place to round to. Can be between -20 and 100, exclusive.
*
* @param place
* @param place value between -20 and 100, exclusive.
* @return new instance of {@link Round}.
*/
public Round place(int place) {

View File

@@ -43,7 +43,7 @@ public class ArrayOperators {
* Take the array referenced by given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ArrayOperatorFactory}.
*/
public static ArrayOperatorFactory arrayOf(String fieldReference) {
return new ArrayOperatorFactory(fieldReference);
@@ -53,7 +53,7 @@ public class ArrayOperators {
* Take the array referenced resulting from the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ArrayOperatorFactory}.
*/
public static ArrayOperatorFactory arrayOf(AggregationExpression expression) {
return new ArrayOperatorFactory(expression);
@@ -123,8 +123,8 @@ public class ArrayOperators {
* Creates new {@link AggregationExpression} that takes the associated array and returns the element at the
* specified array {@literal position}.
*
* @param position
* @return
* @param position the element index.
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(int position) {
return createArrayElemAt().elementAt(position);
@@ -135,7 +135,7 @@ public class ArrayOperators {
* resulting form the given {@literal expression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(AggregationExpression expression) {
@@ -148,7 +148,7 @@ public class ArrayOperators {
* defined by the referenced {@literal field}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(String fieldReference) {
@@ -170,7 +170,7 @@ public class ArrayOperators {
* {@literal arrayFieldReference} to it.
*
* @param arrayFieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ConcatArrays}.
*/
public ConcatArrays concat(String arrayFieldReference) {
@@ -183,7 +183,7 @@ public class ArrayOperators {
* the given {@literal expression} to it.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ConcatArrays}.
*/
public ConcatArrays concat(AggregationExpression expression) {
@@ -204,7 +204,7 @@ public class ArrayOperators {
* Creates new {@link AggregationExpression} that takes the associated array and selects a subset of the array to
* return based on the specified condition.
*
* @return
* @return new instance of {@link AsBuilder} to create a {@link Filter}.
*/
public AsBuilder filter() {
@@ -219,7 +219,7 @@ public class ArrayOperators {
/**
* Creates new {@link AggregationExpression} that takes the associated array and an check if its an array.
*
* @return
* @return new instance of {@link IsArray}.
*/
public IsArray isArray() {
@@ -231,7 +231,7 @@ public class ArrayOperators {
/**
* Creates new {@link AggregationExpression} that takes the associated array and retrieves its length.
*
* @return
* @return new instance of {@link Size}.
*/
public Size length() {
@@ -245,7 +245,7 @@ public class ArrayOperators {
/**
* Creates new {@link AggregationExpression} that takes the associated array and selects a subset from it.
*
* @return
* @return new instance of {@link Slice}.
*/
public Slice slice() {
@@ -261,7 +261,7 @@ public class ArrayOperators {
* value and returns the array index (zero-based) of the first occurrence.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link IndexOfArray}.
*/
public IndexOfArray indexOf(Object value) {
@@ -276,7 +276,7 @@ public class ArrayOperators {
/**
* Creates new {@link AggregationExpression} that returns an array with the elements in reverse order.
*
* @return
* @return new instance of {@link ReverseArray}.
*/
public ReverseArray reverse() {
@@ -293,7 +293,7 @@ public class ArrayOperators {
* an array and combines them into a single value.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}.
*/
public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(AggregationExpression expression) {
@@ -305,8 +305,8 @@ public class ArrayOperators {
* Start creating new {@link AggregationExpression} that applies an {@link AggregationExpression} to each element in
* an array and combines them into a single value.
*
* @param expressions
* @return
* @param expressions must not be {@literal null}.
* @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}.
*/
public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression... expressions) {
@@ -320,7 +320,7 @@ public class ArrayOperators {
* the second input array, etc.
*
* @param arrays must not be {@literal null}.
* @return
* @return new instance of {@link Zip}.
*/
public Zip zipWith(Object... arrays) {
@@ -336,7 +336,7 @@ public class ArrayOperators {
* associated array.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link In}.
*/
public In containsValue(Object value) {
@@ -413,7 +413,7 @@ public class ArrayOperators {
* Creates new {@link ArrayElemAt}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ArrayElemAt}.
*/
public static ArrayElemAt arrayOf(String fieldReference) {
@@ -425,7 +425,7 @@ public class ArrayOperators {
* Creates new {@link ArrayElemAt}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ArrayElemAt}.
*/
public static ArrayElemAt arrayOf(AggregationExpression expression) {
@@ -446,16 +446,34 @@ public class ArrayOperators {
return new ArrayElemAt(Collections.singletonList(values));
}
/**
* Use the element with given index number.
*
* @param index the index number
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(int index) {
return new ArrayElemAt(append(index));
}
/**
* Use the element at the index number evaluated from the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ArrayElemAt(append(expression));
}
/**
* Use the element at the index number traken from the given field.
*
* @param arrayFieldReference the field name.
* @return new instance of {@link ArrayElemAt}.
*/
public ArrayElemAt elementAt(String arrayFieldReference) {
Assert.notNull(arrayFieldReference, "ArrayReference must not be null!");
@@ -483,7 +501,7 @@ public class ArrayOperators {
* Creates new {@link ConcatArrays}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ConcatArrays}.
*/
public static ConcatArrays arrayOf(String fieldReference) {
@@ -495,7 +513,7 @@ public class ArrayOperators {
* Creates new {@link ConcatArrays}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ConcatArrays}.
*/
public static ConcatArrays arrayOf(AggregationExpression expression) {
@@ -516,12 +534,24 @@ public class ArrayOperators {
return new ConcatArrays(Collections.singletonList(values));
}
/**
* Concat with the array stored at the given field.
*
* @param arrayFieldReference must not be {@literal null}.
* @return new instance of {@link ConcatArrays}.
*/
public ConcatArrays concat(String arrayFieldReference) {
Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null!");
return new ConcatArrays(append(Fields.field(arrayFieldReference)));
}
/**
* Concat with the array resulting from the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ConcatArrays}.
*/
public ConcatArrays concat(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
@@ -574,7 +604,7 @@ public class ArrayOperators {
* Set the {@literal values} to apply the {@code $filter} to.
*
* @param values must not be {@literal null}.
* @return
* @return new instance of {@link AsBuilder} to create the {@link Filter}.
*/
public static AsBuilder filter(List<?> values) {
@@ -651,7 +681,7 @@ public class ArrayOperators {
* Set the {@literal variableName} for the elements in the input array.
*
* @param variableName must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
ConditionBuilder as(String variableName);
}
@@ -665,7 +695,7 @@ public class ArrayOperators {
* Set the {@link AggregationExpression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
Filter by(AggregationExpression expression);
@@ -673,7 +703,7 @@ public class ArrayOperators {
* Set the {@literal expression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
Filter by(String expression);
@@ -681,7 +711,7 @@ public class ArrayOperators {
* Set the {@literal expression} that determines whether to include the element in the resulting array.
*
* @param expression must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
Filter by(Document expression);
}
@@ -700,7 +730,7 @@ public class ArrayOperators {
/**
* Creates new {@link InputBuilder}.
*
* @return
* @return new instance of {@link FilterExpressionBuilder}.
*/
public static InputBuilder newBuilder() {
return new FilterExpressionBuilder();
@@ -800,7 +830,7 @@ public class ArrayOperators {
* Creates new {@link IsArray}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link IsArray}.
*/
public static IsArray isArray(String fieldReference) {
@@ -812,7 +842,7 @@ public class ArrayOperators {
* Creates new {@link IsArray}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link IsArray}.
*/
public static IsArray isArray(AggregationExpression expression) {
@@ -841,7 +871,7 @@ public class ArrayOperators {
* Creates new {@link Size}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Size}.
*/
public static Size lengthOfArray(String fieldReference) {
@@ -853,7 +883,7 @@ public class ArrayOperators {
* Creates new {@link Size}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Size}.
*/
public static Size lengthOfArray(AggregationExpression expression) {
@@ -895,7 +925,7 @@ public class ArrayOperators {
* Creates new {@link Slice}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Slice}.
*/
public static Slice sliceArrayOf(String fieldReference) {
@@ -907,7 +937,7 @@ public class ArrayOperators {
* Creates new {@link Slice}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Slice}.
*/
public static Slice sliceArrayOf(AggregationExpression expression) {
@@ -928,10 +958,22 @@ public class ArrayOperators {
return new Slice(Collections.singletonList(values));
}
/**
* Slice the number of elements.
*
* @param nrElements elements to slice.
* @return new instance of {@link Slice}.
*/
public Slice itemCount(int nrElements) {
return new Slice(append(nrElements));
}
/**
* Slice using offset and count.
*
* @param position the start position
* @return new instance of {@link SliceElementsBuilder} to create {@link Slice}.
*/
public SliceElementsBuilder offset(final int position) {
return new SliceElementsBuilder() {
@@ -978,7 +1020,7 @@ public class ArrayOperators {
* Start creating new {@link IndexOfArray}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link IndexOfArray}.
*/
public static IndexOfArrayBuilder arrayOf(String fieldReference) {
@@ -990,7 +1032,7 @@ public class ArrayOperators {
* Start creating new {@link IndexOfArray}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link IndexOfArray}.
*/
public static IndexOfArrayBuilder arrayOf(AggregationExpression expression) {
@@ -1002,7 +1044,7 @@ public class ArrayOperators {
* Start creating new {@link IndexOfArray}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link IndexOfArray}.
* @return new instance of {@link IndexOfArrayBuilder} to create {@link IndexOfArray}.
* @since 2.2
*/
public static IndexOfArrayBuilder arrayOf(Collection<?> values) {
@@ -1011,6 +1053,12 @@ public class ArrayOperators {
return new IndexOfArrayBuilder(values);
}
/**
* Lookup within a given range.
*
* @param range the lookup range.
* @return new instance of {@link IndexOfArray}.
*/
public IndexOfArray within(Range<Long> range) {
return new IndexOfArray(append(AggregationUtils.toRangeValues(range)));
}
@@ -1030,7 +1078,7 @@ public class ArrayOperators {
* Set the {@literal value} to check for its index in the array.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link IndexOfArray}.
*/
public IndexOfArray indexOf(Object value) {
@@ -1060,7 +1108,7 @@ public class ArrayOperators {
* Start creating new {@link RangeOperator}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link RangeOperatorBuilder} to create {@link RangeOperator}.
*/
public static RangeOperatorBuilder rangeStartingAt(String fieldReference) {
return new RangeOperatorBuilder(Fields.field(fieldReference));
@@ -1070,7 +1118,7 @@ public class ArrayOperators {
* Start creating new {@link RangeOperator}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link RangeOperatorBuilder} to create {@link RangeOperator}.
*/
public static RangeOperatorBuilder rangeStartingAt(AggregationExpression expression) {
return new RangeOperatorBuilder(expression);
@@ -1080,7 +1128,7 @@ public class ArrayOperators {
* Start creating new {@link RangeOperator}.
*
* @param value
* @return
* @return new instance of {@link RangeOperator}.
*/
public static RangeOperatorBuilder rangeStartingAt(long value) {
return new RangeOperatorBuilder(value);
@@ -1102,7 +1150,7 @@ public class ArrayOperators {
* Creates new {@link RangeOperator}.
*
* @param index
* @return
* @return new instance of {@link RangeOperator}.
*/
public RangeOperator to(long index) {
return new RangeOperator(Arrays.asList(startPoint, index));
@@ -1112,7 +1160,7 @@ public class ArrayOperators {
* Creates new {@link RangeOperator}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link RangeOperator}.
*/
public RangeOperator to(AggregationExpression expression) {
return new RangeOperator(Arrays.asList(startPoint, expression));
@@ -1122,7 +1170,7 @@ public class ArrayOperators {
* Creates new {@link RangeOperator}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link RangeOperator}.
*/
public RangeOperator to(String fieldReference) {
return new RangeOperator(Arrays.asList(startPoint, Fields.field(fieldReference)));
@@ -1150,7 +1198,7 @@ public class ArrayOperators {
* Creates new {@link ReverseArray} given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ReverseArray}.
*/
public static ReverseArray reverseArrayOf(String fieldReference) {
return new ReverseArray(Fields.field(fieldReference));
@@ -1160,7 +1208,7 @@ public class ArrayOperators {
* Creates new {@link ReverseArray} given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ReverseArray}.
*/
public static ReverseArray reverseArrayOf(AggregationExpression expression) {
return new ReverseArray(expression);
@@ -1239,7 +1287,7 @@ public class ArrayOperators {
* Start creating new {@link Reduce}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link InitialValueBuilder} to create {@link Reduce}.
*/
public static InitialValueBuilder arrayOf(final String fieldReference) {
@@ -1267,7 +1315,7 @@ public class ArrayOperators {
Assert.notNull(expressions, "PropertyExpressions must not be null");
return new Reduce(Fields.field(fieldReference), initialValue,
Arrays.<AggregationExpression> asList(expressions));
Arrays.<AggregationExpression>asList(expressions));
}
};
}
@@ -1278,7 +1326,7 @@ public class ArrayOperators {
* Start creating new {@link Reduce}.
*
* @param arrayValueExpression must not be {@literal null}.
* @return
* @return new instance of {@link InitialValueBuilder} to create {@link Reduce}.
*/
public static InitialValueBuilder arrayOf(final AggregationExpression arrayValueExpression) {
@@ -1318,7 +1366,7 @@ public class ArrayOperators {
* Define the initial cumulative value set before in is applied to the first element of the input array.
*
* @param initialValue must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
ReduceBuilder withInitialValue(Object initialValue);
}
@@ -1335,7 +1383,7 @@ public class ArrayOperators {
* {@link Variable#VALUE} are available.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Reduce}.
*/
Reduce reduce(AggregationExpression expression);
@@ -1346,7 +1394,7 @@ public class ArrayOperators {
* {@link Variable#VALUE} are available.
*
* @param expressions must not be {@literal null}.
* @return
* @return new instance of {@link Reduce}.
*/
Reduce reduce(PropertyExpression... expressions);
}
@@ -1372,7 +1420,7 @@ public class ArrayOperators {
* Define a result property for an {@link AggregationExpression} used in {@link Reduce}.
*
* @param name must not be {@literal null}.
* @return
* @return new instance of {@link AsBuilder} to create {@link Reduce}.
*/
public static AsBuilder property(final String name) {
@@ -1402,7 +1450,7 @@ public class ArrayOperators {
* Set the {@link AggregationExpression} resulting in the properties value.
*
* @param expression must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
PropertyExpression definedAs(AggregationExpression expression);
}
@@ -1459,7 +1507,7 @@ public class ArrayOperators {
* eg. {@code $$value.product}
*
* @param property must not be {@literal null}.
* @return
* @return never {@literal null}.
*/
public Field referringTo(final String property) {
@@ -1508,7 +1556,7 @@ public class ArrayOperators {
* Start creating new {@link Zip}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link ZipBuilder} to create {@link Zip}.
*/
public static ZipBuilder arrayOf(String fieldReference) {
@@ -1520,7 +1568,7 @@ public class ArrayOperators {
* Start creating new {@link Zip}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link ZipBuilder} to create {@link Zip}.
*/
public static ZipBuilder arrayOf(AggregationExpression expression) {
@@ -1544,7 +1592,7 @@ public class ArrayOperators {
/**
* Create new {@link Zip} and set the {@code useLongestLength} property to {@literal true}.
*
* @return
* @return new instance of {@link Zip}.
*/
public Zip useLongestLength() {
return new Zip(append("useLongestLength", true));
@@ -1554,7 +1602,7 @@ public class ArrayOperators {
* Optionally provide a default value.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Zip}.
*/
public Zip defaultTo(String fieldReference) {
@@ -1566,7 +1614,7 @@ public class ArrayOperators {
* Optionally provide a default value.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Zip}.
*/
public Zip defaultTo(AggregationExpression expression) {
@@ -1578,7 +1626,7 @@ public class ArrayOperators {
* Optionally provide a default value.
*
* @param array must not be {@literal null}.
* @return
* @return new instance of {@link Zip}.
*/
public Zip defaultTo(Object[] array) {
@@ -1602,7 +1650,7 @@ public class ArrayOperators {
* array, etc.
*
* @param arrays arrays to zip the referenced one with. must not be {@literal null}.
* @return
* @return new instance of {@link Zip}.
*/
public Zip zip(Object... arrays) {
@@ -1616,7 +1664,7 @@ public class ArrayOperators {
}
}
return new Zip(Collections.<String, Object> singletonMap("inputs", sourceArrays));
return new Zip(Collections.<String, Object>singletonMap("inputs", sourceArrays));
}
}
}
@@ -1627,7 +1675,7 @@ public class ArrayOperators {
* @author Christoph Strobl
* @author Shashank Sharma
* @see <a href=
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
* @since 2.2
*/
public static class In extends AbstractAggregationExpression {
@@ -1645,7 +1693,7 @@ public class ArrayOperators {
* Start creating {@link In}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link InBuilder} to create {@link In}.
*/
public static InBuilder arrayOf(String fieldReference) {
@@ -1662,7 +1710,7 @@ public class ArrayOperators {
* Start creating {@link In}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link InBuilder} to create {@link In}.
*/
public static InBuilder arrayOf(AggregationExpression expression) {
@@ -1704,7 +1752,7 @@ public class ArrayOperators {
* Set the {@literal value} to check for existence in the array.
*
* @param value must not be {@literal value}.
* @return
* @return new instance of {@link In}.
*/
In containsValue(Object value);
}
@@ -1716,7 +1764,7 @@ public class ArrayOperators {
*
* @author Christoph Strobl
* @see <a href=
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
* @since 2.1
*/
public static class ArrayToObject extends AbstractAggregationExpression {

View File

@@ -34,7 +34,7 @@ public class BooleanOperators {
* Take the array referenced by given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link BooleanOperatorFactory}.
*/
public static BooleanOperatorFactory valueOf(String fieldReference) {
return new BooleanOperatorFactory(fieldReference);
@@ -44,7 +44,7 @@ public class BooleanOperators {
* Take the value resulting of the given {@link AggregationExpression}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link BooleanOperatorFactory}.
*/
public static BooleanOperatorFactory valueOf(AggregationExpression fieldReference) {
return new BooleanOperatorFactory(fieldReference);
@@ -55,7 +55,7 @@ public class BooleanOperators {
* opposite boolean value.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Not}.
*/
public static Not not(String fieldReference) {
return Not.not(fieldReference);
@@ -66,7 +66,7 @@ public class BooleanOperators {
* and returns the opposite boolean value.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Not}.
*/
public static Not not(AggregationExpression expression) {
return Not.not(expression);
@@ -109,7 +109,7 @@ public class BooleanOperators {
* all of the expressions are {@literal true}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link And}.
*/
public And and(AggregationExpression expression) {
@@ -122,7 +122,7 @@ public class BooleanOperators {
* all of the expressions are {@literal true}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link And}.
*/
public And and(String fieldReference) {
@@ -139,7 +139,7 @@ public class BooleanOperators {
* any of the expressions are {@literal true}.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public Or or(AggregationExpression expression) {
@@ -152,7 +152,7 @@ public class BooleanOperators {
* any of the expressions are {@literal true}.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public Or or(String fieldReference) {
@@ -167,7 +167,7 @@ public class BooleanOperators {
/**
* Creates new {@link AggregationExpression} that evaluates a boolean and returns the opposite boolean value.
*
* @return
* @return new instance of {@link Not}.
*/
public Not not() {
return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression);
@@ -198,8 +198,8 @@ public class BooleanOperators {
* Creates new {@link And} that evaluates one or more expressions and returns {@literal true} if all of the
* expressions are {@literal true}.
*
* @param expressions
* @return
* @param expressions must not be {@literal null}.
* @return new instance of {@link And}.
*/
public static And and(Object... expressions) {
return new And(Arrays.asList(expressions));
@@ -209,7 +209,7 @@ public class BooleanOperators {
* Creates new {@link And} with all previously added arguments appending the given one.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link And}.
*/
public And andExpression(AggregationExpression expression) {
@@ -221,7 +221,7 @@ public class BooleanOperators {
* Creates new {@link And} with all previously added arguments appending the given one.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link And}.
*/
public And andField(String fieldReference) {
@@ -233,7 +233,7 @@ public class BooleanOperators {
* Creates new {@link And} with all previously added arguments appending the given one.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link And}.
*/
public And andValue(Object value) {
@@ -263,7 +263,7 @@ public class BooleanOperators {
* expressions are {@literal true}.
*
* @param expressions must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public static Or or(Object... expressions) {
@@ -275,7 +275,7 @@ public class BooleanOperators {
* Creates new {@link Or} with all previously added arguments appending the given one.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public Or orExpression(AggregationExpression expression) {
@@ -287,7 +287,7 @@ public class BooleanOperators {
* Creates new {@link Or} with all previously added arguments appending the given one.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public Or orField(String fieldReference) {
@@ -299,7 +299,7 @@ public class BooleanOperators {
* Creates new {@link Or} with all previously added arguments appending the given one.
*
* @param value must not be {@literal null}.
* @return
* @return new instance of {@link Or}.
*/
public Or orValue(Object value) {
@@ -329,7 +329,7 @@ public class BooleanOperators {
* value.
*
* @param fieldReference must not be {@literal null}.
* @return
* @return new instance of {@link Not}.
*/
public static Not not(String fieldReference) {
@@ -342,7 +342,7 @@ public class BooleanOperators {
* returns the opposite boolean value.
*
* @param expression must not be {@literal null}.
* @return
* @return new instance of {@link Not}.
*/
public static Not not(AggregationExpression expression) {

View File

@@ -15,12 +15,11 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.BucketAutoOperationOutputBuilder;
import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder;
import org.springframework.util.Assert;
import org.bson.Document;
/**
* Encapsulates the aggregation framework {@code $bucketAuto}-operation. <br />
* Bucket stage is typically used with {@link Aggregation} and {@code $facet}. Categorizes incoming documents into a
@@ -106,14 +105,23 @@ public class BucketAutoOperation extends BucketOperationSupport<BucketAutoOperat
options.putAll(super.toDocument(context));
return new Document("$bucketAuto", options);
return new Document(getOperator(), options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/
@Override
public String getOperator() {
return "$bucketAuto";
}
/**
* Configures a number of bucket {@literal buckets} and return a new {@link BucketAutoOperation}.
*
* @param buckets must be a positive number.
* @return
* @return new instance of {@link BucketAutoOperation}.
*/
public BucketAutoOperation withBuckets(int buckets) {
@@ -128,7 +136,7 @@ public class BucketAutoOperation extends BucketOperationSupport<BucketAutoOperat
* Use either predefined {@link Granularities} or provide a own one.
*
* @param granularity must not be {@literal null}.
* @return
* @return new instance of {@link BucketAutoOperation}.
*/
public BucketAutoOperation withGranularity(Granularity granularity) {
@@ -209,7 +217,7 @@ public class BucketAutoOperation extends BucketOperationSupport<BucketAutoOperat
*
* @param expression must not be {@literal null}.
* @param operation must not be {@literal null}.
* @param parameters
* @param parameters must not be {@literal null}.
*/
protected ExpressionBucketAutoOperationBuilder(String expression, BucketAutoOperation operation,
Object[] parameters) {

View File

@@ -20,21 +20,19 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.BucketOperation.BucketOperationOutputBuilder;
import org.springframework.util.Assert;
import org.bson.Document;
/**
* Encapsulates the aggregation framework {@code $bucket}-operation. <br />
*
* Bucket stage is typically used with {@link Aggregation} and {@code $facet}. Categorizes incoming documents into
* groups, called buckets, based on a specified expression and bucket boundaries. <br />
*
* We recommend to use the static factory method {@link Aggregation#bucket(String)} instead of creating instances of
* this class directly.
*
* @see <a href="https://docs.mongodb.org/manual/reference/aggregation/bucket/">https://docs.mongodb.org/manual/reference/aggregation/bucket/</a>
* @see <a href=
* "https://docs.mongodb.org/manual/reference/aggregation/bucket/">https://docs.mongodb.org/manual/reference/aggregation/bucket/</a>
* @see BucketOperationSupport
* @author Mark Paluch
* @since 1.10
@@ -103,14 +101,23 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc
options.putAll(super.toDocument(context));
return new Document("$bucket", options);
return new Document(getOperator(), options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#getOperator()
*/
@Override
public String getOperator() {
return "$bucket";
}
/**
* Configures a default bucket {@literal literal} and return a new {@link BucketOperation}.
*
* @param literal must not be {@literal null}.
* @return
* @return new instance of {@link BucketOperation}.
*/
public BucketOperation withDefaultBucket(Object literal) {
@@ -123,7 +130,7 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc
* preserved and the new {@literal boundaries} are appended.
*
* @param boundaries must not be {@literal null}.
* @return
* @return new instance of {@link BucketOperation}.
*/
public BucketOperation withBoundaries(Object... boundaries) {
@@ -204,12 +211,12 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc
extends ExpressionBucketOperationBuilderSupport<BucketOperationOutputBuilder, BucketOperation> {
/**
* Creates a new {@link ExpressionBucketOperationBuilderSupport} for the given value, {@link BucketOperation}
* and parameters.
* Creates a new {@link ExpressionBucketOperationBuilderSupport} for the given value, {@link BucketOperation} and
* parameters.
*
* @param expression must not be {@literal null}.
* @param operation must not be {@literal null}.
* @param parameters
* @param parameters must not be {@literal null}.
*/
protected ExpressionBucketOperationBuilder(String expression, BucketOperation operation, Object[] parameters) {
super(expression, operation, parameters);

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