Compare commits

..

239 Commits

Author SHA1 Message Date
Mark Paluch
383b7031dd DATAMONGO-2452 - Release version 2.1.16 (Lovelace SR16). 2020-02-26 11:11:48 +01:00
Mark Paluch
e8f0ed8d59 DATAMONGO-2452 - Prepare 2.1.16 (Lovelace SR16). 2020-02-26 11:11:28 +01:00
Mark Paluch
f90d30472b DATAMONGO-2452 - Updated changelog. 2020-02-26 11:11:23 +01:00
Mark Paluch
cec1c8305a DATAMONGO-2456 - Updated changelog. 2020-02-12 15:05:03 +01:00
Mark Paluch
cd714b1ba8 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:52:56 +01:00
Mark Paluch
0a83eec8b2 DATAMONGO-2464 - Polishing.
Apply fix also to reactive MongoDB repository documentation.

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

Original pull request: #816.
2020-02-03 11:34:09 +01:00
Mark Paluch
6e65d2fd1d DATAMONGO-2457 - Polishing.
Slightly tweak wording.

Original pull request: #829.
2020-01-29 09:51:31 +01:00
Christoph Strobl
8d63c10e07 DATAMONGO-2459 - Add sample for passing on limit and offset using reactive repositories.
Original pull request: #829.
2020-01-29 09:47:31 +01:00
Christoph Strobl
aee5f71229 DATAMONGO-2457 - Fix id type explanation in repository documentation.
Original pull request: #829.
2020-01-29 09:47:30 +01:00
Mark Paluch
5cc044b72c DATAMONGO-2454 - Updated changelog. 2020-01-17 09:58:35 +01:00
Mark Paluch
45243468ec DATAMONGO-2383 - Updated changelog. 2020-01-16 16:12:38 +01:00
Mark Paluch
5a162bdbaf DATAMONGO-2432 - Updated changelog. 2020-01-15 12:51:15 +01:00
Mark Paluch
1d15eaf42e DATAMONGO-2431 - After release cleanups. 2020-01-15 10:35:39 +01:00
Mark Paluch
0076e294ae DATAMONGO-2431 - Prepare next development iteration. 2020-01-15 10:35:38 +01:00
Mark Paluch
cd8603caa3 DATAMONGO-2431 - Release version 2.1.15 (Lovelace SR15). 2020-01-15 10:25:22 +01:00
Mark Paluch
aac1c3406b DATAMONGO-2431 - Prepare 2.1.15 (Lovelace SR15). 2020-01-15 10:24:59 +01:00
Mark Paluch
3a16cc80c5 DATAMONGO-2431 - Updated changelog. 2020-01-15 10:24:53 +01:00
Christoph Strobl
c2ba361d24 DATAMONGO-2442 - Fix thenValueOf in $cond builder.
We now create a field reference when calling the builder instead of using the value as is.

Original pull request: #818.
2020-01-08 15:14:06 +01:00
Christoph Strobl
eb5a331822 DATAMONGO-2423 - Polishing.
Update nullability annotations and reduce visibility in tests.

Original pull request: #815.
2020-01-08 13:25:32 +01:00
Christoph Strobl
3100e0db01 DATAMONGO-2423 - Nullability refinements for Update.
Ease non null restrictions for operators that may use null values like $set.

Original pull request: #815.
2020-01-08 13:25:31 +01:00
Mark Paluch
4efaed4741 DATAMONGO-2444 - Update copyright years to 2020. 2020-01-07 08:59:42 +01:00
Jens Schauder
00bbf439c0 DATAMONGO-2422 - Updated changelog. 2019-12-04 14:32:47 +01:00
Jens Schauder
87476f9a91 DATAMONGO-2421 - After release cleanups. 2019-12-04 12:03:52 +01:00
Jens Schauder
7bcf686b40 DATAMONGO-2421 - Prepare next development iteration. 2019-12-04 12:03:50 +01:00
Jens Schauder
8e51d005d5 DATAMONGO-2421 - Release version 2.1.14 (Lovelace SR14). 2019-12-04 11:42:03 +01:00
Jens Schauder
1940a5c2c2 DATAMONGO-2421 - Prepare 2.1.14 (Lovelace SR14). 2019-12-04 11:40:56 +01:00
Jens Schauder
5a274029d7 DATAMONGO-2421 - Updated changelog. 2019-12-04 11:40:47 +01:00
Mark Paluch
c342bf266e DATAMONGO-2410 - Polishing.
Simplify cast. Extend test with DBObject interface.

Original pull request: #813.
2019-12-04 08:58:36 +01:00
Christoph Strobl
11baf455d2 DATAMONGO-2410 - Fix Document to BasicDBObject conversion.
Original pull request: #813.
2019-12-04 08:58:09 +01:00
Mark Paluch
05882813ac DATAMONGO-2402 - Updated changelog. 2019-11-18 12:43:27 +01:00
Mark Paluch
bd3f26c928 DATAMONGO-2401 - After release cleanups. 2019-11-18 12:10:53 +01:00
Mark Paluch
5555aa970b DATAMONGO-2401 - Prepare next development iteration. 2019-11-18 12:10:52 +01:00
Mark Paluch
e74fe05abd DATAMONGO-2401 - Release version 2.1.13 (Lovelace SR13). 2019-11-18 12:01:05 +01:00
Mark Paluch
d579254fbc DATAMONGO-2401 - Prepare 2.1.13 (Lovelace SR13). 2019-11-18 12:00:43 +01:00
Mark Paluch
71c8e4cc02 DATAMONGO-2401 - Updated changelog. 2019-11-18 12:00:36 +01:00
Mark Paluch
a087c7d17c DATAMONGO-2409 - Polishing.
Adapt also ExecutableFindOperation.DistinctWithProjection.asType() to return the appropriate TerminatingDistinct.

Original pull request: #805.
2019-11-11 10:22:43 +01:00
Christoph Strobl
90cec275a6 DATAMONGO-2409 - Fix return type of Kotlin extension function for ReactiveFindOperation.DistinctWithProjection.asType().
Original pull request: #805.
2019-11-11 10:22:43 +01:00
Christoph Strobl
e4eefe577d DATAMONGO-2382 - Updated changelog. 2019-11-04 15:39:58 +01:00
Christoph Strobl
b57a6612f6 DATAMONGO-2381 - After release cleanups. 2019-11-04 10:26:37 +01:00
Christoph Strobl
62b2d54e0d DATAMONGO-2381 - Prepare next development iteration. 2019-11-04 10:26:35 +01:00
Christoph Strobl
aff823da57 DATAMONGO-2381 - Release version 2.1.12 (Lovelace SR12). 2019-11-04 09:29:50 +01:00
Christoph Strobl
d45b630724 DATAMONGO-2381 - Prepare 2.1.12 (Lovelace SR12). 2019-11-04 09:29:05 +01:00
Christoph Strobl
fc8c97aeb0 DATAMONGO-2381 - Updated changelog. 2019-11-04 09:29:00 +01:00
Mark Paluch
004e7f01b2 DATAMONGO-2388 - Polishing.
Use StringJoiner to create comma-delimited String.

Original pull request: #797.
2019-10-28 10:58:01 +01:00
Christoph Strobl
5c80ee0087 DATAMONGO-2388 - Fix CodecConfigurationException when reading index info that contains DbRef.
Provide the default CodecRegistry when converting partial index data to its String representation used in IndexInfo.

Original pull request: #797.
2019-10-28 10:48:55 +01:00
Mark Paluch
adb9dc29a2 DATAMONGO-2334 - Updated changelog. 2019-09-30 19:58:07 +02:00
Mark Paluch
6eb6feadbb DATAMONGO-2333 - After release cleanups. 2019-09-30 11:15:26 +02:00
Mark Paluch
166aab39c4 DATAMONGO-2333 - Prepare next development iteration. 2019-09-30 11:15:25 +02:00
Mark Paluch
fa94c22c2a DATAMONGO-2333 - Release version 2.1.11 (Lovelace SR11). 2019-09-30 11:04:23 +02:00
Mark Paluch
e0f88a8b84 DATAMONGO-2333 - Prepare 2.1.11 (Lovelace SR11). 2019-09-30 11:04:00 +02:00
Mark Paluch
59aa8051d3 DATAMONGO-2333 - Updated changelog. 2019-09-30 11:03:56 +02:00
Mark Paluch
205a06e79a DATAMONGO-2377 - Polishing.
Reformat code.

Original pull request: #792.
2019-09-25 13:36:26 +02:00
Christoph Strobl
899b43a29b DATAMONGO-2377 - Fix handling of $$value and $$this in field exposing aggregation.
Internal field references to $$this and $$value are now no longer mapped against exposed fields which had caused errors before.

Original pull request: #792.
2019-09-25 13:36:26 +02:00
Christoph Strobl
0f0a4ed31b DATAMONGO-1731 - Improve update & upsert documentation. 2019-09-23 13:36:03 +02:00
Mark Paluch
9acc8d5268 DATAMONGO-2360 - Polishing.
Apply index hints in ReactiveMongoTemplate.count(…).

Original pull request: #788.
2019-09-19 14:55:06 +02:00
Christoph Strobl
313ffb5426 DATAMONGO-2360 - Apply query hint to count queries.
Original pull request: #788.
2019-09-19 14:41:50 +02:00
Mark Paluch
dc859953f4 DATAMONGO-2374 - Polishing.
Reformat code.

Original pull request: #791.
2019-09-19 14:17:30 +02:00
Christoph Strobl
bc29f2b24e DATAMONGO-2374 - Fix simple type result handling for repository query methods.
Original pull request: #791.
2019-09-19 14:17:09 +02:00
Christoph Strobl
686cdac73f DATAMONGO-2366 - Polishing.
Fix typo in reference documentation and add note on error handling.

Original Pull Request: #790
2019-09-19 11:47:21 +02:00
Mark Paluch
b7b339577b DATAMONGO-2366 - Consistently handle exceptions in CursorReadingTask.
Exceptions during CursorReadingTask startup and during polling are handled now by the same exception handling to handle Exceptions only once and notify ErrorHandler exactly once per exception.

Previously, startup exceptions relied on exception handling in the execute closure and notified ErrorHandler potentially multiple times.

Original Pull Request: #790
2019-09-19 11:34:58 +02:00
Franz van Betteraey
2166a6e953 DATAMONGO-2361 - Fix @Document reference documentation.
Original pull request: #787.
2019-09-13 10:53:15 +02:00
Mark Paluch
3c601a699a DATAMONGO-2335 - Updated changelog. 2019-09-06 10:22:55 +02:00
Christoph Strobl
37211fc6d7 DATAMONGO-2310 - Update documentation for TypedAggregation. 2019-09-05 13:02:30 +02:00
Christoph Strobl
a45c9040c4 DATAMONGO-2348 - Update documentation of version property handling. 2019-09-05 10:47:24 +02:00
Christoph Strobl
23c0a07b93 DATAMONGO-2354 - Polishing.
Same as with FindPublisherPreparer the CursorPreparer needs to be public because it is used in one of the protected methods of MongoTemplate.

Original Pull Request: #784
2019-09-04 13:30:05 +02:00
kostya05983
f3a7d6a20e DATAMONGO-2354 - Change visibility of FindPublisherPreparer.
The FindPublisherPreparer is used in an protected method of ReactiveMongoTemplate and needs to be public to allow overriding.

Original Pull Request: #784
2019-09-04 13:17:33 +02:00
Mark Paluch
0d22d831f8 DATAMONGO-2352 - Polishing.
Apply typo fixes also to ReactiveMongoOperations.

Original pull request: #782.
2019-09-03 11:28:37 +02:00
Ryan Cloherty
6b0e2ab5de DATAMONGO-2352 - Fix documentation typos.
Original pull request: #782.
2019-09-03 11:28:37 +02:00
Mark Paluch
5d02b84856 DATAMONGO-2349 - Polishing.
Reformat code. Remove duplicate simple types.

Original pull request: #783.
2019-09-03 08:58:10 +02:00
Christoph Strobl
93e911985e DATAMONGO-2349 - Fix converter registration for java.time types.
The MongoDB Java Driver does not handle java.time types. Therefore those must not be considered simple types.
The behavior was changed by DATACMNS-1294 forcing usage of Reading & WritingConverter annotations to disambiguate converter direction.
This commit restores the converter registration to the state before the change in Spring Data Commons.

Original pull request: #783.
2019-09-03 08:58:05 +02:00
Christoph Strobl
e7faa1a1ec DATAMONGO-2351 - Fix test setup for indices with collations.
This avoids errors in text execution switching back from a newer version that may leave incompatible data (collation option) in one of the collections.

Original Pull Request: #781
2019-08-23 10:37:58 +02:00
Christoph Strobl
631714941a DATAMONGO-2351 - Polishing.
Fix broken tests and favor StepVerifier over block() for reactive ones.

Original Pull Request: #781
2019-08-23 07:44:44 +02:00
Artyom Gabeev
db9428cebe DATAMONGO-2351 - Return zero deleted count for unacknowledged deleteBy.
Original Pull Request: #781
2019-08-23 07:41:16 +02:00
Christoph Strobl
4be53ac952 DATAMONGO-2339 - Fix QueryMapper field name resolution for properties containing underscore.
We now prevent splitting of paths that contain underscores if the entity contains a property that matches.

Original pull request: #777.
2019-08-13 10:42:05 +02:00
Mark Paluch
564acd75d5 DATAMONGO-2338 - Open RepositoryFactoryBeans for extension.
createRepositoryFactory() is now no longer final allowing for overriding the method. This change aligns with the remanining store modules.
2019-08-06 15:57:49 +02:00
Mark Paluch
95ccdf4c20 DATAMONGO-2337 - Add HTTPS entries into spring.schemas.
To resolve XSD files properly from the classpath, their HTTPS reference must be present in the spring.schemas to avoid internet interaction for resolving an XSD file.
2019-08-06 15:57:47 +02:00
Greg Turnquist
291ef4bb75 DATAMONGO-2280 - Force check for updates. 2019-08-05 11:07:40 -05:00
Mark Paluch
c7461928f4 DATAMONGO-2303 - Updated changelog. 2019-08-05 15:57:31 +02:00
Mark Paluch
f5a5d3e96b DATAMONGO-2302 - After release cleanups. 2019-08-05 11:33:32 +02:00
Mark Paluch
b213aada80 DATAMONGO-2302 - Prepare next development iteration. 2019-08-05 11:33:31 +02:00
Mark Paluch
403e5043cb DATAMONGO-2302 - Release version 2.1.10 (Lovelace SR10). 2019-08-05 11:21:19 +02:00
Mark Paluch
bdbda459c0 DATAMONGO-2302 - Prepare 2.1.10 (Lovelace SR10). 2019-08-05 11:20:57 +02:00
Mark Paluch
0bf6d5f7fa DATAMONGO-2302 - Updated changelog. 2019-08-05 11:20:51 +02:00
Mark Paluch
f2ae14206a DATAMONGO-2272 - Updated changelog. 2019-08-05 11:09:02 +02:00
Mark Paluch
049159374d DATAMONGO-2320 - Polishing.
Use for-loops instead of Stream API and Collectors. Reformat code. Invert condition for smoother readability.

Original pull request: #776.
2019-08-02 11:06:38 +02:00
Christoph Strobl
79f8e06fc1 DATAMONGO-2320 - Simplify test code.
Original pull request: #776.
2019-08-02 11:06:38 +02:00
Christoph Strobl
370db2dce5 DATAMONGO-2320 - Fix aggregation field reference for $filter operator.
We now render field and local variable references correctly when using the $filter aggregation operator.
Prior to this commit field references had been rendered with an additional $ prefix.

Original pull request: #776.
2019-08-02 11:06:38 +02:00
Christoph Strobl
74325d5193 DATAMONGO-2330 - Polishing.
Added tests.

Original Pull Request: #775
2019-07-31 14:47:59 +02:00
nkey
e6ea2e1379 DATAMONGO-2330 - Apply defaultWriteConcern for bulk operations.
Fixed regression introduced in DATAMONGO-1880.

Original Pull Request: #775
2019-07-31 14:27:24 +02:00
Mark Paluch
cb85f3cfa6 DATAMONGO-2322 - Polishing.
Suspend change streams tests relying heavily on timing.
2019-07-18 10:09:19 +02:00
Mark Paluch
aff8b89006 DATAMONGO-2322 - Handle exceptions thrown by MessageListeners.
ErrorHandlers associated with a CursorReadingTask (Change Streams, imperative Tailable Cursors) now handle exceptions raised by the listener callback.

Exceptions are now catched and the callback continues with the next message.
2019-07-18 09:57:38 +02:00
Mark Paluch
0ad8857368 DATAMONGO-2323 - Polishing.
Use .as(StepVerifier::create) syntax where possible.
2019-07-17 14:40:49 +02:00
Mark Paluch
46de82fe0b DATAMONGO-2323 - Fix query(…) and stream(…) to be used with BSON Document.
MongoTemplate.stream(…), MongoTemplate.query(…) and ReactiveMongoTemplate.query(…) now no longer fail when used with BSON Document.class.

Previously, field mapping failed because it required an entity type. Now we gracefully back off when find methods are used with a simple Document entity type.
2019-07-17 14:35:56 +02:00
Mark Paluch
387348b615 DATAMONGO-2318 - Fix typo. 2019-07-10 09:46:15 +02:00
Mark Paluch
8fd41faac6 DATAMONGO-2280 - Cleanup release profile.
Reuse inherited configuration from parent pom.
2019-07-09 11:03:43 +02:00
Mark Paluch
8a15e1086b DATAMONGO-2318 - Revise readme for a consistent structure. 2019-07-09 11:03:42 +02:00
Mark Paluch
8502786648 DATAMONGO-2314 - Polishing.
Reformat code. Remove unnecessary warning suppressions. Switch to diamond syntax.

Original pull request: #771.
2019-07-04 16:27:41 +02:00
Christoph Strobl
d7107d49bf DATAMONGO-2314 - Fix query by example on nested properties.
This fix allows using alike on nested properties.

new Criteria("nested").alike(Example.of(probe, matching().withIgnorePaths("_class"))));

Switch tests to AssertJ.

Original pull request: #771.
2019-07-04 16:27:15 +02:00
Greg Turnquist
f42cb1e2f0 DATAMONGO-2280 - Use parent 'artifactory' profile for snapshot releases. 2019-07-03 17:12:27 -05:00
Mark Paluch
a9403b526f DATAMONGO-2296 - Polishing.
Use getCollectionName() in MongoTemplate.insert/save. Consistently use getCollectionName(Class) from ReactiveMongoTemplate and fluent API implementations.

Original pull request: #768.
2019-07-01 16:48:26 +02:00
Christoph Strobl
5f6291ed32 DATAMONGO-2296 - Consistent use of getCollectionName(Class) throughout MongoTemplate.
Original pull request: #768.
2019-07-01 16:44:33 +02:00
Greg Turnquist
676ee80434 DATAMONGO-2280 - Only test main branch for upstream triggers. 2019-06-28 19:31:04 -05:00
Greg Turnquist
b54641ff86 DATAMONGO-2280 - Set user.name and user.home for CI jobs. 2019-06-25 13:35:49 -05:00
Oliver Drotbohm
6930c720ca DATAMONGO-2309 - Fix NoHTTP errors. 2019-06-24 11:44:50 +02:00
Christoph Strobl
611cfe9c11 DATAMONGO-2256 - Updated changelog. 2019-06-14 15:18:14 +02:00
Christoph Strobl
507a1fbf34 DATAMONGO-2271 - After release cleanups. 2019-06-14 13:14:51 +02:00
Christoph Strobl
087649de35 DATAMONGO-2271 - Prepare next development iteration. 2019-06-14 13:14:50 +02:00
Christoph Strobl
1f01f34377 DATAMONGO-2271 - Release version 2.1.9 (Lovelace SR9). 2019-06-14 12:39:34 +02:00
Christoph Strobl
295c43c6ff DATAMONGO-2271 - Prepare 2.1.9 (Lovelace SR9). 2019-06-14 12:38:59 +02:00
Christoph Strobl
5a62d449bf DATAMONGO-2271 - Updated changelog. 2019-06-14 12:38:52 +02:00
Greg Turnquist
1cbbe692b5 DATAMONGO-2280 - Add maven wrapper. 2019-06-11 15:15:39 -05:00
Greg Turnquist
5bfe125160 DATAMONGO-2280 - Introduce Jenkins. 2019-06-11 15:03:15 -05:00
Christoph Strobl
1b6722324e DATAMONGO-2293 - Fix EntityOperations id population nulling out entire entity.
We now no longer null the entire entity if a given id value is actually null.

Original Pull Request: #742
2019-06-11 12:02:55 +02:00
Mark Paluch
a212f5f79d DATAMONGO-2231 - URL Cleanup. 2019-06-05 11:19:54 +02:00
Hippolyte Durix
2879348d4b DATAMONGO-2288 - Fix wrong indentation on documentation code sample.
Original pull request: #758.
2019-05-29 14:36:38 +02:00
Mark Paluch
10097311c7 DATAMONGO-2278 - Polishing.
Update method comment. Switch to diamond syntax.

Original pull request: #755.
2019-05-29 14:31:45 +02:00
owen.qqq
b8303a56b6 DATAMONGO-2278 - Update Querydsl base package names in MongoAnnotationProcessor.
Current MongoAnnotationProcessor still uses 3.x.x Querydsl package names.
Update package names to com.querydsl.core.annotations.* to use Querydsl annotations for code-generation.

Original pull request: #755.
2019-05-29 14:31:45 +02:00
Christoph Strobl
f9e468aebb DATAMONGO-2252 - Update Javadoc for Reactive/MongoOperations#getCollection(String).
Original pull request: #747.
2019-05-27 11:11:53 +02:00
Christoph Strobl
b900dc6c09 DATAMONGO-2275 - Fix NPE when mapping MongoJsonSchema used in query.
We fixed a NPE when reading raw Document from a collection using a query matching against a JSON schema.

Original pull request: #752.
2019-05-17 14:15:58 +02:00
Mark Paluch
bede55714c DATAMONGO-2267 - Polishing.
Reuse collection name for index creation instead of resolving the collection for every index. Switch lambda to method reference.

Original pull request: #746.
2019-05-15 10:43:11 +02:00
Christoph Strobl
3ec426352f DATAMONGO-2267 - Fix eager collection resolution in Object path.
We now lazily read the collection of an entity as it potentially requires a more expensive SpEL evaluation that might not have been required in fist place.

Original pull request: #746.
2019-05-15 10:39:45 +02:00
Mark Paluch
c6293e0ebd DATAMONGO-2270 - Updated changelog. 2019-05-13 18:19:09 +02:00
Mark Paluch
74e49a2326 DATAMONGO-2269 - After release cleanups. 2019-05-13 14:58:46 +02:00
Mark Paluch
69c451f69f DATAMONGO-2269 - Prepare next development iteration. 2019-05-13 14:58:44 +02:00
Mark Paluch
9af8160e05 DATAMONGO-2269 - Release version 2.1.8 (Lovelace SR8). 2019-05-13 13:25:52 +02:00
Mark Paluch
fdf4ea1e60 DATAMONGO-2269 - Prepare 2.1.8 (Lovelace SR8). 2019-05-13 13:25:06 +02:00
Mark Paluch
8c7afe012f DATAMONGO-2269 - Updated changelog. 2019-05-13 13:24:57 +02:00
Mark Paluch
6ba258a1f3 DATAMONGO-2260 - Updated changelog. 2019-05-13 12:37:41 +02:00
Oliver Drotbohm
059c8cf1dd DATAMONGO-2244 - Updated changelog. 2019-05-10 14:18:11 +02:00
Oliver Drotbohm
2b8955f583 DATAMONGO-2246 - After release cleanups. 2019-05-10 12:55:59 +02:00
Oliver Drotbohm
23fde167f6 DATAMONGO-2246 - Prepare next development iteration. 2019-05-10 12:55:57 +02:00
Oliver Drotbohm
9470f82e9b DATAMONGO-2246 - Release version 2.1.7 (Lovelace SR7). 2019-05-10 12:18:52 +02:00
Oliver Drotbohm
1e88e241d4 DATAMONGO-2246 - Prepare 2.1.7 (Lovelace SR7). 2019-05-10 12:18:10 +02:00
Oliver Drotbohm
0b8396c43c DATAMONGO-2246 - Updated changelog. 2019-05-10 12:18:01 +02:00
Christoph Strobl
b602e4cb26 DATAMONGO-2222 - Updated changelog. 2019-04-11 12:28:51 +02:00
Oliver Drotbohm
500393e596 DATAMONGO-2204 - After release cleanups. 2019-04-01 20:55:13 +02:00
Oliver Drotbohm
7e4cbdb8b0 DATAMONGO-2204 - Prepare next development iteration. 2019-04-01 20:55:12 +02:00
Oliver Drotbohm
1d6d8ff8e6 DATAMONGO-2204 - Release version 2.1.6 (Lovelace SR6). 2019-04-01 20:04:13 +02:00
Oliver Drotbohm
8ea4cbe9ea DATAMONGO-2204 - Prepare 2.1.6 (Lovelace SR6). 2019-04-01 20:03:19 +02:00
Oliver Drotbohm
45a0c36184 DATAMONGO-2204 - Updated changelog. 2019-04-01 20:03:15 +02:00
Oliver Drotbohm
599c79bce2 DATAMONGO-2186 - Updated changelog. 2019-04-01 19:37:01 +02:00
Oliver Drotbohm
eda6d40aa7 DATAMONGO-2243 - Updated changelog. 2019-04-01 18:52:21 +02:00
Oliver Drotbohm
22b844c87f DATAMONGO-2185 - Updated changelog. 2019-04-01 13:54:18 +02:00
Christoph Strobl
bdf7ec7c9b DATAMONGO-2241 - Polishing.
Ensure to have to DbRefResolver operate within the session while reusing the MappingContext.

Original Pull Request: #734
2019-04-01 12:54:50 +02:00
Mark Paluch
13db06d345 DATAMONGO-2241 - Retain MongoConverter using MongoTemplate.withSession(…).
We now reuse the MongoConverter instance that is associated with a MongoTemplate when using withSession(…) instead of creating a new converter instance.
In consequence, we're reusing PersistentEntity instances, EntityInstantiators and generated accessor classes.

Original Pull Request: #734
2019-04-01 10:47:52 +02:00
Mark Paluch
365ecd53c4 DATAMONGO-2221 - Polishing.
Reformat imports.

Original pull request: #732.
2019-03-26 14:46:23 +01:00
Christoph Strobl
dc40c42815 DATAMONGO-2221 - Fix mapping of Strings matching a valid ObjectId for unresolvable paths.
We now make sure we to not convert Strings representing valid ObjectIds into the such for paths that cannot be resolved to a Property.

Original pull request: #732.
2019-03-26 14:46:02 +01:00
Mark Paluch
49415efb8c DATAMONGO-2223 - Polishing.
Replace DBRef annotation using FQCN with import.

Original pull request: #660.
2019-03-25 10:07:04 +01:00
Christoph Strobl
dc234906f4 DATAMONGO-2223 - Add test for DBRef resolution in a different database.
Original pull request: #660.
2019-03-25 10:06:43 +01:00
Christoph Strobl
a7f51a7c85 DATAMONGO-2224 - Add trace logging to DBRef resolution.
We added trace logging to DefaultDbRefResolver.

<logger name="org.springframework.data.mongodb.core.convert.DefaultDbRefResolver" level="trace"/>

Original pull request: #659.
2019-03-25 10:00:39 +01:00
Spring Operator
9b0bd11d09 DATAMONGO-2231 - URL Cleanup.
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://www.apache.org/licenses/ with 1 occurrences migrated to:
  https://www.apache.org/licenses/ ([https](https://www.apache.org/licenses/) result 200).
* [ ] http://www.apache.org/licenses/LICENSE-2.0 with 815 occurrences migrated to:
  https://www.apache.org/licenses/LICENSE-2.0 ([https](https://www.apache.org/licenses/LICENSE-2.0) result 200).

Original Pull Request: #700
2019-03-22 09:56:26 +01:00
Spring Operator
d7ad883f69 DATAMONGO-2231 - URL Cleanup.
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* http://maven.apache.org/xsd/maven-4.0.0.xsd with 3 occurrences migrated to:
  https://maven.apache.org/xsd/maven-4.0.0.xsd ([https](https://maven.apache.org/xsd/maven-4.0.0.xsd) result 200).
* http://www.gopivotal.com (302) with 6 occurrences migrated to:
  https://pivotal.io ([https](https://www.gopivotal.com) result 200).
* http://maven.apache.org/maven-v4_0_0.xsd with 2 occurrences migrated to:
  https://maven.apache.org/maven-v4_0_0.xsd ([https](https://maven.apache.org/maven-v4_0_0.xsd) result 301).
* http://projects.spring.io/spring-data-mongodb with 1 occurrences migrated to:
  https://projects.spring.io/spring-data-mongodb ([https](https://projects.spring.io/spring-data-mongodb) result 301).
* http://www.pivotal.io with 1 occurrences migrated to:
  https://www.pivotal.io ([https](https://www.pivotal.io) result 301).

# Ignored
These URLs were intentionally ignored.

* http://maven.apache.org/POM/4.0.0 with 10 occurrences
* http://www.w3.org/2001/XMLSchema-instance with 5 occurrences

Original Pull Request: #666
2019-03-19 12:55:41 +01:00
Christoph Strobl
44308bfbe1 DATAMONGO-2225 - Fix potential NPE in MongoExampleMapper. 2019-03-18 13:51:37 +01:00
Christoph Strobl
9b673d342f DATAMONGO-2228 - Polishing.
Favor AssertJ over hamcrest.

Original Pull Request: #661
2019-03-18 10:50:16 +01:00
Mikhail Kaduchka
5517198310 DATAMONGO-2228 - Fixed loosing branches in AND expressions in MongodbDocumentSerializer.
Original Pull Request: #661
2019-03-18 10:32:01 +01:00
Christoph Strobl
819a04f3db DATAMONGO-2164 - Updated changelog. 2019-03-07 10:30:07 +01:00
Mark Paluch
f7202067a5 DATAMONGO-2219 - Fix ReactiveMongoTemplate.findAllAndRemove(…) if the query yields no results.
ReactiveMongoTemplate.findAllAndRemove(…) now completes successfully without emitting a result if the find query yields no hits. We no longer call the subsequent remove query if without previous results.

Original Pull Request: #657
2019-03-05 12:57:59 +01:00
Mark Paluch
f20a0f20c9 DATAMONGO-2206 - Polishing.
Reformat code. Convert spaces to tabs. Use mockk version property to define mockk version. Author tags.

Original pull request: #646.
2019-02-20 11:34:12 +01:00
Sebastien Deleuze
02216d5941 DATAMONGO-2206 - Migrate Kotlin tests to Mockk.
Original pull request: #646.
2019-02-20 11:34:12 +01:00
Mark Paluch
79f2094322 DATAMONGO-2187 - After release cleanups. 2019-02-13 11:24:22 +01:00
Mark Paluch
afbc5cfa25 DATAMONGO-2187 - Prepare next development iteration. 2019-02-13 11:24:20 +01:00
Mark Paluch
a3882a5e5c DATAMONGO-2187 - Release version 2.1.5 (Lovelace SR5). 2019-02-13 09:56:38 +01:00
Mark Paluch
8194772388 DATAMONGO-2187 - Prepare 2.1.5 (Lovelace SR5). 2019-02-13 09:55:35 +01:00
Mark Paluch
12f18850dc DATAMONGO-2187 - Updated changelog. 2019-02-13 09:55:29 +01:00
Mark Paluch
816c1da248 DATAMONGO-2196 - Polishing.
Fix stubbing in test that sneaked in through a back port not considering the change which method was used for entity removal.

Original pull request: #641.
2019-02-12 10:47:12 +01:00
Christoph Strobl
5a78f19781 DATAMONGO-2196 - Remove applies WriteConcern to single Document delete operations.
We now make sure to apply the WriteConcern correctly when calling deleteOne on MongoCollection.

Original pull request: #641.
2019-02-07 15:20:36 +01:00
Mark Paluch
698837921b DATAMONGO-2193 - Polishing.
Reformat code.

Original pull request: #640.
2019-02-05 11:44:03 +01:00
Christoph Strobl
0f7fc7880b DATAMONGO-2193 - Fix String <> ObjectId conversion for non-Id properties.
We now make sure to only convert valid ObjectId Strings if the property can be considered as id property.

Original pull request: #640.
2019-02-05 11:43:55 +01:00
Christoph Strobl
6e42f49b08 DATAMONGO-2189 - Polishing.
Assert returned object is not the same as the saved one and move helper method.

Original Pull Request: #638
2019-01-28 13:41:27 +01:00
Mark Paluch
bdfe4e99ed DATAMONGO-2189 - Fix AfterSaveEvent to contain the saved entity in ReactiveMongoTemplate.insert(…).
ReactiveMongoTemplate.insert(…) now uses the saved entity when emitting AfterSaveEvent. This change affects usage of immutable objects that are using Id generation. Previously, the to-be-saved entity instance was used which left the Id unpopulated.

Original Pull Request: #638
2019-01-28 11:50:43 +01:00
Mark Paluch
85aa3927a6 DATAMONGO-2145 - After release cleanups. 2019-01-10 13:48:12 +01:00
Mark Paluch
33c4e4294f DATAMONGO-2145 - Prepare next development iteration. 2019-01-10 13:48:10 +01:00
Mark Paluch
a89ab387cc DATAMONGO-2145 - Release version 2.1.4 (Lovelace SR4). 2019-01-10 12:35:56 +01:00
Mark Paluch
e52b8c9d38 DATAMONGO-2145 - Prepare 2.1.4 (Lovelace SR4). 2019-01-10 12:34:54 +01:00
Mark Paluch
4dbf4795db DATAMONGO-2145 - Updated changelog. 2019-01-10 12:34:48 +01:00
Mark Paluch
8e4c6f68ae DATAMONGO-2144 - Updated changelog. 2019-01-10 12:26:36 +01:00
Mark Paluch
fddbd126ea DATAMONGO-2143 - Updated changelog. 2019-01-10 11:01:21 +01:00
Christoph Strobl
ee5b26ab1c DATAMONGO-2168 - Polishing.
MetadataBackedField no longer fails when Path detects reference to field within java.lang.Class. This can happen when splitting the property name via camel case where the first part matches to class which resolves to the getClass() call on java.lang.Object. When then the 2nd part also maps to a method (like getName()) on Class an error would be thrown.

Original Pull Request: #631
2019-01-09 17:06:29 +01:00
Mark Paluch
01e9a2ed67 DATAMONGO-2168 - Convert assertions to AssertJ.
Original Pull Request: #631
2019-01-09 17:06:29 +01:00
Mark Paluch
10107c7b81 DATAMONGO-2168 - Do not map type key in QueryMapper.
QueryMapper now excludes the type key in during mapping and retains the value as-is. This change fixes an issue in which type keys were attempted to be mapped using the entity model. Type key resolution for _class failed silently while other type keys such as className failed in property path resolution and the failure became visible.

Original Pull Request: #631
2019-01-09 17:06:29 +01:00
Mark Paluch
abe7876086 DATAMONGO-2155 - Polishing.
Reduce visibility of MappedUpdated to package-protected to avoid exposure. Rename UpdateDefinition.incVersion() to inc(). Reintroduce doUpdate() methods accepting Update and delegating to the new doUpdate() methods to preserve binary compatibility.

Original pull request: #625.
2019-01-09 16:35:19 +01:00
Christoph Strobl
a759dff5fd DATAMONGO-2155 - Introduce UpdateDefinition.
Original pull request: #625.
2019-01-09 16:35:19 +01:00
Oliver Drotbohm
9f8d081ef3 DATAMONGO-2155 - Polishing.
Original pull request: #625.
2019-01-09 16:35:19 +01:00
Christoph Strobl
b8f6030441 DATAMONGO-2155 - Bypass mapping for already mapped updates.
We now make sure that mapped updates (as in doSaveVersioned and doUpdate) are mapped only once as mapping is required only once. Mapping already mapped query/update objects comes with undesired side-effects such as following invalid property paths or reduction of type information availability.

We now make sure to map key/value pairs of Map like properties to the values domain type, and apply potentially registered custom converters to the keys.
Fixed invalid test for DATAMONGO-1423 as this one did not check the application of the registered converter.

Original pull request: #625.
2019-01-09 16:33:31 +01:00
Mark Paluch
267decf189 DATAMONGO-2173 - Polishing.
Set interrupted thread state after catching InterruptedException. Fix potential NPE by checking the cursor. Streamline generics to not hide class-level generic types.

Original pull request: #634.
2019-01-09 13:00:10 +01:00
Christoph Strobl
3a7492c68d DATAMONGO-2173 - Translate and forward exceptions during CursorReadingTask#start() to ErrorHandler.
We now make sure to translate and pass on errors during the cursor initialization procedure to the configured error handler.

Original pull request: #634.
2019-01-09 13:00:10 +01:00
Christoph Strobl
273088b6a8 DATAMONGO-2174 - Fix InvalidPersistentPropertyPath exception when updating documents.
MetadataBackedField.getPath() now returns null instead throwing an error for fields that are not part of the domain model. This allows adding any field when updating an entity.

Original pull request: #633.
2019-01-09 10:34:48 +01:00
Mark Paluch
723b481f82 DATAMONGO-2179 - Fixed broken auditing for entities using optimistic locking via batch save.
The previous implementation of (Reactive)MongoTemplate.doInsertBatch(…) prematurely initialized the version property so that the entity wasn't considered new by the auditing subsystem. Even worse, for primitive version properties, the initialization kept the property at a value of 0, so that the just persisted entity was still considered new. This mean that via the repository route, inserts are triggered even for subsequent attempts to save an entity which caused duplicate key exceptions.

We now make sure we fire the BeforeConvertEvent before the version property is initialized or updated. Also, the initialization of the property now sets primitive properties to 1 initially.

Related tickets: DATAMONGO-2139, DATAMONGO-2150.

Original Pull Request: #632
2019-01-08 08:22:38 +01:00
Mark Paluch
8a34bc46a2 DATAMONGO-2181 - Consider repository collection name in ReactiveMongoRepository.saveAll(…).
We now consider the collection name that is bound to the repository when inserting entities of which all are new. Previously, the collection name was derived from the entity.

Original Pull Request: #632
2019-01-08 08:15:41 +01:00
Mark Paluch
bb4c16f4cd DATAMONGO-2170 - Polishing.
Use ObjectUtils to compote hash code as hash code implementation contained artifacts that do not belong there. Extract test method.

Original pull request: #629.
2019-01-07 13:11:02 +01:00
Christoph Strobl
cf5b7c9763 DATAMONGO-2170 - Return null instead of empty string for IndexInfo#getPartialFilterExpression when not set.
We now return null instead of an empty string when calling IndexInfo#getPartialFilterExpression. The method has been marked to return null vales before and we’re complying to that contract and return value expectation.

Original pull request: #629.
2019-01-07 13:11:02 +01:00
Mark Paluch
f4414e98a2 DATAMONGO-2175 - Update copyright years to 2019. 2019-01-02 14:11:51 +01:00
Christoph Strobl
a97bfd2a37 DATAMONGO-2160 - Updated changelog. 2018-12-11 11:43:12 +01:00
Mark Paluch
9fe0f5c984 DATAMONGO-2150 - Polishing.
Fix imperative auditing test to use intended persist mechanism. Remove final keywords from method args and local variables in ReactiveMongoTemplate. Rename DBObject to Document.

Original Pull Request: #627
2018-12-07 14:39:10 +01:00
Mark Paluch
718a7ffe8c DATAMONGO-2150 - Fixed broken auditing for entities using optimistic locking.
The previous implementation of ReactiveMongoTemplate.doSaveVersioned(…) prematurely initialized the version property so that the entity wasn't considered new by the auditing subsystem. Even worse, for primitive version properties, the initialization kept the property at a value of 0, so that the just persisted entity was still considered new. This mean that via the repository route, inserts are triggered even for subsequent attempts to save an entity which caused duplicate key exceptions.

We now make sure we fire the BeforeConvertEvent before the version property is initialized or updated. Also, the initialization of the property now sets primitive properties to 1 initially.

Added integration tests for the auditing via ReactiveMongoTemplate and repositories.

Related ticket: DATAMONGO-2139.

Original Pull Request: #627
2018-12-07 14:38:59 +01:00
Mark Paluch
f7106dc425 DATAMONGO-2156 - Polishing.
Original pull request: #626.
2018-12-05 14:40:13 +01:00
Mark Paluch
0698f8bcb8 DATAMONGO-2156 - Remove dependency to javax.xml.bind.
We now no longer use DatatypeConverter to convert byte[] to its Base64-representation but use Spring Framework's Base64 utils.

Original pull request: #626.
2018-12-05 14:40:13 +01:00
Mark Paluch
3effd9ae6f DATAMONGO-2149 - Polishing.
Add ticket reference to follow-up ticket regarding array matching on partial DBRef expressions.

Related ticket: DATAMONGO-2154

Original pull request: #623.
2018-11-30 14:53:56 +01:00
Christoph Strobl
7002cd1456 DATAMONGO-2149 - Fix $slice in fields projection when pointing to array of DBRefs.
We now no longer try to convert the actual slice parameters into a DBRef.

Original pull request: #623.
2018-11-30 14:52:58 +01:00
Mark Paluch
a15d488657 DATAMONGO-2148 - Polishing.
Add author tag. Add logging for ReactiveMongoTemplate.count(…) and findDistinct(…) operations. Fix variable names.

Original pull request: #620.
2018-11-28 17:24:52 +01:00
Cimon Lucas (LCM)
44651581b1 DATAMONGO-2148 - Add query logging for MongoTemplate.count(…).
Original pull request: #620.
2018-11-28 17:24:46 +01:00
Mark Paluch
6d64f5b2b2 DATAMONGO-2121 - After release cleanups. 2018-11-27 14:23:35 +01:00
Mark Paluch
0c52a29ba8 DATAMONGO-2121 - Prepare next development iteration. 2018-11-27 14:23:33 +01:00
Mark Paluch
bd8bd4f568 DATAMONGO-2121 - Release version 2.1.3 (Lovelace SR3). 2018-11-27 13:43:24 +01:00
Mark Paluch
c75f29dc42 DATAMONGO-2121 - Prepare 2.1.3 (Lovelace SR3). 2018-11-27 13:42:17 +01:00
Mark Paluch
e493af7266 DATAMONGO-2121 - Updated changelog. 2018-11-27 13:42:07 +01:00
Mark Paluch
8d892e5924 DATAMONGO-2109 - Updated changelog. 2018-11-27 12:36:47 +01:00
Mark Paluch
053299f243 DATAMONGO-2110 - Updated changelog. 2018-11-27 11:27:21 +01:00
Mark Paluch
872659cc00 DATAMONGO-2119 - Polishing.
Convert anonymous JSON callback class into a private static one. Use an expressive Pattern constant.

Original pull request: #621.
2018-11-23 09:48:26 +01:00
Christoph Strobl
96978a6194 DATAMONGO-2119 - Allow SpEL usage for annotated $regex query.
Original pull request: #621.
2018-11-23 09:48:26 +01:00
Oliver Drotbohm
2253d3e301 DATAMONGO-2108 - Fixed broken auditing for entities using optimistic locking.
The previous implementation of MongoTemplate.doSaveVersioned(…) prematurely initialized the version property so that the entity wasn't considered new by the auditing subsystem. Even worse, for primitive version properties, the initialization kept the property at a value of 0, so that the just persisted entity was still considered new. This mean that via the repository route, inserts are triggered even for subsequent attempts to save an entity which caused duplicate key exceptions.

We now make sure we fire the BeforeConvertEvent before the version property is initialized or updated. Also, the initialization of the property now sets primitive properties to 1 initially.

Added integration tests for the auditing via MongoOperations and repositories.
2018-11-22 15:05:31 +01:00
Mark Paluch
5982ee84f7 DATAMONGO-2130 - Polishing.
Replace duplicate checks to ClientSession.hasActiveTransaction() with MongoResourceHolder.hasActiveTransaction(). Introduce MongoResourceHolder.getRequiredSession() to avoid nullability warnings.

Original pull request: #618.
2018-11-16 12:58:40 +01:00
Christoph Strobl
dd2af6462d DATAMONGO-2130 - Polishing.
Set timeout for InetAdress host lookup to reduce test execution time.

Original pull request: #618.
2018-11-16 12:58:40 +01:00
Christoph Strobl
622643bf24 DATAMONGO-2130 - Fix Repository count & exists inside transaction.
We now make sure invocations on repository count and exists methods delegate to countDocuments when inside a transaction.

Original pull request: #618.
2018-11-16 12:58:40 +01:00
Oliver Drotbohm
51cc55baac DATAMONGO-2135 - Default to intermediate List for properties typed to Collection.
We now defensively create a List rather than a LinkedHashSet (which Spring's CollectionFactory.createCollection(…) defaults to) to make sure we're not accidentally dropping values that are considered equal according to their Java class definition.
2018-11-15 15:26:36 +01:00
Mark Paluch
0b106e5649 DATAMONGO-2107 - After release cleanups. 2018-10-29 13:59:17 +01:00
Mark Paluch
8975d93ab3 DATAMONGO-2107 - Prepare next development iteration. 2018-10-29 13:59:15 +01:00
Mark Paluch
e25b6c49f5 DATAMONGO-2107 - Release version 2.1.2 (Lovelace SR2). 2018-10-29 12:53:51 +01:00
Mark Paluch
7a70c205de DATAMONGO-2107 - Prepare 2.1.2 (Lovelace SR2). 2018-10-29 12:52:54 +01:00
Mark Paluch
6045efa450 DATAMONGO-2107 - Updated changelog. 2018-10-29 12:52:45 +01:00
Mark Paluch
7b0816b3ee DATAMONGO-2118 - Polishing.
Fix typo in reactive repositories reference documentation.

Original pull request: #611.
2018-10-26 10:08:03 +02:00
Mona Mohamadinia
14e4ea736d DATAMONGO-2118 - Fix typo in repositories reference documentation.
Original pull request: #611.
2018-10-26 10:08:03 +02:00
Mark Paluch
32e7d9ab7f DATAMONGO-2098 - Polishing.
Annotate methods and parameters with Nullable. Use diamond syntax where appropriate.

Original pull request: #612.
2018-10-25 15:35:26 +02:00
Zied Yaich
7f35ad9e45 DATAMONGO-2098 - Fix typo in MappingMongoConverterParser method.
Original pull request: #612.
2018-10-25 15:35:26 +02:00
Mark Paluch
60228f6e5a DATAMONGO-2113 - Polishing.
Increase subscription await timeout to allow for slow system processing such as on TravisCI.

Original pull request: #615.
2018-10-25 14:33:28 +02:00
Christoph Strobl
7604492b7f DATAMONGO-2113 - Polishing.
Use AssertJ in tests.

Original pull request: #615.
2018-10-25 14:33:28 +02:00
Christoph Strobl
4680fe0e77 DATAMONGO-2113 - Fix resumeTimestamp conversion for change streams.
We now use the first 32 bits of the timestamp to create the instant and ignore the ordinal value.

Original pull request: #615.
2018-10-25 14:33:28 +02:00
Mark Paluch
b4228c88d3 DATAMONGO-2083 - Updated changelog. 2018-10-15 14:19:03 +02:00
Mark Paluch
f6ef8c94c8 DATAMONGO-2084 - Updated changelog. 2018-10-15 12:46:24 +02:00
Mark Paluch
0d0dafa85e DATAMONGO-2094 - After release cleanups. 2018-10-15 11:12:14 +02:00
Mark Paluch
29aa34619f DATAMONGO-2094 - Prepare next development iteration. 2018-10-15 11:12:12 +02:00
Mark Paluch
7f19f769c4 DATAMONGO-2094 - Release version 2.1.1 (Lovelace SR1). 2018-10-15 10:42:04 +02:00
Mark Paluch
a40e89d90a DATAMONGO-2094 - Prepare 2.1.1 (Lovelace SR1). 2018-10-15 10:40:57 +02:00
Mark Paluch
6b2350200a DATAMONGO-2094 - Updated changelog. 2018-10-15 10:40:53 +02:00
Mark Paluch
fb50b0f6e7 DATAMONGO-2096 - Polishing.
Migrate assertions to AssertJ.

Original pull request: #613.
2018-10-05 15:02:38 +02:00
Christoph Strobl
ab568229b5 DATAMONGO-2096 - Fix target field name for GraphLookup aggregation operation.
We now make sure to use the target field name instead of the alias when processing GraphLookupOperation.

Original pull request: #613.
2018-10-05 15:02:38 +02:00
Mark Paluch
7f9c1bd774 DATAMONGO-2061 - After release cleanups. 2018-09-21 07:46:17 -04:00
Mark Paluch
670a0978da DATAMONGO-2061 - Prepare next development iteration. 2018-09-21 07:46:16 -04:00
575 changed files with 10358 additions and 38941 deletions

View File

@@ -16,12 +16,9 @@ before_install:
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
global:
- MONGO_VERSION=4.0.0
addons:
apt:

314
Jenkinsfile vendored
View File

@@ -1,219 +1,123 @@
pipeline {
agent none
agent none
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
}
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/2.1.x", threshold: hudson.model.Result.SUCCESS)
}
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '14'))
}
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '14'))
}
stages {
stage("Docker images") {
parallel {
stage('Publish JDK 8 + MongoDB 4.0') {
when {
changeset "ci/openjdk8-mongodb-4.0/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.0", "ci/openjdk8-mongodb-4.0/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
stage('Publish JDK 8 + MongoDB 4.1') {
when {
changeset "ci/openjdk8-mongodb-4.1/**"
}
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/")
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') }
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()
}
}
}
stages {
stage("Test") {
when {
anyOf {
branch '2.1.x'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: baseline") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.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 --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'
}
}
}
}
stage("test: baseline") {
when {
anyOf {
branch 'master'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.2: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 --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'
}
}
}
}
stage("Test other configurations") {
when {
anyOf {
branch 'master'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: mongodb 4.0") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.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 --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'
}
}
stage("test: mongodb 4.1") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.1: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 --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'
}
}
}
}
stage('Release to artifactory') {
when {
branch 'issue/*'
not { triggeredBy 'UpstreamCause' }
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 20, unit: 'MINUTES') }
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-mongodb-2.1 " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
steps {
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-mongodb " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
stage('Release to artifactory with docs') {
when {
branch '2.1.x'
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 20, unit: 'MINUTES') }
stage('Publish documentation') {
when {
branch 'master'
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-mongodb-2.1 " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
steps {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.distribution-repository=temp-private-local " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
post {
changed {
script {
slackSend(
color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger',
channel: '#spring-data-dev',
message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}")
emailext(
subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
mimeType: 'text/html',
recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
body: "<a href=\"${env.BUILD_URL}\">${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}</a>")
}
}
}
post {
changed {
script {
slackSend(
color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger',
channel: '#spring-data-dev',
message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}")
emailext(
subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
mimeType: 'text/html',
recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
body: "<a href=\"${env.BUILD_URL}\">${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}</a>")
}
}
}
}

View File

@@ -1,9 +0,0 @@
# Security Policy
## Supported Versions
Please see the https://spring.io/projects/spring-data-mongodb[Spring Data MongoDB] project page for supported versions.
## Reporting a Vulnerability
Please don't raise security vulnerabilities here. Head over to https://pivotal.io/security to learn how to disclose them responsibly.

View File

@@ -1,39 +0,0 @@
== Running CI tasks locally
Since Concourse is built on top of Docker, it's easy to:
* Debug what went wrong on your local machine.
* Test out a a tweak to your `test.sh` script before sending it out.
* Experiment against a new image before submitting your pull request.
All of these use cases are great reasons to essentially run what Concourse does on your local machine.
IMPORTANT: To do this you must have Docker installed on your machine.
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-mongodb-github springci/spring-data-8-jdk-with-mongodb /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-mongodb-github`.
+
Next, run the `test.sh` script from inside the container:
+
2. `PROFILE=none spring-data-mongodb-github/ci/test.sh`
Since the container is binding to your source, you can make edits from your IDE and continue to run build jobs.
If you need to test the `build.sh` script, do this:
1. `mkdir /tmp/spring-data-mongodb-artifactory`
2. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-mongodb-github --mount type=bind,source="/tmp/spring-data-mongodb-artifactory",target=/spring-data-mongodb-artifactory springci/spring-data-8-jdk-with-mongodb /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-mongodb-github` and the temporary
artifactory output directory at `spring-data-mongodb-artifactory`.
+
Next, run the `build.sh` script from inside the container:
+
3. `spring-data-mongodb-github/ci/build.sh`
IMPORTANT: `build.sh` doesn't actually push to Artifactory so don't worry about accidentally deploying anything.
It just deploys to a local folder. That way, the `artifactory-resource` later in the pipeline can pick up these artifacts
and deliver them to artifactory.
NOTE: Docker containers can eat up disk space fast! From time to time, run `docker system prune` to clean out old images.

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 e162f504a20cdf15827f718d4b7c549a058f8b6b
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/*

35
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>2.1.16.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,20 +15,21 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.3.0.M2</version>
<version>2.1.16.RELEASE</version>
</parent>
<modules>
<module>spring-data-mongodb</module>
<module>spring-data-mongodb-cross-store</module>
<module>spring-data-mongodb-distribution</module>
</modules>
<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.1.16.RELEASE</springdata.commons>
<mongo>3.8.2</mongo>
<mongo.reactivestreams>1.9.2</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -117,10 +118,30 @@
<id>benchmarks</id>
<modules>
<module>spring-data-mongodb</module>
<module>spring-data-mongodb-cross-store</module>
<module>spring-data-mongodb-distribution</module>
<module>spring-data-mongodb-benchmarks</module>
</modules>
</profile>
<profile>
<id>distribute</id>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<configuration>
<attributes>
<mongo-reactivestreams>${mongo.reactivestreams}</mongo-reactivestreams>
<reactor>${reactor}</reactor>
</attributes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
@@ -134,8 +155,8 @@
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</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>2.1.16.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -87,7 +87,6 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<testSourceDirectory>${project.build.sourceDirectory}</testSourceDirectory>
<testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
<excludes>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
<aspects>
<aspect name="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" />
<aspect name="org.springframework.data.mongodb.crossstore.MongoDocumentBacking" />
</aspects>
</aspectj>

View File

@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.1.16.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
<name>Spring Data MongoDB - Cross-Store Support</name>
<properties>
<jpa>2.1.1</jpa>
<hibernate>5.2.1.Final</hibernate>
<java-module-name>spring.data.mongodb.cross.store</java-module-name>
<project.root>${basedir}/..</project.root>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.16.RELEASE</version>
</dependency>
<!-- reactive -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj}</version>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>${jpa}</version>
<optional>true</optional>
</dependency>
<!-- For Tests -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.6</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>${source.level}</complianceLevel>
<source>${source.level}</source>
<target>${source.level}</target>
<xmlConfigured>aop.xml</xmlConfigured>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 the original author or authors.
* 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.
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository;
package org.springframework.data.mongodb.crossstore;
import lombok.Value;
import org.springframework.data.crossstore.ChangeSetBacked;
/**
* @author Christoph Strobl
* @author Thomas Risberg
* @author Oliver Gierke
* @deprecated will be removed without replacement.
*/
@Value
class SumAge {
@Deprecated
public interface DocumentBacked extends ChangeSetBacked {
private Long total;
}

View File

@@ -0,0 +1,214 @@
/*
* 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.crossstore;
import javax.persistence.EntityManagerFactory;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.crossstore.ChangeSet;
import org.springframework.data.crossstore.ChangeSetBacked;
import org.springframework.data.crossstore.ChangeSetPersister;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.ClassUtils;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
/**
* @author Thomas Risberg
* @author Oliver Gierke
* @author Alex Vengrovsk
* @author Mark Paluch
* @deprecated will be removed without replacement.
*/
@Deprecated
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
private static final String ENTITY_CLASS = "_entity_class";
private static final String ENTITY_ID = "_entity_id";
private static final String ENTITY_FIELD_NAME = "_entity_field_name";
private static final String ENTITY_FIELD_CLASS = "_entity_field_class";
private final Logger log = LoggerFactory.getLogger(getClass());
private MongoTemplate mongoTemplate;
private EntityManagerFactory entityManagerFactory;
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#getPersistentState(java.lang.Class, java.lang.Object, org.springframework.data.crossstore.ChangeSet)
*/
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass, Object id, final ChangeSet changeSet)
throws DataAccessException, NotFoundException {
if (id == null) {
log.debug("Unable to load MongoDB data for null id");
return;
}
String collName = getCollectionNameForEntity(entityClass);
final Document dbk = new Document();
dbk.put(ENTITY_ID, id);
dbk.put(ENTITY_CLASS, entityClass.getName());
if (log.isDebugEnabled()) {
log.debug("Loading MongoDB data for {}", dbk);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
for (Document dbo : collection.find(dbk)) {
String key = (String) dbo.get(ENTITY_FIELD_NAME);
if (log.isDebugEnabled()) {
log.debug("Processing key: {}", key);
}
if (!changeSet.getValues().containsKey(key)) {
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
if (className == null) {
throw new DataIntegrityViolationException(
"Unble to convert property " + key + ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
}
Class<?> clazz = ClassUtils.resolveClassName(className, ClassUtils.getDefaultClassLoader());
Object value = mongoTemplate.getConverter().read(clazz, dbo);
if (log.isDebugEnabled()) {
log.debug("Adding to ChangeSet: {}", key);
}
changeSet.set(key, value);
}
}
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#getPersistentId(org.springframework.data.crossstore.ChangeSetBacked, org.springframework.data.crossstore.ChangeSet)
*/
public Object getPersistentId(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException {
if (log.isDebugEnabled()) {
log.debug("getPersistentId called on {}", entity);
}
if (entityManagerFactory == null) {
throw new DataAccessResourceFailureException("EntityManagerFactory cannot be null");
}
return entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity);
}
/*
* (non-Javadoc)
* @see org.springframework.data.crossstore.ChangeSetPersister#persistState(org.springframework.data.crossstore.ChangeSetBacked, org.springframework.data.crossstore.ChangeSet)
*/
public Object persistState(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException {
if (cs == null) {
log.debug("Flush: changeset was null, nothing to flush.");
return 0L;
}
if (log.isDebugEnabled()) {
log.debug("Flush: changeset: {}", cs.getValues());
}
String collName = getCollectionNameForEntity(entity.getClass());
if (mongoTemplate.getCollection(collName) == null) {
mongoTemplate.createCollection(collName);
}
for (String key : cs.getValues().keySet()) {
if (key != null && !key.startsWith("_") && !key.equals(ChangeSetPersister.ID_KEY)) {
Object value = cs.getValues().get(key);
final Document dbQuery = new Document();
dbQuery.put(ENTITY_ID, getPersistentId(entity, cs));
dbQuery.put(ENTITY_CLASS, entity.getClass().getName());
dbQuery.put(ENTITY_FIELD_NAME, key);
final Document dbId = mongoTemplate.execute(collName, new CollectionCallback<Document>() {
public Document doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
Document id = collection.find(dbQuery).first();
return id;
}
});
if (value == null) {
if (log.isDebugEnabled()) {
log.debug("Flush: removing: {}", dbQuery);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
DeleteResult dr = collection.deleteMany(dbQuery);
return null;
}
});
} else {
final Document dbDoc = new Document();
dbDoc.putAll(dbQuery);
if (log.isDebugEnabled()) {
log.debug("Flush: saving: {}", dbQuery);
}
mongoTemplate.getConverter().write(value, dbDoc);
dbDoc.put(ENTITY_FIELD_CLASS, value.getClass().getName());
if (dbId != null) {
dbDoc.put("_id", dbId.get("_id"));
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
if (dbId != null) {
collection.replaceOne(Filters.eq("_id", dbId.get("_id")), dbDoc);
} else {
if (dbDoc.containsKey("_id") && dbDoc.get("_id") == null) {
dbDoc.remove("_id");
}
collection.insertOne(dbDoc);
}
return null;
}
});
}
}
}
return 0L;
}
/**
* Returns the collection the given entity type shall be persisted to.
*
* @param entityClass must not be {@literal null}.
* @return
*/
private String getCollectionNameForEntity(Class<? extends ChangeSetBacked> entityClass) {
return mongoTemplate.getCollectionName(entityClass);
}
}

View File

@@ -0,0 +1,272 @@
/*
* 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.crossstore;
import java.lang.reflect.Field;
import javax.persistence.EntityManager;
import javax.persistence.Transient;
import javax.persistence.Entity;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.crossstore.RelatedDocument;
import org.springframework.data.mongodb.crossstore.DocumentBacked;
import org.springframework.data.crossstore.ChangeSetBackedTransactionSynchronization;
import org.springframework.data.crossstore.ChangeSet;
import org.springframework.data.crossstore.ChangeSetPersister;
import org.springframework.data.crossstore.ChangeSetPersister.NotFoundException;
import org.springframework.data.crossstore.HashMapChangeSet;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Aspect to turn an object annotated with @Document into a persistent document using Mongo.
*
* @author Thomas Risberg
* @deprecated will be removed without replacement.
*/
@Deprecated
public aspect MongoDocumentBacking {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDocumentBacking.class);
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
public void setChangeSetPersister(ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @RelatedDocument * (@Entity+ *).*:@Transient;
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
execution((DocumentBacked+).new(..)) &&
!execution((DocumentBacked+).new(ChangeSet)) &&
this(entity);
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity, ChangeSet cs) :
execution((DocumentBacked+).new(ChangeSet)) &&
this(entity) &&
args(cs);
protected pointcut entityFieldGet(DocumentBacked entity) :
get(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
!get(* DocumentBacked.*);
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
set(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
args(newVal) &&
!set(* DocumentBacked.*);
// intercept EntityManager.merge calls
public pointcut entityManagerMerge(EntityManager em, Object entity) :
call(* EntityManager.merge(Object)) &&
target(em) &&
args(entity);
// intercept EntityManager.remove calls
// public pointcut entityManagerRemove(EntityManager em, Object entity) :
// call(* EntityManager.remove(Object)) &&
// target(em) &&
// args(entity);
// move changeSet from detached entity to the newly merged persistent object
Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) {
Object mergedEntity = proceed(em, entity);
if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) {
((DocumentBacked) mergedEntity).changeSet = ((DocumentBacked) entity).getChangeSet();
}
return mergedEntity;
}
// clear changeSet from removed entity
// Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) {
// if (entity instanceof DocumentBacked) {
// removeChangeSetValues((DocumentBacked)entity);
// }
// return proceed(em, entity);
// }
private static void removeChangeSetValues(DocumentBacked entity) {
LOGGER.debug("Removing all change-set values for " + entity);
ChangeSet nulledCs = new HashMapChangeSet();
DocumentBacked documentEntity = (DocumentBacked) entity;
@SuppressWarnings("unchecked")
ChangeSetPersister<Object> changeSetPersister = (ChangeSetPersister<Object>) documentEntity.itdChangeSetPersister;
try {
changeSetPersister.getPersistentState(documentEntity.getClass(), documentEntity.get_persistent_id(),
documentEntity.getChangeSet());
} catch (DataAccessException e) {
} catch (NotFoundException e) {
}
for (String key : entity.getChangeSet().getValues().keySet()) {
nulledCs.set(key, null);
}
entity.setChangeSet(nulledCs);
}
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER.debug("User-defined constructor called on DocumentBacked object of class " + entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
entity.itdTransactionSynchronization = new ChangeSetBackedTransactionSynchronization(changeSetPersister, entity);
// registerTransactionSynchronization(entity);
}
private static void registerTransactionSynchronization(DocumentBacked entity) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding transaction synchronization for " + entity);
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity);
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization is not active for " + entity);
}
}
}
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
@Transient
private ChangeSet DocumentBacked.changeSet;
@Transient
private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
@Transient
private ChangeSetBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
Object id = itdChangeSetPersister.getPersistentId(this, this.changeSet);
itdChangeSetPersister.persistState(this, this.changeSet);
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
// lifecycle methods
@javax.persistence.PostPersist
public void DocumentBacked.itdPostPersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate
public void DocumentBacked.itdPreUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostUpdate
public void DocumentBacked.itdPostUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostRemove
public void DocumentBacked.itdPostRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
removeChangeSetValues(this);
}
@javax.persistence.PostLoad
public void DocumentBacked.itdPostLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName + "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister
.getPersistentState(entity.getClass(), entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName + "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
return proceed(entity, newVal);
}
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 the original author or authors.
* 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.
@@ -13,27 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.test.util;
package org.springframework.data.mongodb.crossstore;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
/**
* {@link EnableIfReplicaSetAvailable} marks a specific test class or method to be only executed against a server running in
* replicaSet mode. Intended to be used along with {@link MongoServerCondition}.
*
* @author Christoph Strobl
* @since 3.0
* @author Thomas Risberg
* @deprecated will be removed without replacement.
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Tag("replSet")
public @interface EnableIfReplicaSetAvailable {
@Target({ ElementType.FIELD })
public @interface RelatedDocument {
}

View File

@@ -0,0 +1,5 @@
/**
* Infrastructure for Spring Data's MongoDB cross store support.
*/
package org.springframework.data.mongodb.crossstore;

View File

@@ -0,0 +1,195 @@
/*
* 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.crossstore;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.bson.Document;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.crossstore.test.Address;
import org.springframework.data.mongodb.crossstore.test.Person;
import org.springframework.data.mongodb.crossstore.test.Resume;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Integration tests for MongoDB cross-store persistence (mainly {@link MongoChangeSetPersister}).
*
* @author Thomas Risberg
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/META-INF/spring/applicationContext.xml")
public class CrossStoreMongoTests {
@Autowired MongoTemplate mongoTemplate;
@PersistenceContext EntityManager entityManager;
@Autowired PlatformTransactionManager transactionManager;
TransactionTemplate txTemplate;
@Before
public void setUp() {
txTemplate = new TransactionTemplate(transactionManager);
clearData(Person.class);
Address address = new Address(12, "MAin St.", "Boston", "MA", "02101");
Resume resume = new Resume();
resume.addEducation("Skanstulls High School, 1975");
resume.addEducation("Univ. of Stockholm, 1980");
resume.addJob("DiMark, DBA, 1990-2000");
resume.addJob("VMware, Developer, 2007-");
final Person person = new Person("Thomas", 20);
person.setAddress(address);
person.setResume(resume);
person.setId(1L);
txTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus status) {
entityManager.persist(person);
return null;
}
});
}
@After
public void tearDown() {
txTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus status) {
entityManager.remove(entityManager.find(Person.class, 1L));
return null;
}
});
}
private void clearData(Class<?> domainType) {
String collectionName = mongoTemplate.getCollectionName(domainType);
mongoTemplate.dropCollection(collectionName);
}
@Test
@Transactional
public void testReadJpaToMongoEntityRelationship() {
Person found = entityManager.find(Person.class, 1L);
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-", found.getResume().getJobs());
}
@Test
@Transactional
public void testUpdatedJpaToMongoEntityRelationship() {
Person found = entityManager.find(Person.class, 1L);
found.setAge(44);
found.getResume().addJob("SpringDeveloper.com, Consultant, 2005-2006");
entityManager.merge(found);
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-" + "; "
+ "SpringDeveloper.com, Consultant, 2005-2006", found.getResume().getJobs());
}
@Test
public void testMergeJpaEntityWithMongoDocument() {
final Person detached = entityManager.find(Person.class, 1L);
entityManager.detach(detached);
detached.getResume().addJob("TargetRx, Developer, 2000-2005");
Person merged = txTemplate.execute(new TransactionCallback<Person>() {
public Person doInTransaction(TransactionStatus status) {
Person result = entityManager.merge(detached);
entityManager.flush();
return result;
}
});
Assert.assertTrue(detached.getResume().getJobs().contains("TargetRx, Developer, 2000-2005"));
Assert.assertTrue(merged.getResume().getJobs().contains("TargetRx, Developer, 2000-2005"));
final Person updated = entityManager.find(Person.class, 1L);
Assert.assertTrue(updated.getResume().getJobs().contains("TargetRx, Developer, 2000-2005"));
}
@Test
public void testRemoveJpaEntityWithMongoDocument() {
txTemplate.execute(new TransactionCallback<Person>() {
public Person doInTransaction(TransactionStatus status) {
Person p2 = new Person("Thomas", 20);
Resume r2 = new Resume();
r2.addEducation("Skanstulls High School, 1975");
r2.addJob("DiMark, DBA, 1990-2000");
p2.setResume(r2);
p2.setId(2L);
entityManager.persist(p2);
Person p3 = new Person("Thomas", 20);
Resume r3 = new Resume();
r3.addEducation("Univ. of Stockholm, 1980");
r3.addJob("VMware, Developer, 2007-");
p3.setResume(r3);
p3.setId(3L);
entityManager.persist(p3);
return null;
}
});
txTemplate.execute(new TransactionCallback<Person>() {
public Person doInTransaction(TransactionStatus status) {
final Person found2 = entityManager.find(Person.class, 2L);
entityManager.remove(found2);
return null;
}
});
boolean weFound3 = false;
for (Document dbo : this.mongoTemplate.getCollection(mongoTemplate.getCollectionName(Person.class)).find()) {
Assert.assertTrue(!dbo.get("_entity_id").equals(2L));
if (dbo.get("_entity_id").equals(3L)) {
weFound3 = true;
}
}
Assert.assertTrue(weFound3);
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.crossstore.test;
public class Address {
private Integer streetNumber;
private String streetName;
private String city;
private String state;
private String zip;
public Address(Integer streetNumber, String streetName, String city, String state, String zip) {
super();
this.streetNumber = streetNumber;
this.streetName = streetName;
this.city = city;
this.state = state;
this.zip = zip;
}
public Integer getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(Integer streetNumber) {
this.streetNumber = streetNumber;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.crossstore.test;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.springframework.data.mongodb.crossstore.RelatedDocument;
@Entity
public class Person {
@Id
Long id;
private String name;
private int age;
private java.util.Date birthDate;
@RelatedDocument
private Address address;
@RelatedDocument
private Resume resume;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
this.birthDate = new java.util.Date();
}
public void birthday() {
++age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public java.util.Date getBirthDate() {
return birthDate;
}
public void setBirthDate(java.util.Date birthDate) {
this.birthDate = birthDate;
}
public Resume getResume() {
return resume;
}
public void setResume(Resume resume) {
this.resume = resume;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.crossstore.test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Resume {
private static final Log LOGGER = LogFactory.getLog(Resume.class);
@Id
private ObjectId id;
private String education = "";
private String jobs = "";
public String getId() {
return id.toString();
}
public String getEducation() {
return education;
}
public void addEducation(String education) {
LOGGER.debug("Adding education " + education);
this.education = this.education + (this.education.length() > 0 ? "; " : "") + education;
}
public String getJobs() {
return jobs;
}
public void addJob(String job) {
LOGGER.debug("Adding job " + job);
this.jobs = this.jobs + (this.jobs.length() > 0 ? "; " : "") + job;
}
@Override
public String toString() {
return "Resume [education=" + education + ", jobs=" + jobs + "]";
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.springframework.data.mongodb.crossstore.test.Person</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<!--value='create' to build a new database on each run; value='update' to modify an existing database; value='create-drop' means the same as 'create' but also drops tables when Hibernate closes; value='validate' makes no changes to the database-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
</properties>
</persistence-unit>
</persistence>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:spring-configured/>
<context:component-scan base-package="org.springframework.persistence.mongodb.test">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<mongo:mapping-converter/>
<!-- Mongo config -->
<bean id="mongoClient" class="org.springframework.data.mongodb.core.MongoClientFactoryBean">
<property name="host" value="localhost"/>
<property name="port" value="27017"/>
</bean>
<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<constructor-arg name="mongoClient" ref="mongoClient"/>
<constructor-arg name="databaseName" value="database"/>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>
<bean class="org.springframework.data.mongodb.core.MongoExceptionTranslator"/>
<!-- Mongo aspect config -->
<bean class="org.springframework.data.mongodb.crossstore.MongoDocumentBacking"
factory-method="aspectOf">
<property name="changeSetPersister" ref="mongoChangeSetPersister"/>
</bean>
<bean id="mongoChangeSetPersister"
class="org.springframework.data.mongodb.crossstore.MongoChangeSetPersister">
<property name="mongoTemplate" ref="mongoTemplate"/>
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<jdbc:embedded-database id="dataSource" type="HSQL">
</jdbc:embedded-database>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="test"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
</bean>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<!--
<logger name="org.springframework" level="debug" />
-->
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.M2</version>
<version>2.1.16.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -32,15 +31,8 @@
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<configuration>
<attributes>
<mongo-reactivestreams>${mongo.reactivestreams}</mongo-reactivestreams>
<reactor>${reactor}</reactor>
</attributes>
</configuration>
</plugin>
</plugins>
</build>
</project>

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>2.1.16.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -83,14 +83,14 @@
<!-- reactive -->
<dependency>
<groupId>org.mongodb</groupId>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo.reactivestreams}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<version>${mongo}</version>
<optional>true</optional>
@@ -107,7 +107,7 @@
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
@@ -119,14 +119,14 @@
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<groupId>io.reactivex</groupId>
<artifactId>rxjava-reactive-streams</artifactId>
<version>${rxjava-reactive-streams}</version>
<optional>true</optional>
@@ -264,25 +264,20 @@
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
<optional>true</optional>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -327,7 +322,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2010-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.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.lang.Nullable;
/**
* Exception being thrown in case we cannot connect to a MongoDB instance.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public class CannotGetMongoDbConnectionException extends DataAccessResourceFailureException {
private final UserCredentials credentials;
private final @Nullable String database;
private static final long serialVersionUID = 1172099106475265589L;
public CannotGetMongoDbConnectionException(String msg, Throwable cause) {
super(msg, cause);
this.database = null;
this.credentials = UserCredentials.NO_CREDENTIALS;
}
public CannotGetMongoDbConnectionException(String msg) {
this(msg, null, UserCredentials.NO_CREDENTIALS);
}
public CannotGetMongoDbConnectionException(String msg, @Nullable String database, UserCredentials credentials) {
super(msg);
this.database = database;
this.credentials = credentials;
}
/**
* Returns the {@link UserCredentials} that were used when trying to connect to the MongoDB instance.
*
* @return
*/
public UserCredentials getCredentials() {
return this.credentials;
}
/**
* Returns the name of the database trying to be accessed.
*
* @return
*/
@Nullable
public String getDatabase() {
return database;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
import reactor.core.publisher.Mono;
@@ -87,16 +88,4 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
* @since 2.1
*/
ReactiveMongoDatabaseFactory withSession(ClientSession session);
/**
* Returns if the given {@link ReactiveMongoDatabaseFactory} is bound to a
* {@link com.mongodb.reactivestreams.client.ClientSession} that has an
* {@link com.mongodb.reactivestreams.client.ClientSession#hasActiveTransaction() active transaction}.
*
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
* @since 2.2
*/
default boolean isTransactionActive() {
return false;
}
}

View File

@@ -1,278 +0,0 @@
/*
* Copyright 2019-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 reactor.core.publisher.Mono;
import reactor.util.context.Context;
import org.springframework.lang.Nullable;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.reactive.ReactiveResourceSynchronization;
import org.springframework.transaction.reactive.TransactionSynchronization;
import org.springframework.transaction.reactive.TransactionSynchronizationManager;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.reactivestreams.client.ClientSession;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
/**
* Helper class for managing reactive {@link MongoDatabase} instances via {@link ReactiveMongoDatabaseFactory}. Used for
* obtaining {@link ClientSession session bound} resources, such as {@link MongoDatabase} and {@link MongoCollection}
* suitable for transactional usage.
* <p />
* <strong>Note:</strong> Intended for internal usage only.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.2
*/
public class ReactiveMongoDatabaseUtils {
/**
* Check if the {@link ReactiveMongoDatabaseFactory} is actually bound to a
* {@link com.mongodb.reactivestreams.client.ClientSession} that has an active transaction, or if a
* {@link org.springframework.transaction.reactive.TransactionSynchronization} has been registered for the
* {@link ReactiveMongoDatabaseFactory resource} and if the associated
* {@link com.mongodb.reactivestreams.client.ClientSession} has an
* {@link com.mongodb.reactivestreams.client.ClientSession#hasActiveTransaction() active transaction}.
*
* @param databaseFactory the resource to check transactions for. Must not be {@literal null}.
* @return a {@link Mono} emitting {@literal true} if the factory has an ongoing transaction.
*/
public static Mono<Boolean> isTransactionActive(ReactiveMongoDatabaseFactory databaseFactory) {
if (databaseFactory.isTransactionActive()) {
return Mono.just(true);
}
return TransactionSynchronizationManager.forCurrentTransaction() //
.map(it -> {
ReactiveMongoResourceHolder holder = (ReactiveMongoResourceHolder) it.getResource(databaseFactory);
return holder != null && holder.hasActiveTransaction();
}) //
.onErrorResume(NoTransactionException.class, e -> Mono.just(false));
}
/**
* Obtain the default {@link MongoDatabase database} form the given {@link ReactiveMongoDatabaseFactory factory} using
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
* {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static Mono<MongoDatabase> getDatabase(ReactiveMongoDatabaseFactory factory) {
return doGetMongoDatabase(null, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the default {@link MongoDatabase database} form the given {@link ReactiveMongoDatabaseFactory factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
* {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link ReactiveMongoDatabaseFactory} 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 Mono<MongoDatabase> getDatabase(ReactiveMongoDatabaseFactory factory,
SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(null, factory, sessionSynchronization);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory
* factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
* {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static Mono<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory) {
return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory
* factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
* {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link ReactiveMongoDatabaseFactory} 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 Mono<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory,
SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(dbName, factory, sessionSynchronization);
}
private static Mono<MongoDatabase> doGetMongoDatabase(@Nullable String dbName, ReactiveMongoDatabaseFactory factory,
SessionSynchronization sessionSynchronization) {
Assert.notNull(factory, "DatabaseFactory must not be null!");
return TransactionSynchronizationManager.forCurrentTransaction()
.filter(TransactionSynchronizationManager::isSynchronizationActive) //
.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));
}
private static MongoDatabase getMongoDatabaseOrDefault(@Nullable String dbName,
ReactiveMongoDatabaseFactory factory) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}
private static Mono<ClientSession> doGetSession(TransactionSynchronizationManager synchronizationManager,
ReactiveMongoDatabaseFactory dbFactory, SessionSynchronization sessionSynchronization) {
final ReactiveMongoResourceHolder registeredHolder = (ReactiveMongoResourceHolder) synchronizationManager
.getResource(dbFactory);
// check for native MongoDB transaction
if (registeredHolder != null
&& (registeredHolder.hasSession() || registeredHolder.isSynchronizedWithTransaction())) {
return registeredHolder.hasSession() ? Mono.just(registeredHolder.getSession())
: createClientSession(dbFactory).map(registeredHolder::setSessionIfAbsent);
}
if (SessionSynchronization.ON_ACTUAL_TRANSACTION.equals(sessionSynchronization)) {
return Mono.empty();
}
// init a non native MongoDB transaction by registering a MongoSessionSynchronization
return createClientSession(dbFactory).map(session -> {
ReactiveMongoResourceHolder newHolder = new ReactiveMongoResourceHolder(session, dbFactory);
newHolder.getRequiredSession().startTransaction();
synchronizationManager
.registerSynchronization(new MongoSessionSynchronization(synchronizationManager, newHolder, dbFactory));
newHolder.setSynchronizedWithTransaction(true);
synchronizationManager.bindResource(dbFactory, newHolder);
return newHolder.getSession();
});
}
private static Mono<ClientSession> createClientSession(ReactiveMongoDatabaseFactory dbFactory) {
return dbFactory.getSession(ClientSessionOptions.builder().causallyConsistent(true).build());
}
/**
* MongoDB specific {@link ResourceHolderSynchronization} for resource cleanup at the end of a transaction when
* participating in a non-native MongoDB transaction, such as a R2CBC transaction.
*
* @author Mark Paluch
* @since 2.2
*/
private static class MongoSessionSynchronization
extends ReactiveResourceSynchronization<ReactiveMongoResourceHolder, Object> {
private final ReactiveMongoResourceHolder resourceHolder;
MongoSessionSynchronization(TransactionSynchronizationManager synchronizationManager,
ReactiveMongoResourceHolder resourceHolder, ReactiveMongoDatabaseFactory dbFactory) {
super(resourceHolder, dbFactory, synchronizationManager);
this.resourceHolder = resourceHolder;
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.ReactiveResourceSynchronization#shouldReleaseBeforeCompletion()
*/
@Override
protected boolean shouldReleaseBeforeCompletion() {
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.ReactiveResourceSynchronization#processResourceAfterCommit(java.lang.Object)
*/
@Override
protected Mono<Void> processResourceAfterCommit(ReactiveMongoResourceHolder resourceHolder) {
if (isTransactionActive(resourceHolder)) {
return Mono.from(resourceHolder.getRequiredSession().commitTransaction());
}
return Mono.empty();
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.ReactiveResourceSynchronization#afterCompletion(int)
*/
@Override
public Mono<Void> afterCompletion(int status) {
return Mono.defer(() -> {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK && isTransactionActive(this.resourceHolder)) {
return Mono.from(resourceHolder.getRequiredSession().abortTransaction()) //
.then(super.afterCompletion(status));
}
return super.afterCompletion(status);
});
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.ReactiveResourceSynchronization#releaseResource(java.lang.Object, java.lang.Object)
*/
@Override
protected Mono<Void> releaseResource(ReactiveMongoResourceHolder resourceHolder, Object resourceKey) {
return Mono.fromRunnable(() -> {
if (resourceHolder.hasActiveSession()) {
resourceHolder.getRequiredSession().close();
}
});
}
private boolean isTransactionActive(ReactiveMongoResourceHolder resourceHolder) {
if (!resourceHolder.hasSession()) {
return false;
}
return resourceHolder.getRequiredSession().hasActiveTransaction();
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright 2019-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.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.lang.Nullable;
import org.springframework.transaction.support.ResourceHolderSupport;
import com.mongodb.reactivestreams.client.ClientSession;
/**
* MongoDB specific resource holder, wrapping a {@link ClientSession}. {@link ReactiveMongoTransactionManager} binds
* instances of this class to the subscriber context.
* <p />
* <strong>Note:</strong> Intended for internal usage only.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.2
* @see ReactiveMongoTransactionManager
* @see ReactiveMongoTemplate
*/
class ReactiveMongoResourceHolder extends ResourceHolderSupport {
private @Nullable ClientSession session;
private ReactiveMongoDatabaseFactory databaseFactory;
/**
* 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}.
*/
ReactiveMongoResourceHolder(@Nullable ClientSession session, ReactiveMongoDatabaseFactory databaseFactory) {
this.session = session;
this.databaseFactory = databaseFactory;
}
/**
* @return the associated {@link ClientSession}. Can be {@literal null}.
*/
@Nullable
ClientSession getSession() {
return session;
}
/**
* @return the required associated {@link ClientSession}.
* @throws IllegalStateException if no session is associated.
*/
ClientSession getRequiredSession() {
ClientSession session = getSession();
if (session == null) {
throw new IllegalStateException("No ClientSession associated");
}
return session;
}
/**
* @return the associated {@link ReactiveMongoDatabaseFactory}.
*/
public ReactiveMongoDatabaseFactory getDatabaseFactory() {
return databaseFactory;
}
/**
* Set the {@link ClientSession} to guard.
*
* @param session can be {@literal null}.
*/
public void setSession(@Nullable ClientSession session) {
this.session = session;
}
/**
* @return {@literal true} if session is not {@literal null}.
*/
boolean hasSession() {
return session != null;
}
/**
* 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
*/
@Nullable
public ClientSession setSessionIfAbsent(@Nullable ClientSession session) {
if (!hasSession()) {
setSession(session);
}
return session;
}
/**
* @return {@literal true} if the session is active and has not been closed.
*/
boolean hasActiveSession() {
if (!hasSession()) {
return false;
}
return hasServerSession() && !getRequiredSession().getServerSession().isClosed();
}
/**
* @return {@literal true} if the session has an active transaction.
* @see #hasActiveSession()
*/
boolean hasActiveTransaction() {
if (!hasActiveSession()) {
return false;
}
return getRequiredSession().hasActiveTransaction();
}
/**
* @return {@literal true} if the {@link ClientSession} has a {@link com.mongodb.session.ServerSession} associated
* that is accessible via {@link ClientSession#getServerSession()}.
*/
boolean hasServerSession() {
try {
return getRequiredSession().getServerSession() != null;
} catch (IllegalStateException serverSessionClosed) {
// ignore
}
return false;
}
}

View File

@@ -1,530 +0,0 @@
/*
* Copyright 2019-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 reactor.core.publisher.Mono;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.reactive.AbstractReactiveTransactionManager;
import org.springframework.transaction.reactive.GenericReactiveTransaction;
import org.springframework.transaction.reactive.TransactionSynchronizationManager;
import org.springframework.transaction.support.SmartTransactionObject;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.MongoException;
import com.mongodb.TransactionOptions;
import com.mongodb.reactivestreams.client.ClientSession;
/**
* A {@link org.springframework.transaction.ReactiveTransactionManager} implementation that manages
* {@link com.mongodb.reactivestreams.client.ClientSession} based transactions for a single
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory}.
* <p />
* Binds a {@link ClientSession} from the specified
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory} to the subscriber
* {@link reactor.util.context.Context}.
* <p />
* {@link org.springframework.transaction.TransactionDefinition#isReadOnly() Readonly} transactions operate on a
* {@link ClientSession} and enable causal consistency, and also {@link ClientSession#startTransaction() start},
* {@link com.mongodb.reactivestreams.client.ClientSession#commitTransaction() commit} or
* {@link ClientSession#abortTransaction() abort} a transaction.
* <p />
* Application code is required to retrieve the {@link com.mongodb.reactivestreams.client.MongoDatabase} via
* {@link org.springframework.data.mongodb.ReactiveMongoDatabaseUtils#getDatabase(ReactiveMongoDatabaseFactory)} instead
* of a standard {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase()} call. Spring
* classes such as {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate} use this strategy implicitly.
* <p />
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. You can override
* {@link #doCommit(TransactionSynchronizationManager, ReactiveMongoTransactionObject)} to implement the
* <a href="https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation">Retry Commit Operation</a>
* behavior as outlined in the MongoDB reference manual.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.2
* @see <a href="https://www.mongodb.com/transactions">MongoDB Transaction Documentation</a>
* @see ReactiveMongoDatabaseUtils#getDatabase(ReactiveMongoDatabaseFactory, SessionSynchronization)
*/
public class ReactiveMongoTransactionManager extends AbstractReactiveTransactionManager implements InitializingBean {
private @Nullable ReactiveMongoDatabaseFactory databaseFactory;
private @Nullable TransactionOptions options;
/**
* Create a new {@link ReactiveMongoTransactionManager} for bean-style usage.
* <p />
* <strong>Note:</strong>The {@link org.springframework.data.mongodb.ReactiveMongoDatabaseFactory db factory} has to
* be {@link #setDatabaseFactory(ReactiveMongoDatabaseFactory)} set} before using the instance. Use this constructor
* to prepare a {@link ReactiveMongoTransactionManager} 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 #setDatabaseFactory(ReactiveMongoDatabaseFactory)
*/
public ReactiveMongoTransactionManager() {}
/**
* Create a new {@link ReactiveMongoTransactionManager} obtaining sessions from the given
* {@link ReactiveMongoDatabaseFactory}.
*
* @param databaseFactory must not be {@literal null}.
*/
public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory) {
this(databaseFactory, null);
}
/**
* Create a new {@link ReactiveMongoTransactionManager} obtaining sessions from the given
* {@link ReactiveMongoDatabaseFactory} applying the given {@link TransactionOptions options}, if present, when
* starting a new transaction.
*
* @param databaseFactory must not be {@literal null}.
* @param options can be {@literal null}.
*/
public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory,
@Nullable TransactionOptions options) {
Assert.notNull(databaseFactory, "DatabaseFactory must not be null!");
this.databaseFactory = databaseFactory;
this.options = options;
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doGetTransaction(org.springframework.transaction.reactive.TransactionSynchronizationManager)
*/
@Override
protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager)
throws TransactionException {
ReactiveMongoResourceHolder resourceHolder = (ReactiveMongoResourceHolder) synchronizationManager
.getResource(getRequiredDatabaseFactory());
return new ReactiveMongoTransactionObject(resourceHolder);
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#isExistingTransaction(java.lang.Object)
*/
@Override
protected boolean isExistingTransaction(Object transaction) throws TransactionException {
return extractMongoTransaction(transaction).hasResourceHolder();
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doBegin(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object, org.springframework.transaction.TransactionDefinition)
*/
@Override
protected Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction,
TransactionDefinition definition) throws TransactionException {
return Mono.defer(() -> {
ReactiveMongoTransactionObject mongoTransactionObject = extractMongoTransaction(transaction);
Mono<ReactiveMongoResourceHolder> holder = newResourceHolder(definition,
ClientSessionOptions.builder().causallyConsistent(true).build());
return holder.doOnNext(resourceHolder -> {
mongoTransactionObject.setResourceHolder(resourceHolder);
if (logger.isDebugEnabled()) {
logger.debug(
String.format("About to start transaction for session %s.", debugString(resourceHolder.getSession())));
}
}).doOnNext(resourceHolder -> {
mongoTransactionObject.startTransaction(options);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Started transaction for session %s.", debugString(resourceHolder.getSession())));
}
})//
.onErrorMap(
ex -> new TransactionSystemException(String.format("Could not start Mongo transaction for session %s.",
debugString(mongoTransactionObject.getSession())), ex))
.doOnSuccess(resourceHolder -> {
synchronizationManager.bindResource(getRequiredDatabaseFactory(), resourceHolder);
}).then();
});
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doSuspend(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object)
*/
@Override
protected Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager, Object transaction)
throws TransactionException {
return Mono.fromSupplier(() -> {
ReactiveMongoTransactionObject mongoTransactionObject = extractMongoTransaction(transaction);
mongoTransactionObject.setResourceHolder(null);
return synchronizationManager.unbindResource(getRequiredDatabaseFactory());
});
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doResume(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object, java.lang.Object)
*/
@Override
protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager, @Nullable Object transaction,
Object suspendedResources) {
return Mono
.fromRunnable(() -> synchronizationManager.bindResource(getRequiredDatabaseFactory(), suspendedResources));
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doCommit(org.springframework.transaction.reactive.TransactionSynchronizationManager, org.springframework.transaction.reactive.GenericReactiveTransaction)
*/
@Override
protected final Mono<Void> doCommit(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException {
return Mono.defer(() -> {
ReactiveMongoTransactionObject mongoTransactionObject = extractMongoTransaction(status);
if (logger.isDebugEnabled()) {
logger.debug(String.format("About to commit transaction for session %s.",
debugString(mongoTransactionObject.getSession())));
}
return doCommit(synchronizationManager, mongoTransactionObject).onErrorMap(ex -> {
return new TransactionSystemException(String.format("Could not commit Mongo transaction for session %s.",
debugString(mongoTransactionObject.getSession())), ex);
});
});
}
/**
* Customization hook to perform an actual commit of the given transaction.<br />
* If a commit operation encounters an error, the MongoDB driver throws a {@link MongoException} holding
* {@literal error labels}. <br />
* By default those labels are ignored, nevertheless one might check for
* {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the
* commit.
*
* @param synchronizationManager reactive synchronization manager.
* @param transactionObject never {@literal null}.
*/
protected Mono<Void> doCommit(TransactionSynchronizationManager synchronizationManager,
ReactiveMongoTransactionObject transactionObject) {
return transactionObject.commitTransaction();
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doRollback(org.springframework.transaction.reactive.TransactionSynchronizationManager, org.springframework.transaction.reactive.GenericReactiveTransaction)
*/
@Override
protected Mono<Void> doRollback(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) {
return Mono.defer(() -> {
ReactiveMongoTransactionObject mongoTransactionObject = extractMongoTransaction(status);
if (logger.isDebugEnabled()) {
logger.debug(String.format("About to abort transaction for session %s.",
debugString(mongoTransactionObject.getSession())));
}
return mongoTransactionObject.abortTransaction().onErrorResume(MongoException.class, ex -> {
return Mono
.error(new TransactionSystemException(String.format("Could not abort Mongo transaction for session %s.",
debugString(mongoTransactionObject.getSession())), ex));
});
});
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doSetRollbackOnly(org.springframework.transaction.reactive.TransactionSynchronizationManager, org.springframework.transaction.reactive.GenericReactiveTransaction)
*/
@Override
protected Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException {
return Mono.fromRunnable(() -> {
ReactiveMongoTransactionObject transactionObject = extractMongoTransaction(status);
transactionObject.getRequiredResourceHolder().setRollbackOnly();
});
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager#doCleanupAfterCompletion(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object)
*/
@Override
protected Mono<Void> doCleanupAfterCompletion(TransactionSynchronizationManager synchronizationManager,
Object transaction) {
Assert.isInstanceOf(ReactiveMongoTransactionObject.class, transaction,
() -> String.format("Expected to find a %s but it turned out to be %s.", ReactiveMongoTransactionObject.class,
transaction.getClass()));
return Mono.fromRunnable(() -> {
ReactiveMongoTransactionObject mongoTransactionObject = (ReactiveMongoTransactionObject) transaction;
// Remove the connection holder from the thread.
synchronizationManager.unbindResource(getRequiredDatabaseFactory());
mongoTransactionObject.getRequiredResourceHolder().clear();
if (logger.isDebugEnabled()) {
logger.debug(String.format("About to release Session %s after transaction.",
debugString(mongoTransactionObject.getSession())));
}
mongoTransactionObject.closeSession();
});
}
/**
* Set the {@link ReactiveMongoDatabaseFactory} that this instance should manage transactions for.
*
* @param databaseFactory must not be {@literal null}.
*/
public void setDatabaseFactory(ReactiveMongoDatabaseFactory databaseFactory) {
Assert.notNull(databaseFactory, "DatabaseFactory must not be null!");
this.databaseFactory = databaseFactory;
}
/**
* Set the {@link TransactionOptions} to be applied when starting transactions.
*
* @param options can be {@literal null}.
*/
public void setOptions(@Nullable TransactionOptions options) {
this.options = options;
}
/**
* Get the {@link ReactiveMongoDatabaseFactory} that this instance manages transactions for.
*
* @return can be {@literal null}.
*/
@Nullable
public ReactiveMongoDatabaseFactory getDatabaseFactory() {
return databaseFactory;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
getRequiredDatabaseFactory();
}
private Mono<ReactiveMongoResourceHolder> newResourceHolder(TransactionDefinition definition,
ClientSessionOptions options) {
ReactiveMongoDatabaseFactory dbFactory = getRequiredDatabaseFactory();
return dbFactory.getSession(options).map(session -> new ReactiveMongoResourceHolder(session, dbFactory));
}
/**
* @throws IllegalStateException if {@link #databaseFactory} is {@literal null}.
*/
private ReactiveMongoDatabaseFactory getRequiredDatabaseFactory() {
Assert.state(databaseFactory != null,
"ReactiveMongoTransactionManager operates upon a ReactiveMongoDatabaseFactory. Did you forget to provide one? It's required.");
return databaseFactory;
}
private static ReactiveMongoTransactionObject extractMongoTransaction(Object transaction) {
Assert.isInstanceOf(ReactiveMongoTransactionObject.class, transaction,
() -> String.format("Expected to find a %s but it turned out to be %s.", ReactiveMongoTransactionObject.class,
transaction.getClass()));
return (ReactiveMongoTransactionObject) transaction;
}
private static ReactiveMongoTransactionObject extractMongoTransaction(GenericReactiveTransaction status) {
Assert.isInstanceOf(ReactiveMongoTransactionObject.class, status.getTransaction(),
() -> String.format("Expected to find a %s but it turned out to be %s.", ReactiveMongoTransactionObject.class,
status.getTransaction().getClass()));
return (ReactiveMongoTransactionObject) status.getTransaction();
}
private static String debugString(@Nullable ClientSession session) {
if (session == null) {
return "null";
}
String debugString = String.format("[%s@%s ", ClassUtils.getShortName(session.getClass()),
Integer.toHexString(session.hashCode()));
try {
if (session.getServerSession() != null) {
debugString += String.format("id = %s, ", session.getServerSession().getIdentifier());
debugString += String.format("causallyConsistent = %s, ", session.isCausallyConsistent());
debugString += String.format("txActive = %s, ", session.hasActiveTransaction());
debugString += String.format("txNumber = %d, ", session.getServerSession().getTransactionNumber());
debugString += String.format("closed = %d, ", session.getServerSession().isClosed());
debugString += String.format("clusterTime = %s", session.getClusterTime());
} else {
debugString += "id = n/a";
debugString += String.format("causallyConsistent = %s, ", session.isCausallyConsistent());
debugString += String.format("txActive = %s, ", session.hasActiveTransaction());
debugString += String.format("clusterTime = %s", session.getClusterTime());
}
} catch (RuntimeException e) {
debugString += String.format("error = %s", e.getMessage());
}
debugString += "]";
return debugString;
}
/**
* MongoDB specific transaction object, representing a {@link MongoResourceHolder}. Used as transaction object by
* {@link ReactiveMongoTransactionManager}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.2
* @see ReactiveMongoResourceHolder
*/
protected static class ReactiveMongoTransactionObject implements SmartTransactionObject {
private @Nullable ReactiveMongoResourceHolder resourceHolder;
ReactiveMongoTransactionObject(@Nullable ReactiveMongoResourceHolder resourceHolder) {
this.resourceHolder = resourceHolder;
}
/**
* Set the {@link MongoResourceHolder}.
*
* @param resourceHolder can be {@literal null}.
*/
void setResourceHolder(@Nullable ReactiveMongoResourceHolder resourceHolder) {
this.resourceHolder = resourceHolder;
}
/**
* @return {@literal true} if a {@link MongoResourceHolder} is set.
*/
final boolean hasResourceHolder() {
return resourceHolder != null;
}
/**
* Start a MongoDB transaction optionally given {@link TransactionOptions}.
*
* @param options can be {@literal null}
*/
void startTransaction(@Nullable TransactionOptions options) {
ClientSession session = getRequiredSession();
if (options != null) {
session.startTransaction(options);
} else {
session.startTransaction();
}
}
/**
* Commit the transaction.
*/
public Mono<Void> commitTransaction() {
return Mono.from(getRequiredSession().commitTransaction());
}
/**
* Rollback (abort) the transaction.
*/
public Mono<Void> abortTransaction() {
return Mono.from(getRequiredSession().abortTransaction());
}
/**
* Close a {@link ClientSession} without regard to its transactional state.
*/
void closeSession() {
ClientSession session = getRequiredSession();
if (session.getServerSession() != null && !session.getServerSession().isClosed()) {
session.close();
}
}
@Nullable
public ClientSession getSession() {
return resourceHolder != null ? resourceHolder.getSession() : null;
}
private ReactiveMongoResourceHolder getRequiredResourceHolder() {
Assert.state(resourceHolder != null, "ReactiveMongoResourceHolder is required but not present. o_O");
return resourceHolder;
}
private ClientSession getRequiredSession() {
ClientSession session = getSession();
Assert.state(session != null, "A Session is required but it turned out to be null.");
return session;
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.support.SmartTransactionObject#isRollbackOnly()
*/
@Override
public boolean isRollbackOnly() {
return this.resourceHolder != null && this.resourceHolder.isRollbackOnly();
}
/*
* (non-Javadoc)
* @see org.springframework.transaction.support.SmartTransactionObject#flush()
*/
@Override
public void flush() {
throw new UnsupportedOperationException("flush() not supported");
}
}
}

View File

@@ -105,7 +105,6 @@ public abstract class AbstractMongoClientConfiguration extends MongoConfiguratio
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
return converter;
}

View File

@@ -42,10 +42,8 @@ import com.mongodb.MongoClient;
* @author Mark Paluch
* @see MongoConfigurationSupport
* @see AbstractMongoClientConfiguration
* @deprecated since 2.2 in favor of {@link AbstractMongoClientConfiguration}.
*/
@Configuration
@Deprecated
public abstract class AbstractMongoConfiguration extends MongoConfigurationSupport {
/**
@@ -113,7 +111,6 @@ public abstract class AbstractMongoConfiguration extends MongoConfigurationSuppo
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
return converter;
}

View File

@@ -83,7 +83,6 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(reactiveMongoDbFactory());
return converter;
}

View File

@@ -51,6 +51,7 @@ import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
@@ -101,6 +102,8 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
BeanDefinition conversionsDefinition = getCustomConversions(element, parserContext);
String ctxRef = potentiallyCreateMappingContext(element, parserContext, conversionsDefinition, id);
createIsNewStrategyFactoryBeanDefinition(ctxRef, parserContext, element);
// Need a reference to a Mongo instance
String dbFactoryRef = element.getAttribute("db-factory-ref");
if (!StringUtils.hasText(dbFactoryRef)) {
@@ -345,6 +348,20 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
return null;
}
public static String createIsNewStrategyFactoryBeanDefinition(String mappingContextRef, ParserContext context,
Element element) {
BeanDefinitionBuilder mappingContextStrategyFactoryBuilder = BeanDefinitionBuilder
.rootBeanDefinition(MappingContextIsNewStrategyFactory.class);
mappingContextStrategyFactoryBuilder.addConstructorArgReference(mappingContextRef);
BeanComponentDefinitionBuilder builder = new BeanComponentDefinitionBuilder(element, context);
context.registerBeanComponent(
builder.getComponent(mappingContextStrategyFactoryBuilder, IS_NEW_STRATEGY_FACTORY_BEAN_NAME));
return IS_NEW_STRATEGY_FACTORY_BEAN_NAME;
}
/**
* {@link TypeFilter} that returns {@literal false} in case any of the given delegates matches.
*

View File

@@ -18,7 +18,6 @@ package org.springframework.data.mongodb.config;
import static org.springframework.data.config.ParsingUtils.*;
import static org.springframework.data.mongodb.config.BeanNames.*;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -27,33 +26,25 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDefinitionParser;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} to register a {@link AuditingEntityCallback} to transparently set auditing information
* on an entity.
* {@link BeanDefinitionParser} to register a {@link AuditingEventListener} to transparently set auditing information on
* an entity.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public class MongoAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
MongoAuditingRegistrar.class.getClassLoader());
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return AuditingEntityCallback.class;
return AuditingEventListener.class;
}
/*
@@ -89,24 +80,7 @@ public class MongoAuditingBeanDefinitionParser extends AbstractSingleBeanDefinit
mappingContextRef);
parser.parse(element, parserContext);
AbstractBeanDefinition isNewAwareAuditingHandler = getObjectFactoryBeanDefinition(parser.getResolvedBeanName(),
parserContext.extractSource(element));
builder.addConstructorArgValue(isNewAwareAuditingHandler);
if (PROJECT_REACTOR_AVAILABLE) {
registerReactiveAuditingEntityCallback(parserContext.getRegistry(), isNewAwareAuditingHandler,
parserContext.extractSource(element));
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry,
AbstractBeanDefinition isNewAwareAuditingHandler, @Nullable Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(isNewAwareAuditingHandler);
builder.getRawBeanDefinition().setSource(source);
registry.registerBeanDefinition(ReactiveAuditingEntityCallback.class.getName(), builder.getBeanDefinition());
builder.addConstructorArgValue(getObjectFactoryBeanDefinition(parser.getResolvedBeanName(),
parserContext.extractSource(element)));
}
}

View File

@@ -32,23 +32,17 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMongoAuditing} annotation.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
*/
class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
MongoAuditingRegistrar.class.getClassLoader());
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
@@ -110,27 +104,12 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEntityCallback.class);
.rootBeanDefinition(AuditingEventListener.class);
listenerBeanDefinitionBuilder
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);
if (PROJECT_REACTOR_AVAILABLE) {
registerReactiveAuditingEntityCallback(registry, auditingHandlerDefinition.getSource());
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry, Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(source);
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
AuditingEventListener.class.getName(), registry);
}
/**

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.config;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -27,12 +28,17 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
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.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.support.CachingIsNewStrategyFactory;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -81,11 +87,23 @@ public abstract class MongoConfigurationSupport {
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());
return mappingContext;
}
/**
* Returns a {@link MappingContextIsNewStrategyFactory} wrapped into a {@link CachingIsNewStrategyFactory}.
*
* @return
* @throws ClassNotFoundException
*/
@Bean
public IsNewStrategyFactory isNewStrategyFactory() throws ClassNotFoundException {
return new CachingIsNewStrategyFactory(new MappingContextIsNewStrategyFactory(
new PersistentEntities(Arrays.<MappingContext<?, ?>> asList(new MappingContext[] { mongoMappingContext() }))));
}
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
@@ -172,16 +190,4 @@ public abstract class MongoConfigurationSupport {
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy()
: PropertyNameFieldNamingStrategy.INSTANCE;
}
/**
* 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}.
* @since 2.2
*/
protected boolean autoIndexCreation() {
return true;
}
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -27,7 +26,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoCredential;
@@ -80,23 +78,12 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
verifyUserNamePresent(userNameAndPassword);
credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0]));
} else if ("MONGODB-CR".equals(authMechanism)) {
} else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) {
verifyUsernameAndPasswordPresent(userNameAndPassword);
verifyDatabasePresent(database);
Method createCRCredentialMethod = ReflectionUtils.findMethod(MongoCredential.class,
"createMongoCRCredential", String.class, String.class, char[].class);
if (createCRCredentialMethod == null) {
throw new IllegalArgumentException("MONGODB-CR is no longer supported.");
}
MongoCredential credential = MongoCredential.class
.cast(ReflectionUtils.invokeMethod(createCRCredentialMethod, null, userNameAndPassword[0], database,
userNameAndPassword[1].toCharArray()));
credentials.add(credential);
credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database,
userNameAndPassword[1].toCharArray()));
} else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) {
verifyUserNamePresent(userNameAndPassword);

View File

@@ -35,7 +35,6 @@ import org.w3c.dom.Element;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@SuppressWarnings("deprecation")
abstract class MongoParsingUtils {
@@ -93,7 +92,6 @@ abstract class MongoParsingUtils {
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");
mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition());

View File

@@ -31,7 +31,6 @@ import com.mongodb.bulk.BulkWriteResult;
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Minsu Kim
* @since 1.9
*/
public interface BulkOperations {
@@ -136,29 +135,6 @@ public interface BulkOperations {
*/
BulkOperations remove(List<Query> removes);
/**
* Add a single replace operation to the bulk operation.
*
* @param query Update criteria.
* @param replacement the replacement document. Must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the replace added, will never be {@literal null}.
* @since 2.2
*/
default BulkOperations replaceOne(Query query, Object replacement) {
return replaceOne(query, replacement, FindAndReplaceOptions.empty());
}
/**
* Add a single replace operation to the bulk operation.
*
* @param query Update criteria.
* @param replacement the replacement document. Must not be {@literal null}.
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the replace added, will never be {@literal null}.
* @since 2.2
*/
BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options);
/**
* Execute all bulk operations using the default write concern.
*

View File

@@ -20,7 +20,6 @@ import lombok.EqualsAndHashCode;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -85,19 +84,8 @@ public class ChangeStreamEvent<T> {
@Nullable
public Instant getTimestamp() {
return getBsonTimestamp() != null ? converter.getConversionService().convert(raw.getClusterTime(), Instant.class)
: null;
}
/**
* Get the {@link ChangeStreamDocument#getClusterTime() cluster time}.
*
* @return can be {@literal null}.
* @since 2.2
*/
@Nullable
public BsonTimestamp getBsonTimestamp() {
return raw != null ? raw.getClusterTime() : null;
return raw != null && raw.getClusterTime() != null
? converter.getConversionService().convert(raw.getClusterTime(), Instant.class) : null;
}
/**

View File

@@ -21,17 +21,12 @@ import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;
import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.client.model.changestream.FullDocument;
@@ -52,8 +47,7 @@ public class ChangeStreamOptions {
private @Nullable BsonValue resumeToken;
private @Nullable FullDocument fullDocumentLookup;
private @Nullable Collation collation;
private @Nullable Object resumeTimestamp;
private Resume resume = Resume.UNDEFINED;
private @Nullable Instant resumeTimestamp;
protected ChangeStreamOptions() {}
@@ -89,31 +83,7 @@ public class ChangeStreamOptions {
* @return {@link Optional#empty()} if not set.
*/
public Optional<Instant> getResumeTimestamp() {
return Optional.ofNullable(resumeTimestamp).map(timestamp -> asTimestampOfType(timestamp, Instant.class));
}
/**
* @return {@link Optional#empty()} if not set.
* @since 2.2
*/
public Optional<BsonTimestamp> getResumeBsonTimestamp() {
return Optional.ofNullable(resumeTimestamp).map(timestamp -> asTimestampOfType(timestamp, BsonTimestamp.class));
}
/**
* @return {@literal true} if the change stream should be started after the {@link #getResumeToken() token}.
* @since 2.2
*/
public boolean isStartAfter() {
return Resume.START_AFTER.equals(resume);
}
/**
* @return {@literal true} if the change stream should be resumed after the {@link #getResumeToken() token}.
* @since 2.2
*/
public boolean isResumeAfter() {
return Resume.RESUME_AFTER.equals(resume);
return Optional.ofNullable(resumeTimestamp);
}
/**
@@ -133,48 +103,6 @@ public class ChangeStreamOptions {
return new ChangeStreamOptionsBuilder();
}
private static <T> T asTimestampOfType(Object timestamp, Class<T> targetType) {
return targetType.cast(doGetTimestamp(timestamp, targetType));
}
private static <T> Object doGetTimestamp(Object timestamp, Class<T> targetType) {
if (ClassUtils.isAssignableValue(targetType, timestamp)) {
return timestamp;
}
if (timestamp instanceof Instant) {
return new BsonTimestamp((int) ((Instant) timestamp).getEpochSecond(), 0);
}
if (timestamp instanceof BsonTimestamp) {
return Instant.ofEpochSecond(((BsonTimestamp) timestamp).getTime());
}
throw new IllegalArgumentException(
"o_O that should actually not happen. The timestamp should be an Instant or a BsonTimestamp but was "
+ ObjectUtils.nullSafeClassName(timestamp));
}
/**
* @author Christoph Strobl
* @since 2.2
*/
enum Resume {
UNDEFINED,
/**
* @see com.mongodb.client.ChangeStreamIterable#startAfter(BsonDocument)
*/
START_AFTER,
/**
* @see com.mongodb.client.ChangeStreamIterable#resumeAfter(BsonDocument)
*/
RESUME_AFTER
}
/**
* Builder for creating {@link ChangeStreamOptions}.
*
@@ -187,8 +115,7 @@ public class ChangeStreamOptions {
private @Nullable BsonValue resumeToken;
private @Nullable FullDocument fullDocumentLookup;
private @Nullable Collation collation;
private @Nullable Object resumeTimestamp;
private Resume resume = Resume.UNDEFINED;
private @Nullable Instant resumeTimestamp;
private ChangeStreamOptionsBuilder() {}
@@ -256,11 +183,6 @@ public class ChangeStreamOptions {
Assert.notNull(resumeToken, "ResumeToken must not be null!");
this.resumeToken = resumeToken;
if (this.resume == Resume.UNDEFINED) {
this.resume = Resume.RESUME_AFTER;
}
return this;
}
@@ -302,51 +224,6 @@ public class ChangeStreamOptions {
return this;
}
/**
* Set the cluster time to resume from.
*
* @param resumeTimestamp must not be {@literal null}.
* @return this.
* @since 2.2
*/
public ChangeStreamOptionsBuilder resumeAt(BsonTimestamp resumeTimestamp) {
Assert.notNull(resumeTimestamp, "ResumeTimestamp must not be null!");
this.resumeTimestamp = resumeTimestamp;
return this;
}
/**
* Set the resume token after which to continue emitting notifications.
*
* @param resumeToken must not be {@literal null}.
* @return this.
* @since 2.2
*/
public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) {
resumeToken(resumeToken);
this.resume = Resume.RESUME_AFTER;
return this;
}
/**
* Set the resume token after which to start emitting notifications.
*
* @param resumeToken must not be {@literal null}.
* @return this.
* @since 2.2
*/
public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) {
resumeToken(resumeToken);
this.resume = Resume.START_AFTER;
return this;
}
/**
* @return the built {@link ChangeStreamOptions}
*/
@@ -354,12 +231,11 @@ public class ChangeStreamOptions {
ChangeStreamOptions options = new ChangeStreamOptions();
options.filter = this.filter;
options.resumeToken = this.resumeToken;
options.fullDocumentLookup = this.fullDocumentLookup;
options.collation = this.collation;
options.resumeTimestamp = this.resumeTimestamp;
options.resume = this.resume;
options.filter = filter;
options.resumeToken = resumeToken;
options.fullDocumentLookup = fullDocumentLookup;
options.collation = collation;
options.resumeTimestamp = resumeTimestamp;
return options;
}

View File

@@ -1,227 +0,0 @@
/*
* Copyright 2019-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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.springframework.data.geo.Point;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Value object representing a count query. Count queries using {@code $near} or {@code $nearSphere} require a rewrite
* to {@code $geoWithin}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
class CountQuery {
private Document source;
private CountQuery(Document source) {
this.source = source;
}
public static CountQuery of(Document source) {
return new CountQuery(source);
}
/**
* Returns the query {@link Document} that can be used with {@code countDocuments()}. Potentially rewrites the query
* to be usable with {@code countDocuments()}.
*
* @return the query {@link Document} that can be used with {@code countDocuments()}.
*/
public Document toQueryDocument() {
if (!requiresRewrite(source)) {
return source;
}
Document target = new Document();
for (Map.Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Document && requiresRewrite(entry.getValue())) {
Document theValue = (Document) entry.getValue();
target.putAll(createGeoWithin(entry.getKey(), theValue, source.get("$and")));
continue;
}
if (entry.getValue() instanceof Collection && requiresRewrite(entry.getValue())) {
Collection<?> source = (Collection<?>) entry.getValue();
target.put(entry.getKey(), rewriteCollection(source));
continue;
}
if ("$and".equals(entry.getKey()) && target.containsKey("$and")) {
// Expect $and to be processed with Document and createGeoWithin.
continue;
}
target.put(entry.getKey(), entry.getValue());
}
return target;
}
/**
* @param valueToInspect
* @return {@code true} if the enclosing element needs to be rewritten.
*/
private boolean requiresRewrite(Object valueToInspect) {
if (valueToInspect instanceof Document) {
return requiresRewrite((Document) valueToInspect);
}
if (valueToInspect instanceof Collection) {
return requiresRewrite((Collection) valueToInspect);
}
return false;
}
private boolean requiresRewrite(Collection<?> collection) {
for (Object o : collection) {
if (o instanceof Document && requiresRewrite((Document) o)) {
return true;
}
}
return false;
}
private boolean requiresRewrite(Document document) {
if (containsNear(document)) {
return true;
}
for (Object entry : document.values()) {
if (requiresRewrite(entry)) {
return true;
}
}
return false;
}
private Collection<Object> rewriteCollection(Collection<?> source) {
Collection<Object> rewrittenCollection = new ArrayList<>(source.size());
for (Object item : source) {
if (item instanceof Document && requiresRewrite(item)) {
rewrittenCollection.add(CountQuery.of((Document) item).toQueryDocument());
} else {
rewrittenCollection.add(item);
}
}
return rewrittenCollection;
}
/**
* Rewrite the near query for field {@code key} to {@code $geoWithin}.
*
* @param key the queried field.
* @param source source {@link Document}.
* @param $and potentially existing {@code $and} condition.
* @return the rewritten query {@link Document}.
*/
private static Document createGeoWithin(String key, Document source, @Nullable Object $and) {
boolean spheric = source.containsKey("$nearSphere");
Object $near = spheric ? source.get("$nearSphere") : source.get("$near");
Number maxDistance = source.containsKey("$maxDistance") ? (Number) source.get("$maxDistance") : Double.MAX_VALUE;
List<Object> $centerMax = Arrays.asList(toCenterCoordinates($near), maxDistance);
Document $geoWithinMax = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMax));
if (!containsNearWithMinDistance(source)) {
return new Document(key, $geoWithinMax);
}
Number minDistance = (Number) source.get("$minDistance");
List<Object> $centerMin = Arrays.asList(toCenterCoordinates($near), minDistance);
Document $geoWithinMin = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMin));
List<Document> criteria = new ArrayList<>();
if ($and != null) {
if ($and instanceof Collection) {
criteria.addAll((Collection) $and);
} else {
throw new IllegalArgumentException(
"Cannot rewrite query as it contains an '$and' element that is not a Collection!: Offending element: "
+ $and);
}
}
criteria.add(new Document("$nor", Collections.singletonList(new Document(key, $geoWithinMin))));
criteria.add(new Document(key, $geoWithinMax));
return new Document("$and", criteria);
}
private static boolean containsNear(Document source) {
return source.containsKey("$near") || source.containsKey("$nearSphere");
}
private static boolean containsNearWithMinDistance(Document source) {
if (!containsNear(source)) {
return false;
}
return source.containsKey("$minDistance");
}
private static Object toCenterCoordinates(Object value) {
if (ObjectUtils.isArray(value)) {
return value;
}
if (value instanceof Point) {
return Arrays.asList(((Point) value).getX(), ((Point) value).getY());
}
if (value instanceof Document && ((Document) value).containsKey("x")) {
Document point = (Document) value;
return Arrays.asList(point.get("x"), point.get("y"));
}
return value;
}
}

View File

@@ -15,15 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import java.util.function.Function;
import org.bson.Document;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.ReadPreference;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
/**
* Simple callback interface to allow customization of a {@link FindIterable}.
@@ -31,14 +25,7 @@ import com.mongodb.client.MongoCollection;
* @author Oliver Gierke
* @author Christoph Strobl
*/
public interface CursorPreparer extends ReadPreferenceAware {
/**
* Default {@link CursorPreparer} just passing on the given {@link FindIterable}.
*
* @since 2.2
*/
CursorPreparer NO_OP_PREPARER = (iterable -> iterable);
public interface CursorPreparer {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
@@ -46,37 +33,4 @@ public interface CursorPreparer extends ReadPreferenceAware {
* @param cursor
*/
FindIterable<Document> prepare(FindIterable<Document> cursor);
/**
* Apply query specific settings to {@link MongoCollection} and initate a find operation returning a
* {@link FindIterable} via the given {@link Function find} function.
*
* @param collection must not be {@literal null}.
* @param find must not be {@literal null}.
* @return
* @throws IllegalArgumentException if one of the required arguments is {@literal null}.
* @since 2.2
*/
default FindIterable<Document> initiateFind(MongoCollection<Document> collection,
Function<MongoCollection<Document>, FindIterable<Document>> find) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(find, "Find function must not be null!");
if (hasReadPreference()) {
collection = collection.withReadPreference(getReadPreference());
}
return prepare(find.apply(collection));
}
/**
* @return the {@link ReadPreference} to apply or {@literal null} if none defined.
* @since 2.2
*/
@Override
@Nullable
default ReadPreference getReadPreference() {
return null;
}
}

View File

@@ -26,31 +26,30 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
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.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.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.query.Collation;
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 org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.util.Pair;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.BulkWriteException;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.*;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.DeleteManyModel;
import com.mongodb.client.model.DeleteOneModel;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.UpdateManyModel;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
/**
* Default implementation for {@link BulkOperations}.
@@ -59,8 +58,6 @@ import com.mongodb.client.model.*;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Minsu Kim
* @author Jens Schauder
* @author Michail Nikolaev
* @since 1.9
*/
@@ -69,7 +66,7 @@ class DefaultBulkOperations implements BulkOperations {
private final MongoOperations mongoOperations;
private final String collectionName;
private final BulkOperationContext bulkOperationContext;
private final List<SourceAwareWriteModelHolder> models = new ArrayList<>();
private final List<WriteModel<Document>> models = new ArrayList<>();
private PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern defaultWriteConcern;
@@ -126,9 +123,16 @@ class DefaultBulkOperations implements BulkOperations {
Assert.notNull(document, "Document must not be null!");
maybeEmitEvent(new BeforeConvertEvent<>(document, collectionName));
Object source = maybeInvokeBeforeConvertCallback(document);
addModel(source, new InsertOneModel<>(getMappedObject(source)));
if (document instanceof Document) {
models.add(new InsertOneModel<>((Document) document));
return this;
}
Document sink = new Document();
mongoOperations.getConverter().write(document, sink);
models.add(new InsertOneModel<>(sink));
return this;
}
@@ -242,7 +246,7 @@ class DefaultBulkOperations implements BulkOperations {
DeleteOptions deleteOptions = new DeleteOptions();
query.getCollation().map(Collation::toMongoCollation).ifPresent(deleteOptions::collation);
addModel(query, new DeleteManyModel<>(query.getQueryObject(), deleteOptions));
models.add(new DeleteManyModel<>(query.getQueryObject(), deleteOptions));
return this;
}
@@ -263,29 +267,6 @@ class DefaultBulkOperations implements BulkOperations {
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.BulkOperations#replaceOne(org.springframework.data.mongodb.core.query.Query, java.lang.Object, org.springframework.data.mongodb.core.FindAndReplaceOptions)
*/
@Override
public BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) {
Assert.notNull(query, "Query must not be null!");
Assert.notNull(replacement, "Replacement must not be null!");
Assert.notNull(options, "Options must not be null!");
ReplaceOptions replaceOptions = new ReplaceOptions();
replaceOptions.upsert(options.isUpsert());
query.getCollation().map(Collation::toMongoCollation).ifPresent(replaceOptions::collation);
maybeEmitEvent(new BeforeConvertEvent<>(replacement, collectionName));
Object source = maybeInvokeBeforeConvertCallback(replacement);
addModel(source,
new ReplaceOneModel<>(getMappedQuery(query.getQueryObject()), getMappedObject(source), replaceOptions));
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.BulkOperations#executeBulk()
@@ -295,48 +276,18 @@ class DefaultBulkOperations implements BulkOperations {
try {
com.mongodb.bulk.BulkWriteResult result = mongoOperations.execute(collectionName, this::bulkWriteTo);
return mongoOperations.execute(collectionName, collection -> {
Assert.state(result != null, "Result must not be null.");
models.forEach(this::maybeEmitAfterSaveEvent);
return result;
if (defaultWriteConcern != null) {
collection = collection.withWriteConcern(defaultWriteConcern);
}
return collection.bulkWrite(models.stream().map(this::mapWriteModel).collect(Collectors.toList()), bulkOptions);
});
} finally {
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
}
}
private BulkWriteResult bulkWriteTo(MongoCollection<Document> collection) {
if (defaultWriteConcern != null) {
collection = collection.withWriteConcern(defaultWriteConcern);
}
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
}
private WriteModel<Document> extractAndMapWriteModel(SourceAwareWriteModelHolder it) {
maybeEmitBeforeSaveEvent(it);
if (it.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeInvokeBeforeSaveCallback(it.getSource(), target);
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeInvokeBeforeSaveCallback(it.getSource(), target);
}
return mapWriteModel(it.getModel());
}
/**
* Performs update and upsert bulk operations.
*
@@ -351,12 +302,14 @@ class DefaultBulkOperations implements BulkOperations {
Assert.notNull(query, "Query must not be null!");
Assert.notNull(update, "Update must not be null!");
UpdateOptions options = computeUpdateOptions(query, update, upsert);
UpdateOptions options = new UpdateOptions();
options.upsert(upsert);
query.getCollation().map(Collation::toMongoCollation).ifPresent(options::collation);
if (multi) {
addModel(update, new UpdateManyModel<>(query.getQueryObject(), update.getUpdateObject(), options));
models.add(new UpdateManyModel<>(query.getQueryObject(), update.getUpdateObject(), options));
} else {
addModel(update, new UpdateOneModel<>(query.getQueryObject(), update.getUpdateObject(), options));
models.add(new UpdateOneModel<>(query.getQueryObject(), update.getUpdateObject(), options));
}
return this;
@@ -405,76 +358,6 @@ class DefaultBulkOperations implements BulkOperations {
return bulkOperationContext.getQueryMapper().getMappedObject(query, bulkOperationContext.getEntity());
}
private Document getMappedObject(Object source) {
if (source instanceof Document) {
return (Document) source;
}
Document sink = new Document();
mongoOperations.getConverter().write(source, sink);
return sink;
}
private void addModel(Object source, WriteModel<Document> model) {
models.add(new SourceAwareWriteModelHolder(source, model));
}
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder it) {
if (it.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 = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(it.getSource(), target, collectionName));
}
}
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder it) {
if (it.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 = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(it.getSource(), target, collectionName));
}
}
private <E extends MongoMappingEvent<T>, T> E maybeEmitEvent(E event) {
if (null != bulkOperationContext.getEventPublisher()) {
bulkOperationContext.getEventPublisher().publishEvent(event);
}
return event;
}
private Object maybeInvokeBeforeConvertCallback(Object value) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(BeforeConvertCallback.class, value, collectionName);
}
private Object maybeInvokeBeforeSaveCallback(Object value, Document mappedDocument) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(BeforeSaveCallback.class, value, mappedDocument,
collectionName);
}
private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
BulkWriteOptions options = new BulkWriteOptions();
@@ -489,29 +372,6 @@ class DefaultBulkOperations implements BulkOperations {
throw new IllegalStateException("BulkMode was null!");
}
/**
* @param filterQuery The {@link Query} to read a potential {@link Collation} from. Must not be {@literal null}.
* @param update The {@link Update} to apply
* @param upsert flag to indicate if document should be upserted.
* @return new instance of {@link UpdateOptions}.
*/
private static UpdateOptions computeUpdateOptions(Query filterQuery, UpdateDefinition update, boolean upsert) {
UpdateOptions options = new UpdateOptions();
options.upsert(upsert);
if (update.hasArrayFilters()) {
List<Document> list = new ArrayList<>(update.getArrayFilters().size());
for (ArrayFilter arrayFilter : update.getArrayFilters()) {
list.add(arrayFilter.asDocument());
}
options.arrayFilters(list);
}
filterQuery.getCollation().map(Collation::toMongoCollation).ifPresent(options::collation);
return options;
}
/**
* {@link BulkOperationContext} holds information about
* {@link org.springframework.data.mongodb.core.BulkOperations.BulkMode} the entity in use as well as references to
@@ -527,20 +387,5 @@ class DefaultBulkOperations implements BulkOperations {
@NonNull Optional<? extends MongoPersistentEntity<?>> entity;
@NonNull QueryMapper queryMapper;
@NonNull UpdateMapper updateMapper;
ApplicationEventPublisher eventPublisher;
EntityCallbacks entityCallbacks;
}
/**
* Value object chaining together an actual source with its {@link WriteModel} representation.
*
* @since 2.2
* @author Christoph Strobl
*/
@Value
private static class SourceAwareWriteModelHolder {
Object source;
WriteModel<Document> model;
}
}

View File

@@ -120,15 +120,19 @@ public class DefaultIndexOperations implements IndexOperations {
return execute(collection -> {
MongoPersistentEntity<?> entity = lookupPersistentEntity(type, collectionName);
Document indexOptions = indexDefinition.getIndexOptions();
IndexOptions indexOptions = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
indexOptions = addPartialFilterIfPresent(indexOptions, indexDefinition.getIndexOptions(), entity);
indexOptions = addDefaultCollationIfRequired(indexOptions, entity);
if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
Document mappedKeys = mapper.getMappedSort(indexDefinition.getIndexKeys(), entity);
return collection.createIndex(mappedKeys, indexOptions);
Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
ops.partialFilterExpression(mapper.getMappedObject((Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
lookupPersistentEntity(type, collectionName)));
}
return collection.createIndex(indexDefinition.getIndexKeys(), ops);
});
}
@@ -188,7 +192,7 @@ public class DefaultIndexOperations implements IndexOperations {
private List<IndexInfo> getIndexData(MongoCursor<Document> cursor) {
List<IndexInfo> indexInfoList = new ArrayList<>();
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
while (cursor.hasNext()) {
@@ -213,25 +217,4 @@ public class DefaultIndexOperations implements IndexOperations {
return mongoOperations.execute(collectionName, callback);
}
private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document sourceOptions,
@Nullable MongoPersistentEntity<?> entity) {
if (!sourceOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
return ops;
}
Assert.isInstanceOf(Document.class, sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
return ops.partialFilterExpression(
mapper.getMappedSort((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity));
}
private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops, MongoPersistentEntity<?> entity) {
if (ops.getCollation() != null || entity == null || !entity.hasCollation()) {
return ops;
}
return ops.collation(entity.getCollation().toMongoCollation());
}
}

View File

@@ -94,16 +94,23 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
return mongoOperations.execute(collectionName, collection -> {
MongoPersistentEntity<?> entity = type
.map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val))
.orElseGet(() -> lookupPersistentEntity(collectionName));
Document indexOptions = indexDefinition.getIndexOptions();
IndexOptions indexOptions = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
indexOptions = addPartialFilterIfPresent(indexOptions, indexDefinition.getIndexOptions(), entity);
indexOptions = addDefaultCollationIfRequired(indexOptions, entity);
if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
return collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
MongoPersistentEntity<?> entity = type
.map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val))
.orElseGet(() -> lookupPersistentEntity(collectionName));
ops = ops.partialFilterExpression(
queryMapper.getMappedObject(indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY, Document.class), entity));
}
return collection.createIndex(indexDefinition.getIndexKeys(), ops);
}).next();
}
@@ -119,24 +126,21 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
.orElse(null);
}
/*
* (non-Javadoc)
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#dropIndex(java.lang.String)
*/
public Mono<Void> dropIndex(final String name) {
return mongoOperations.execute(collectionName, collection -> collection.dropIndex(name)).then();
}
/*
* (non-Javadoc)
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#dropAllIndexes()
*/
public Mono<Void> dropAllIndexes() {
return dropIndex("*");
}
/*
* (non-Javadoc)
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.ReactiveIndexOperations#getIndexInfo()
*/
public Flux<IndexInfo> getIndexInfo() {
@@ -144,25 +148,4 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
return mongoOperations.execute(collectionName, collection -> collection.listIndexes(Document.class)) //
.map(IndexConverters.documentToIndexInfoConverter()::convert);
}
private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document sourceOptions,
@Nullable MongoPersistentEntity<?> entity) {
if (!sourceOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) {
return ops;
}
Assert.isInstanceOf(Document.class, sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
return ops.partialFilterExpression(
queryMapper.getMappedObject((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity));
}
private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops, MongoPersistentEntity<?> entity) {
if (ops.getCollation() != null || entity == null || !entity.hasCollation()) {
return ops;
}
return ops.collation(entity.getCollation().toMongoCollation());
}
}

View File

@@ -42,15 +42,13 @@ import com.mongodb.MongoException;
import com.mongodb.client.MongoDatabase;
/**
* Default implementation of {@link ScriptOperations} capable of saving and executing {@link ExecutableMongoScript}.
* Default implementation of {@link ScriptOperations} capable of saving and executing {@link ServerSideJavaScript}.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.7
* @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0.
*/
@Deprecated
class DefaultScriptOperations implements ScriptOperations {
private static final String SCRIPT_COLLECTION_NAME = "system.js";

View File

@@ -21,14 +21,12 @@ import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import org.bson.Document;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -36,15 +34,15 @@ import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import com.mongodb.util.JSONParseException;
/**
* Common operations performed on an entity in the context of it's mapping metadata.
*
@@ -153,59 +151,15 @@ class EntityOperations {
return ID_FIELD;
}
/**
* Return the name used for {@code $geoNear.distanceField} avoiding clashes with potentially existing properties.
*
* @param domainType must not be {@literal null}.
* @return the name of the distanceField to use. {@literal dis} by default.
* @since 2.2
*/
public String nearQueryDistanceFieldName(Class<?> domainType) {
MongoPersistentEntity<?> persistentEntity = context.getPersistentEntity(domainType);
if (persistentEntity == null || persistentEntity.getPersistentProperty("dis") == null) {
return "dis";
}
String distanceFieldName = "calculated-distance";
int counter = 0;
while (persistentEntity.getPersistentProperty(distanceFieldName) != null) {
distanceFieldName += "-" + (counter++);
}
return distanceFieldName;
}
private static Document parse(String source) {
try {
return Document.parse(source);
} catch (org.bson.json.JsonParseException o_O) {
} catch (JSONParseException | org.bson.json.JsonParseException o_O) {
throw new MappingException("Could not parse given String to save into a JSON document!", o_O);
} catch (RuntimeException o_O) {
// legacy 3.x exception
if (ClassUtils.matchesTypeName(o_O.getClass(), "JSONParseException")) {
throw new MappingException("Could not parse given String to save into a JSON document!", o_O);
}
throw o_O;
}
}
public <T> TypedOperations<T> forType(@Nullable Class<T> entityClass) {
if (entityClass != null) {
MongoPersistentEntity<?> entity = context.getPersistentEntity(entityClass);
if (entity != null) {
return new TypedEntityOperations(entity);
}
}
return UntypedOperations.instance();
}
/**
* A representation of information about an entity.
*
@@ -235,16 +189,6 @@ class EntityOperations {
*/
Query getByIdQuery();
/**
* Returns the {@link Query} to remove an entity by its {@literal id} and if applicable {@literal version}.
*
* @return the {@link Query} to use for removing the entity. Never {@literal null}.
* @since 2.2
*/
default Query getRemoveByQuery() {
return isVersionedEntity() ? getQueryForVersion() : getByIdQuery();
}
/**
* Returns the {@link Query} to find the entity in its current version.
*
@@ -275,11 +219,9 @@ class EntityOperations {
}
/**
* Returns the value of the version if the entity {@link #isVersionedEntity() has a version property}.
* Returns the value of the version if the entity has a version property, {@literal null} otherwise.
*
* @return the entity version. Can be {@literal null}.
* @throws IllegalStateException if the entity does not define a {@literal version} property. Make sure to check
* {@link #isVersionedEntity()}.
* @return
*/
@Nullable
Object getVersion();
@@ -293,7 +235,7 @@ class EntityOperations {
/**
* Returns whether the entity is considered to be new.
*
*
* @return
* @since 2.1.2
*/
@@ -335,8 +277,8 @@ class EntityOperations {
/**
* Returns the current version value if the entity has a version property.
*
* @return the current version or {@literal null} in case it's uninitialized.
* @throws IllegalStateException if the entity does not define a {@literal version} property.
* @return the current version or {@literal null} in case it's uninitialized or the entity doesn't expose a version
* property.
*/
@Nullable
Number getVersion();
@@ -444,7 +386,7 @@ class EntityOperations {
return map;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.Entity#isNew()
*/
@@ -538,10 +480,10 @@ class EntityOperations {
public Query getQueryForVersion() {
MongoPersistentProperty idProperty = entity.getRequiredIdProperty();
MongoPersistentProperty versionProperty = entity.getRequiredVersionProperty();
MongoPersistentProperty property = entity.getRequiredVersionProperty();
return new Query(Criteria.where(idProperty.getName()).is(getId())//
.and(versionProperty.getName()).is(getVersion()));
.and(property.getName()).is(getVersion()));
}
/*
@@ -615,7 +557,7 @@ class EntityOperations {
return propertyAccessor.getBean();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.Entity#isNew()
*/
@@ -725,102 +667,4 @@ class EntityOperations {
return propertyAccessor.getBean();
}
}
/**
* Type-specific operations abstraction.
*
* @author Mark Paluch
* @param <T>
* @since 2.2
*/
interface TypedOperations<T> {
/**
* Return the optional {@link Collation} for the underlying entity.
*
* @return
*/
Optional<Collation> getCollation();
/**
* Return the optional {@link Collation} from the given {@link Query} and fall back to the collation configured for
* the underlying entity.
*
* @return
*/
Optional<Collation> getCollation(Query query);
}
/**
* {@link TypedOperations} for generic entities that are not represented with {@link PersistentEntity} (e.g. custom
* conversions).
*/
@RequiredArgsConstructor
enum UntypedOperations implements TypedOperations<Object> {
INSTANCE;
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> TypedOperations<T> instance() {
return (TypedOperations) INSTANCE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.TypedOperations#getCollation()
*/
@Override
public Optional<Collation> getCollation() {
return Optional.empty();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.TypedOperations#getCollation(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Optional<Collation> getCollation(Query query) {
if (query == null) {
return Optional.empty();
}
return query.getCollation();
}
}
/**
* {@link TypedOperations} backed by {@link MongoPersistentEntity}.
*
* @param <T>
*/
@RequiredArgsConstructor
static class TypedEntityOperations<T> implements TypedOperations<T> {
private final @NonNull MongoPersistentEntity<T> entity;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.TypedOperations#getCollation()
*/
@Override
public Optional<Collation> getCollation() {
return Optional.ofNullable(entity.getCollation());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.TypedOperations#getCollation(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Optional<Collation> getCollation(Query query) {
if (query.getCollation().isPresent()) {
return query.getCollation();
}
return Optional.ofNullable(entity.getCollation());
}
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.ReadPreference;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@@ -268,11 +267,6 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
this.limit = Optional.of(limit);
return this;
}
@Override
public ReadPreference getReadPreference() {
return delegate.getReadPreference();
}
}
/**

View File

@@ -17,10 +17,8 @@ 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.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import com.mongodb.client.result.UpdateResult;
@@ -153,16 +151,13 @@ public interface ExecutableUpdateOperation {
interface UpdateWithUpdate<T> {
/**
* Set the {@link UpdateDefinition} to be applied.
* Set the {@link Update} to be applied.
*
* @param update must not be {@literal null}.
* @return new instance of {@link TerminatingUpdate}.
* @throws IllegalArgumentException if update is {@literal null}.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
TerminatingUpdate<T> apply(UpdateDefinition update);
TerminatingUpdate<T> apply(Update update);
/**
* Specify {@code replacement} object.

View File

@@ -21,7 +21,7 @@ import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -67,7 +67,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
@NonNull MongoTemplate template;
@NonNull Class domainType;
Query query;
@Nullable UpdateDefinition update;
@Nullable Update update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@@ -76,10 +76,10 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.UpdateDefinition)
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithUpdate#apply(Update)
*/
@Override
public TerminatingUpdate<T> apply(UpdateDefinition update) {
public TerminatingUpdate<T> apply(Update update) {
Assert.notNull(update, "Update must not be null!");

View File

@@ -33,31 +33,6 @@ public class FindAndModifyOptions {
private @Nullable Collation collation;
private static final FindAndModifyOptions NONE = new FindAndModifyOptions() {
private static final String ERROR_MSG = "FindAndModifyOptions.none() cannot be changed. Please use FindAndModifyOptions.options() instead.";
@Override
public FindAndModifyOptions returnNew(boolean returnNew) {
throw new UnsupportedOperationException(ERROR_MSG);
}
@Override
public FindAndModifyOptions upsert(boolean upsert) {
throw new UnsupportedOperationException(ERROR_MSG);
}
@Override
public FindAndModifyOptions remove(boolean remove) {
throw new UnsupportedOperationException(ERROR_MSG);
}
@Override
public FindAndModifyOptions collation(@Nullable Collation collation) {
throw new UnsupportedOperationException(ERROR_MSG);
}
};
/**
* Static factory method to create a FindAndModifyOptions instance
*
@@ -67,19 +42,9 @@ public class FindAndModifyOptions {
return new FindAndModifyOptions();
}
/**
* Static factory method returning an unmodifiable {@link FindAndModifyOptions} instance.
*
* @return unmodifiable {@link FindAndModifyOptions} instance.
* @since 2.2
*/
public static FindAndModifyOptions none() {
return NONE;
}
/**
* Create new {@link FindAndModifyOptions} based on option of given {@litearl source}.
*
*
* @param source can be {@literal null}.
* @return new instance of {@link FindAndModifyOptions}.
* @since 2.0

View File

@@ -36,21 +36,6 @@ public class FindAndReplaceOptions {
private boolean returnNew;
private boolean upsert;
private static final FindAndReplaceOptions NONE = new FindAndReplaceOptions() {
private static final String ERROR_MSG = "FindAndReplaceOptions.none() cannot be changed. Please use FindAndReplaceOptions.options() instead.";
@Override
public FindAndReplaceOptions returnNew() {
throw new UnsupportedOperationException(ERROR_MSG);
}
@Override
public FindAndReplaceOptions upsert() {
throw new UnsupportedOperationException(ERROR_MSG);
}
};
/**
* Static factory method to create a {@link FindAndReplaceOptions} instance.
* <dl>
@@ -66,16 +51,6 @@ public class FindAndReplaceOptions {
return new FindAndReplaceOptions();
}
/**
* Static factory method returning an unmodifiable {@link FindAndReplaceOptions} instance.
*
* @return unmodifiable {@link FindAndReplaceOptions} instance.
* @since 2.2
*/
public static FindAndReplaceOptions none() {
return NONE;
}
/**
* Static factory method to create a {@link FindAndReplaceOptions} instance with
* <dl>

View File

@@ -15,69 +15,20 @@
*/
package org.springframework.data.mongodb.core;
import java.util.function.Function;
import org.bson.Document;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.ReadPreference;
import com.mongodb.reactivestreams.client.FindPublisher;
import com.mongodb.reactivestreams.client.MongoCollection;
/**
* Simple callback interface to allow customization of a {@link FindPublisher}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Konstantin Volivach
*/
public interface FindPublisherPreparer extends ReadPreferenceAware {
/**
* Default {@link FindPublisherPreparer} just passing on the given {@link FindPublisher}.
*
* @since 2.2
*/
FindPublisherPreparer NO_OP_PREPARER = (findPublisher -> findPublisher);
public interface FindPublisherPreparer {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns the prepared cursor.
*
* @param findPublisher must not be {@literal null}.
*/
FindPublisher<Document> prepare(FindPublisher<Document> findPublisher);
/**
* Apply query specific settings to {@link MongoCollection} and initate a find operation returning a
* {@link FindPublisher} via the given {@link Function find} function.
*
* @param collection must not be {@literal null}.
* @param find must not be {@literal null}.
* @return
* @throws IllegalArgumentException if one of the required arguments is {@literal null}.
* @since 2.2
*/
default FindPublisher<Document> initiateFind(MongoCollection<Document> collection,
Function<MongoCollection<Document>, FindPublisher<Document>> find) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(find, "Find function must not be null!");
if (hasReadPreference()) {
collection = collection.withReadPreference(getReadPreference());
}
return prepare(find.apply(collection));
}
/**
* @return the {@link ReadPreference} to apply or {@literal null} if none defined.
* @since 2.2
*/
@Override
@Nullable
default ReadPreference getReadPreference() {
return null;
}
<T> FindPublisher<T> prepare(FindPublisher<T> findPublisher);
}

View File

@@ -92,7 +92,7 @@ public class MappedDocument {
* mapped to the specific domain type.
*
* @author Christoph Strobl
* @since 2.2
* @since 2.1.4
*/
class MappedUpdate implements UpdateDefinition {
@@ -137,14 +137,5 @@ public class MappedDocument {
public Boolean isIsolated() {
return delegate.isIsolated();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getArrayFilters()
*/
@Override
public List<ArrayFilter> getArrayFilters() {
return delegate.getArrayFilters();
}
}
}

View File

@@ -1,210 +0,0 @@
/*
* Copyright 2019-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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder;
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* {@link MongoJsonSchemaCreator} implementation using both {@link MongoConverter} and {@link MappingContext} to obtain
* domain type meta information which considers {@link org.springframework.data.mongodb.core.mapping.Field field names}
* and {@link org.springframework.data.mongodb.core.convert.MongoCustomConversions custom conversions}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.2
*/
class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
private final MongoConverter converter;
private final MappingContext<MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
/**
* Create a new instance of {@link MappingMongoJsonSchemaCreator}.
*
* @param converter must not be {@literal null}.
*/
@SuppressWarnings("unchecked")
MappingMongoJsonSchemaCreator(MongoConverter converter) {
Assert.notNull(converter, "Converter must not be null!");
this.converter = converter;
this.mappingContext = (MappingContext<MongoPersistentEntity<?>, MongoPersistentProperty>) converter
.getMappingContext();
}
/*
* (non-Javadoc)
* org.springframework.data.mongodb.core.MongoJsonSchemaCreator#createSchemaFor(java.lang.Class)
*/
@Override
public MongoJsonSchema createSchemaFor(Class<?> type) {
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
MongoJsonSchemaBuilder schemaBuilder = MongoJsonSchema.builder();
List<JsonSchemaProperty> schemaProperties = computePropertiesForEntity(Collections.emptyList(), entity);
schemaBuilder.properties(schemaProperties.toArray(new JsonSchemaProperty[0]));
return schemaBuilder.build();
}
private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistentProperty> path,
MongoPersistentEntity<?> entity) {
List<JsonSchemaProperty> schemaProperties = new ArrayList<>();
for (MongoPersistentProperty nested : entity) {
List<MongoPersistentProperty> currentPath = new ArrayList<>(path);
if (path.contains(nested)) { // cycle guard
schemaProperties.add(createSchemaProperty(computePropertyFieldName(CollectionUtils.lastElement(currentPath)),
Object.class, false));
break;
}
currentPath.add(nested);
schemaProperties.add(computeSchemaForProperty(currentPath));
}
return schemaProperties;
}
private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty> path) {
MongoPersistentProperty property = CollectionUtils.lastElement(path);
boolean required = isRequiredProperty(property);
Class<?> rawTargetType = computeTargetType(property); // target type before conversion
Class<?> targetType = converter.getTypeMapper().getWriteTargetTypeFor(rawTargetType); // conversion target type
if (property.isEntity() && ObjectUtils.nullSafeEquals(rawTargetType, targetType)) {
return createObjectSchemaPropertyForEntity(path, property, required);
}
String fieldName = computePropertyFieldName(property);
if (property.isCollectionLike()) {
return createSchemaProperty(fieldName, targetType, required);
} else if (property.isMap()) {
return createSchemaProperty(fieldName, Type.objectType(), required);
} else if (ClassUtils.isAssignable(Enum.class, targetType)) {
return createEnumSchemaProperty(fieldName, targetType, required);
}
return createSchemaProperty(fieldName, targetType, required);
}
private JsonSchemaProperty createObjectSchemaPropertyForEntity(List<MongoPersistentProperty> path,
MongoPersistentProperty property, boolean required) {
ObjectJsonSchemaProperty target = JsonSchemaProperty.object(property.getName());
List<JsonSchemaProperty> nestedProperties = computePropertiesForEntity(path,
mappingContext.getRequiredPersistentEntity(property));
return createPotentiallyRequiredSchemaProperty(
target.properties(nestedProperties.toArray(new JsonSchemaProperty[0])), required);
}
private JsonSchemaProperty createEnumSchemaProperty(String fieldName, Class<?> targetType, boolean required) {
List<Object> possibleValues = new ArrayList<>();
for (Object enumValue : EnumSet.allOf((Class) targetType)) {
possibleValues.add(converter.convertToMongoType(enumValue));
}
targetType = possibleValues.isEmpty() ? targetType : possibleValues.iterator().next().getClass();
return createSchemaProperty(fieldName, targetType, required, possibleValues);
}
JsonSchemaProperty createSchemaProperty(String fieldName, Object type, boolean required) {
return createSchemaProperty(fieldName, type, required, Collections.emptyList());
}
JsonSchemaProperty createSchemaProperty(String fieldName, Object type, boolean required,
Collection<?> possibleValues) {
TypedJsonSchemaObject schemaObject = type instanceof Type ? JsonSchemaObject.of(Type.class.cast(type))
: JsonSchemaObject.of(Class.class.cast(type));
if (!CollectionUtils.isEmpty(possibleValues)) {
schemaObject = schemaObject.possibleValues(possibleValues);
}
return createPotentiallyRequiredSchemaProperty(JsonSchemaProperty.named(fieldName).with(schemaObject), required);
}
private String computePropertyFieldName(PersistentProperty property) {
return property instanceof MongoPersistentProperty ? ((MongoPersistentProperty) property).getFieldName()
: property.getName();
}
private boolean isRequiredProperty(PersistentProperty property) {
return property.getType().isPrimitive();
}
private Class<?> computeTargetType(PersistentProperty<?> property) {
if (!(property instanceof MongoPersistentProperty)) {
return property.getType();
}
MongoPersistentProperty mongoProperty = (MongoPersistentProperty) property;
if (!mongoProperty.isIdProperty()) {
return mongoProperty.getFieldType();
}
if (mongoProperty.hasExplicitWriteTarget()) {
return mongoProperty.getRequiredAnnotation(Field.class).targetType().getJavaClass();
}
return mongoProperty.getFieldType() != mongoProperty.getActualType() ? Object.class : mongoProperty.getFieldType();
}
static JsonSchemaProperty createPotentiallyRequiredSchemaProperty(JsonSchemaProperty property, boolean required) {
if (!required) {
return property;
}
return JsonSchemaProperty.required(property);
}
}

View File

@@ -34,29 +34,14 @@ import com.mongodb.client.MongoDatabase;
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
private final Object mongoClient;
private final MongoClient 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;
}
/**
* @param client the underlying {@link com.mongodb.client.MongoClient} used for data access.
* @since 2.2
*/
public MongoAdmin(com.mongodb.client.MongoClient client) {
Assert.notNull(client, "Client must not be null!");
this.mongoClient = client;
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.core.MongoAdminOperations#dropDatabase(java.lang.String)
*/
@@ -88,11 +73,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

@@ -38,11 +38,7 @@ import com.mongodb.ServerAddress;
* @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();

View File

@@ -22,7 +22,6 @@ 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;
@@ -37,17 +36,12 @@ import com.mongodb.WriteConcern;
* @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 @Nullable String description = DEFAULT_MONGO_OPTIONS.getDescription();
private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost();
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
@@ -57,8 +51,6 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
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();
@@ -66,8 +58,6 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
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();
@@ -78,14 +68,12 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
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;
}
@@ -247,7 +235,7 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
}
/**
* This controls if the driver should us an SSL connection. Defaults to {@literal false}.
* This controls if the driver should us an SSL connection. Defaults to |@literal false}.
*
* @param ssl
*/
@@ -278,16 +266,6 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
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()
@@ -307,7 +285,7 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
.cursorFinalizerEnabled(cursorFinalizerEnabled) //
.dbDecoderFactory(dbDecoderFactory) //
.dbEncoderFactory(dbEncoderFactory) //
.applicationName(description) // TODO: Mongo Driver 4 - use application name if description not available
.description(description) //
.heartbeatConnectTimeout(heartbeatConnectTimeout) //
.heartbeatFrequency(heartbeatFrequency) //
.heartbeatSocketTimeout(heartbeatSocketTimeout) //
@@ -319,10 +297,8 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
.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
.socketFactory(socketFactoryToUse) //
.socketKeepAlive(socketKeepAlive) //
.socketTimeout(socketTimeout) //
.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) //
.writeConcern(writeConcern).build();

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2019-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.Collections;
import java.util.Map;
import org.bson.BsonDocument;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.Nullable;
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.MongoClientSettings;
/**
* {@link FactoryBean} for creating {@link AutoEncryptionSettings} using the {@link AutoEncryptionSettings.Builder}.
*
* @author Christoph Strobl
* @since 2.2
*/
public class MongoEncryptionSettingsFactoryBean implements FactoryBean<AutoEncryptionSettings> {
private boolean bypassAutoEncryption;
private String keyVaultNamespace;
private Map<String, Object> extraOptions;
private MongoClientSettings keyVaultClientSettings;
private Map<String, Map<String, Object>> kmsProviders;
private Map<String, BsonDocument> schemaMap;
/**
* @param bypassAutoEncryption
* @see AutoEncryptionSettings.Builder#bypassAutoEncryption(boolean)
*/
public void setBypassAutoEncryption(boolean bypassAutoEncryption) {
this.bypassAutoEncryption = bypassAutoEncryption;
}
/**
* @param extraOptions
* @see AutoEncryptionSettings.Builder#extraOptions(Map)
*/
public void setExtraOptions(Map<String, Object> extraOptions) {
this.extraOptions = extraOptions;
}
/**
* @param keyVaultNamespace
* @see AutoEncryptionSettings.Builder#keyVaultNamespace(String)
*/
public void setKeyVaultNamespace(String keyVaultNamespace) {
this.keyVaultNamespace = keyVaultNamespace;
}
/**
* @param keyVaultClientSettings
* @see AutoEncryptionSettings.Builder#keyVaultMongoClientSettings(MongoClientSettings)
*/
public void setKeyVaultClientSettings(MongoClientSettings keyVaultClientSettings) {
this.keyVaultClientSettings = keyVaultClientSettings;
}
/**
* @param kmsProviders
* @see AutoEncryptionSettings.Builder#kmsProviders(Map)
*/
public void setKmsProviders(Map<String, Map<String, Object>> kmsProviders) {
this.kmsProviders = kmsProviders;
}
/**
* @param schemaMap
* @see AutoEncryptionSettings.Builder#schemaMap(Map)
*/
public void setSchemaMap(Map<String, BsonDocument> schemaMap) {
this.schemaMap = schemaMap;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public AutoEncryptionSettings getObject() {
return AutoEncryptionSettings.builder() //
.bypassAutoEncryption(bypassAutoEncryption) //
.keyVaultNamespace(keyVaultNamespace) //
.keyVaultMongoClientSettings(keyVaultClientSettings) //
.kmsProviders(orEmpty(kmsProviders)) //
.extraOptions(orEmpty(extraOptions)) //
.schemaMap(orEmpty(schemaMap)) //
.build();
}
private <K, V> Map<K, V> orEmpty(@Nullable Map<K, V> source) {
return source != null ? source : Collections.emptyMap();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return AutoEncryptionSettings.class;
}
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2019-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 org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.util.Assert;
/**
* {@link MongoJsonSchemaCreator} extracts the {@link MongoJsonSchema} for a given {@link Class} by applying the
* following mapping rules.
* <p>
* <strong>Required Properties</strong>
* <ul>
* <li>Properties of primitive type</li>
* </ul>
* <strong>Ignored Properties</strong>
* <ul>
* <li>All properties annotated with {@link org.springframework.data.annotation.Transient}</li>
* </ul>
* <strong>Property Type Mapping</strong>
* <ul>
* <li>{@link java.lang.Object} -> {@code type : 'object'}</li>
* <li>{@link java.util.Arrays} -> {@code type : 'array'}</li>
* <li>{@link java.util.Collection} -> {@code type : 'array'}</li>
* <li>{@link java.util.Map} -> {@code type : 'object'}</li>
* <li>{@link java.lang.Enum} -> {@code type : 'string', enum : [the enum values]}</li>
* <li>Simple Types -> {@code type : 'the corresponding bson type' }</li>
* <li>Domain Types -> {@code type : 'object', properties : &#123;the types properties&#125; }</li>
* </ul>
* <br />
* {@link org.springframework.data.annotation.Id _id} properties using types that can be converted into
* {@link org.bson.types.ObjectId} like {@link String} will be mapped to {@code type : 'object'} unless there is more
* specific information available via the {@link org.springframework.data.mongodb.core.mapping.MongoId} annotation.
* </p>
*
* @author Christoph Strobl
* @since 2.2
*/
public interface MongoJsonSchemaCreator {
/**
* Create the {@link MongoJsonSchema} for the given {@link Class type}.
*
* @param type must not be {@literal null}.
* @return never {@literal null}.
*/
MongoJsonSchema createSchemaFor(Class<?> type);
/**
* Creates a new {@link MongoJsonSchemaCreator} that is aware of conversions applied by the given
* {@link MongoConverter}.
*
* @param mongoConverter must not be {@literal null}.
* @return new instance of {@link MongoJsonSchemaCreator}.
*/
static MongoJsonSchemaCreator create(MongoConverter mongoConverter) {
Assert.notNull(mongoConverter, "MongoConverter must not be null!");
return new MappingMongoJsonSchemaCreator(mongoConverter);
}
}

View File

@@ -27,7 +27,6 @@ import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
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.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -41,7 +40,6 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -59,7 +57,7 @@ import com.mongodb.client.result.UpdateResult;
* Interface that specifies a basic set of MongoDB operations. Implemented by {@link MongoTemplate}. Not often used but
* a useful option for extensibility and testability (as it can be easily mocked, stubbed, or be the target of a JDK
* proxy).
* <p/>
* <p />
* <strong>NOTE:</strong> Some operations cannot be executed within a MongoDB transaction. Please refer to the MongoDB
* specific documentation to learn more about <a href="https://docs.mongodb.com/manual/core/transactions/">Multi
* Document Transactions</a>.
@@ -177,7 +175,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Obtain a {@link ClientSession session} bound instance of {@link SessionScoped} binding the {@link ClientSession}
* provided by the given {@link Supplier} to each and every command issued against MongoDB.
* <p/>
* <p />
* <strong>Note:</strong> It is up to the caller to manage the {@link ClientSession} lifecycle. Use the
* {@link SessionScoped#execute(SessionCallback, Consumer)} hook to potentially close the {@link ClientSession}.
*
@@ -213,7 +211,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoOperations}.
* <p/>
* <p />
* <strong>Note:</strong> It is up to the caller to manage the {@link ClientSession} lifecycle.
*
* @param session must not be {@literal null}.
@@ -364,9 +362,7 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @return
* @since 1.7
* @deprecated since 2.2. The {@code eval} command has been removed without replacement in MongoDB Server 4.2.0.
*/
@Deprecated
ScriptOperations scriptOps();
/**
@@ -438,11 +434,7 @@ public interface MongoOperations extends FluentMongoOperations {
* reduce function.
* @param entityClass The parametrized type of the returned list
* @return The results of the group operation
* @deprecated since 2.2. The {@code group} command has been removed in MongoDB Server 4.2.0. <br />
* Please use {@link #aggregate(TypedAggregation, String, Class) } with a
* {@link org.springframework.data.mongodb.core.aggregation.GroupOperation} instead.
*/
@Deprecated
<T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/**
@@ -457,12 +449,7 @@ public interface MongoOperations extends FluentMongoOperations {
* reduce function.
* @param entityClass The parametrized type of the returned list
* @return The results of the group operation
* @deprecated since 2.2. The {@code group} command has been removed in MongoDB Server 4.2.0. <br />
* Please use {@link #aggregate(TypedAggregation, String, Class) } with a
* {@link org.springframework.data.mongodb.core.aggregation.GroupOperation} and
* {@link org.springframework.data.mongodb.core.aggregation.MatchOperation} instead.
*/
@Deprecated
<T> GroupByResults<T> group(@Nullable Criteria criteria, String inputCollectionName, GroupBy groupBy,
Class<T> entityClass);
@@ -650,52 +637,24 @@ public interface MongoOperations extends FluentMongoOperations {
* information to determine the collection the query is ran against. Note, that MongoDB limits the number of results
* by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a particular number of
* results.
* <p>
* MongoDB 4.2 has removed the {@code geoNear} command. This method uses since version 2.2 aggregations and the
* {@code $geoNear} aggregation command to emulate {@code geoNear} command functionality. We recommend using
* aggregations directly:
* </p>
*
* <pre class="code">
* TypedAggregation&lt;T&gt; geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, "dis"))
* .withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
* AggregationResults&lt;Document&gt; results = aggregate(geoNear, Document.class);
* </pre>
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @return
* @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0. Use Aggregations with
* {@link Aggregation#geoNear(NearQuery, String)} instead.
*/
@Deprecated
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass);
/**
* Returns {@link GeoResults} for all entities matching the given {@link NearQuery}. Note, that MongoDB limits the
* number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a
* particular number of results.
* <p>
* MongoDB 4.2 has removed the {@code geoNear} command. This method uses since version 2.2 aggregations and the
* {@code $geoNear} aggregation command to emulate {@code geoNear} command functionality. We recommend using
* aggregations directly:
* </p>
*
* <pre class="code">
* TypedAggregation&lt;T&gt; geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, "dis"))
* .withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
* AggregationResults&lt;Document&gt; results = aggregate(geoNear, Document.class);
* </pre>
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @param collectionName the collection to trigger the query against. If no collection name is given the entity class
* will be inspected. Must not be {@literal null} nor empty.
* @return
* @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0. Use Aggregations with
* {@link Aggregation#geoNear(NearQuery, String)} instead.
*/
@Deprecated
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName);
/**
@@ -885,15 +844,12 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @return the converted object that was updated before it was updated or {@literal null}, if not found.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -901,16 +857,13 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated before it was updated or {@literal null}, if not found.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -919,18 +872,15 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link UpdateDefinition} to apply on matching documents.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parametrized type.
* @return the converted object that was updated or {@literal null}, if not found. Depending on the value of
* {@link FindAndModifyOptions#isReturnNew()} this will either be the object as it was before the update or as
* it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -939,19 +889,16 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated or {@literal null}, if not found. Depending on the value of
* {@link FindAndModifyOptions#isReturnNew()} this will either be the object as it was before the update or as
* it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
@@ -1154,11 +1101,6 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} by querying the collection of the given entity class.
* <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -1170,11 +1112,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} querying the given collection. The given {@link Query}
* must solely consist of document field references as we lack type information to map potential property references
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support. <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support.
*
* @param query the {@link Query} class that specifies the criteria used to find documents.
* @param collectionName must not be {@literal null} or empty.
@@ -1185,11 +1123,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} by querying the given collection using the given entity
* class to map the given {@link Query}. <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* class to map the given {@link Query}.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -1207,8 +1141,8 @@ public interface MongoOperations extends FluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
* <p/>
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
@@ -1269,8 +1203,8 @@ public interface MongoOperations extends FluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the saved object.
@@ -1300,112 +1234,99 @@ public interface MongoOperations extends FluentMongoOperations {
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, FindAndModifyOptions, Class, String)} instead.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #upsert(Query, UpdateDefinition, Class, String)} to get full type specific
* support. <br />
* domain type information. Use {@link #upsert(Query, Update, 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, UpdateDefinition, FindAndModifyOptions, Class, String)} instead.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, String collectionName);
UpdateResult upsert(Query query, Update update, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult upsert(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document.
* the provided update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class that determines the collection to use.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult updateFirst(Query query, Update update, Class<?> entityClass);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* 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.
* domain type information. Use {@link #updateFirst(Query, Update, 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.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, String collectionName);
UpdateResult updateFirst(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, 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}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult updateFirst(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1413,34 +1334,27 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult updateMulti(Query query, Update update, Class<?> entityClass);
/**
* Updates all objects that are found in the specified collection that matches the query document criteria with 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 #updateMulti(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, String collectionName);
UpdateResult updateMulti(Query query, Update update, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1448,22 +1362,16 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult updateMulti(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Remove the given object from the collection by {@literal id} and (if applicable) its
* {@link org.springframework.data.annotation.Version}. <br />
* Use {@link DeleteResult#getDeletedCount()} for insight whether an {@link DeleteResult#wasAcknowledged()
* acknowledged} remove operation was successful or not.
* Remove the given object from the collection by id.
*
* @param object must not be {@literal null}.
* @return the {@link DeleteResult} which lets you access the results of the previous delete.
@@ -1471,10 +1379,7 @@ public interface MongoOperations extends FluentMongoOperations {
DeleteResult remove(Object object);
/**
* Removes the given object from the given collection by {@literal id} and (if applicable) its
* {@link org.springframework.data.annotation.Version}. <br />
* Use {@link DeleteResult#getDeletedCount()} for insight whether an {@link DeleteResult#wasAcknowledged()
* acknowledged} remove operation was successful or not.
* Removes the given object from the given collection.
*
* @param object must not be {@literal null}.
* @param collectionName name of the collection where the objects will removed, must not be {@literal null} or empty.

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2019-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 reactor.core.publisher.Flux;
import java.time.Instant;
import java.util.function.Consumer;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
/**
* {@link ReactiveChangeStreamOperation} allows creation and execution of reactive MongoDB
* <a href="https://docs.mongodb.com/manual/changeStreams/">Change Stream</a> operations in a fluent API style. <br />
* The starting {@literal domainType} is used for mapping a potentially given
* {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} used for filtering. By default, the
* originating {@literal domainType} is also used for mapping back the result from the {@link org.bson.Document}.
* However, it is possible to define an different {@literal returnType} via {@code as}.<br />
* The collection to operate on is optional in which case call collection with the actual database are watched, use
* {@literal watchCollection} to define a fixed collection.
*
* <pre>
* <code>
* changeStream(Jedi.class)
* .watchCollection("star-wars")
* .filter(where("operationType").is("insert"))
* .resumeAt(Instant.now())
* .listen();
* </code>
* </pre>
*
* @author Christoph Strobl
* @since 2.2
*/
public interface ReactiveChangeStreamOperation {
/**
* Start creating a change stream operation for the given {@literal domainType} watching all collections within the
* database. <br />
* Consider limiting events be defining a {@link ChangeStreamWithCollection#watchCollection(String) collection} and/or
* {@link ChangeStreamWithFilterAndProjection#filter(CriteriaDefinition) filter}.
*
* @param domainType must not be {@literal null}. Use {@link org.bson.Document} to obtain raw elements.
* @return new instance of {@link ReactiveChangeStream}. Never {@literal null}.
* @throws IllegalArgumentException if domainType is {@literal null}.
*/
<T> ReactiveChangeStream<T> changeStream(Class<T> domainType);
/**
* Compose change stream execution by calling one of the terminating methods.
*/
interface TerminatingChangeStream<T> {
/**
* Start listening to changes. The stream will not be completed unless the {@link org.reactivestreams.Subscription}
* is {@link org.reactivestreams.Subscription#cancel() canceled}.
* <p />
* However, the stream may become dead, or invalid, if all watched collections, databases are dropped.
*/
Flux<ChangeStreamEvent<T>> listen();
}
/**
* Collection override (optional).
*/
interface ChangeStreamWithCollection<T> {
/**
* Explicitly set the name of the collection to watch.<br />
* Skip this step to watch all collections within the database.
*
* @param collection must not be {@literal null} nor {@literal empty}.
* @return new instance of {@link ChangeStreamWithFilterAndProjection}.
* @throws IllegalArgumentException if {@code collection} is {@literal null}.
*/
ChangeStreamWithFilterAndProjection<T> watchCollection(String collection);
/**
* Set the the collection to watch. Collection name is derived from the {@link Class entityClass}.<br />
* Skip this step to watch all collections within the database.
*
* @param entityClass must not be {@literal null}.
* @return new instance of {@link ChangeStreamWithFilterAndProjection}.
* @throws IllegalArgumentException if {@code entityClass} is {@literal null}.
*/
ChangeStreamWithFilterAndProjection<T> watchCollection(Class<?> entityClass);
}
/**
* Provide a filter for limiting results (optional).
*/
interface ChangeStreamWithFilterAndProjection<T> extends ResumingChangeStream<T>, TerminatingChangeStream<T> {
/**
* Use an {@link Aggregation} to filter matching events.
*
* @param by must not be {@literal null}.
* @return new instance of {@link ChangeStreamWithFilterAndProjection}.
* @throws IllegalArgumentException if the given {@link Aggregation} is {@literal null}.
*/
ChangeStreamWithFilterAndProjection<T> filter(Aggregation by);
/**
* Use a {@link CriteriaDefinition critera} to filter matching events via an
* {@link org.springframework.data.mongodb.core.aggregation.MatchOperation}.
*
* @param by must not be {@literal null}.
* @return new instance of {@link ChangeStreamWithFilterAndProjection}.
* @throws IllegalArgumentException if the given {@link CriteriaDefinition} is {@literal null}.
*/
ChangeStreamWithFilterAndProjection<T> filter(CriteriaDefinition by);
/**
* Define the target type fields should be mapped to.
*
* @param resultType must not be {@literal null}.
* @param <R> result type.
* @return new instance of {@link ChangeStreamWithFilterAndProjection}.
* @throws IllegalArgumentException if resultType is {@literal null}.
*/
<R> ChangeStreamWithFilterAndProjection<R> as(Class<R> resultType);
}
/**
* Resume a change stream. (optional).
*/
interface ResumingChangeStream<T> extends TerminatingChangeStream<T> {
/**
* Resume the change stream at a given point.
*
* @param token an {@link Instant} or {@link BsonTimestamp}
* @return new instance of {@link TerminatingChangeStream}.
* @see ChangeStreamOptionsBuilder#resumeAt(Instant)
* @see ChangeStreamOptionsBuilder#resumeAt(BsonTimestamp)
* @throws IllegalArgumentException if the given beacon is neither {@link Instant} nor {@link BsonTimestamp}.
*/
TerminatingChangeStream<T> resumeAt(Object token);
/**
* Resume the change stream after a given point.
*
* @param token an {@link Instant} or {@link BsonTimestamp}
* @return new instance of {@link TerminatingChangeStream}.
* @see ChangeStreamOptionsBuilder#resumeAfter(BsonValue)
* @see ChangeStreamOptionsBuilder#resumeToken(BsonValue)
* @throws IllegalArgumentException if the given beacon not a {@link BsonValue}.
*/
TerminatingChangeStream<T> resumeAfter(Object token);
/**
* Start the change stream after a given point.
*
* @param token an {@link Instant} or {@link BsonTimestamp}
* @return new instance of {@link TerminatingChangeStream}.
* @see ChangeStreamOptionsBuilder#startAfter(BsonValue) (BsonValue)
* @throws IllegalArgumentException if the given beacon not a {@link BsonValue}.
*/
TerminatingChangeStream<T> startAfter(Object token);
}
/**
* Provide some options.
*/
interface ChangeStreamWithOptions<T> {
/**
* Provide some options via the callback by modifying the given {@link ChangeStreamOptionsBuilder}. Previously
* defined options like a {@link ResumingChangeStream#resumeAfter(Object) resumeToken} are carried over to the
* builder and can be overwritten via eg. {@link ChangeStreamOptionsBuilder#resumeToken(BsonValue)}.
*
* @param optionsConsumer never {@literal null}.
* @return new instance of {@link ReactiveChangeStream}.
*/
ReactiveChangeStream<T> withOptions(Consumer<ChangeStreamOptionsBuilder> optionsConsumer);
}
/**
* {@link ReactiveChangeStream} provides methods for constructing change stream operations in a fluent way.
*/
interface ReactiveChangeStream<T> extends ChangeStreamWithOptions<T>, ChangeStreamWithCollection<T>,
TerminatingChangeStream<T>, ResumingChangeStream<T>, ChangeStreamWithFilterAndProjection<T> {}
}

View File

@@ -1,230 +0,0 @@
/*
* Copyright 2019-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 reactor.core.publisher.Flux;
import java.time.Instant;
import java.util.List;
import java.util.function.Consumer;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* @author Christoph Strobl
* @since 2.2
*/
class ReactiveChangeStreamOperationSupport implements ReactiveChangeStreamOperation {
private final ReactiveMongoTemplate template;
/**
* @param template must not be {@literal null}.
*/
ReactiveChangeStreamOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation#changeStream(java.lang.Class)
*/
@Override
public <T> ReactiveChangeStream<T> changeStream(Class<T> domainType) {
Assert.notNull(domainType, "DomainType must not be null!");
return new ReactiveChangeStreamSupport<>(template, domainType, domainType, null, null);
}
static class ReactiveChangeStreamSupport<T>
implements ReactiveChangeStream<T>, ChangeStreamWithFilterAndProjection<T> {
private final ReactiveMongoTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final @Nullable String collection;
private final @Nullable ChangeStreamOptions options;
private ReactiveChangeStreamSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType,
@Nullable String collection, @Nullable ChangeStreamOptions options) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.collection = collection;
this.options = options;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithCollection#watchCollection(java.lang.String)
*/
@Override
public ChangeStreamWithFilterAndProjection<T> watchCollection(String collection) {
Assert.hasText(collection, "Collection name must not be null nor empty!");
return new ReactiveChangeStreamSupport<>(template, domainType, returnType, collection, options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithCollection#watchCollection(java.lang.Class)
*/
@Override
public ChangeStreamWithFilterAndProjection<T> watchCollection(Class<?> entityClass) {
Assert.notNull(entityClass, "Collection type not be null!");
return watchCollection(template.getCollectionName(entityClass));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ResumingChangeStream#resumeAt(java.lang.Object)
*/
@Override
public TerminatingChangeStream<T> resumeAt(Object token) {
return withOptions(builder -> {
if (token instanceof Instant) {
builder.resumeAt((Instant) token);
} else if (token instanceof BsonTimestamp) {
builder.resumeAt((BsonTimestamp) token);
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ResumingChangeStream#resumeAfter(java.lang.Object)
*/
@Override
public TerminatingChangeStream<T> resumeAfter(Object token) {
Assert.isInstanceOf(BsonValue.class, token, "Token must be a BsonValue");
return withOptions(builder -> builder.resumeAfter((BsonValue) token));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ResumingChangeStream#startAfter(java.lang.Object)
*/
@Override
public TerminatingChangeStream<T> startAfter(Object token) {
Assert.isInstanceOf(BsonValue.class, token, "Token must be a BsonValue");
return withOptions(builder -> builder.startAfter((BsonValue) token));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithOptions#withOptions(java.util.function.Consumer)
*/
@Override
public ReactiveChangeStreamSupport<T> withOptions(Consumer<ChangeStreamOptionsBuilder> optionsConsumer) {
ChangeStreamOptionsBuilder builder = initOptionsBuilder();
optionsConsumer.accept(builder);
return new ReactiveChangeStreamSupport<>(template, domainType, returnType, collection, builder.build());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithProjection#as(java.lang.Class)
*/
@Override
public <R> ChangeStreamWithFilterAndProjection<R> as(Class<R> resultType) {
Assert.notNull(resultType, "ResultType must not be null!");
return new ReactiveChangeStreamSupport<>(template, domainType, resultType, collection, options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithFilter#filter(org.springframework.data.mongodb.core.aggregation.Aggregation)
*/
@Override
public ChangeStreamWithFilterAndProjection<T> filter(Aggregation filter) {
return withOptions(builder -> builder.filter(filter));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.ChangeStreamWithFilter#filter(org.springframework.data.mongodb.core.query.CriteriaDefinition)
*/
@Override
public ChangeStreamWithFilterAndProjection<T> filter(CriteriaDefinition by) {
MatchOperation $match = Aggregation.match(by);
Aggregation aggregation = !Document.class.equals(domainType) ? Aggregation.newAggregation(domainType, $match)
: Aggregation.newAggregation($match);
return filter(aggregation);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveChangeStreamOperation.TerminatingChangeStream#listen()
*/
@Override
public Flux<ChangeStreamEvent<T>> listen() {
return template.changeStream(collection, options != null ? options : ChangeStreamOptions.empty(), returnType);
}
private ChangeStreamOptionsBuilder initOptionsBuilder() {
ChangeStreamOptionsBuilder builder = ChangeStreamOptions.builder();
if (options == null) {
return builder;
}
options.getFilter().ifPresent(it -> {
if (it instanceof Aggregation) {
builder.filter((Aggregation) it);
} else {
builder.filter(((List<Document>) it).toArray(new Document[0]));
}
});
options.getFullDocumentLookup().ifPresent(builder::fullDocumentLookup);
options.getCollation().ifPresent(builder::collation);
if (options.isResumeAfter()) {
options.getResumeToken().ifPresent(builder::resumeAfter);
options.getResumeBsonTimestamp().ifPresent(builder::resumeAfter);
} else if (options.isStartAfter()) {
options.getResumeToken().ifPresent(builder::startAfter);
} else {
options.getResumeTimestamp().ifPresent(builder::resumeAt);
options.getResumeBsonTimestamp().ifPresent(builder::resumeAt);
}
return builder;
}
}
}

View File

@@ -23,7 +23,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.bson.Document;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -32,6 +31,8 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.reactivestreams.client.FindPublisher;
/**
* Implementation of {@link ReactiveFindOperation}.
*
@@ -119,7 +120,12 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
public Mono<T> first() {
FindPublisherPreparer preparer = getCursorPreparer(query);
Flux<T> result = doFind(publisher -> preparer.prepare(publisher).limit(1));
Flux<T> result = doFind(new FindPublisherPreparer() {
@Override
public <D> FindPublisher<D> prepare(FindPublisher<D> publisher) {
return preparer.prepare(publisher).limit(1);
}
});
return result.next();
}
@@ -132,7 +138,12 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
public Mono<T> one() {
FindPublisherPreparer preparer = getCursorPreparer(query);
Flux<T> result = doFind(publisher -> preparer.prepare(publisher).limit(2));
Flux<T> result = doFind(new FindPublisherPreparer() {
@Override
public <D> FindPublisher<D> prepare(FindPublisher<D> publisher) {
return preparer.prepare(publisher).limit(2);
}
});
return result.collectList().flatMap(it -> {

View File

@@ -23,4 +23,4 @@ package org.springframework.data.mongodb.core;
* @since 2.0
*/
public interface ReactiveFluentMongoOperations extends ReactiveFindOperation, ReactiveInsertOperation,
ReactiveUpdateOperation, ReactiveRemoveOperation, ReactiveAggregationOperation, ReactiveMapReduceOperation, ReactiveChangeStreamOperation {}
ReactiveUpdateOperation, ReactiveRemoveOperation, ReactiveAggregationOperation, ReactiveMapReduceOperation {}

View File

@@ -29,7 +29,6 @@ import org.springframework.data.geo.GeoResult;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -40,9 +39,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -223,9 +220,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* {@link ClientSession#abortTransaction() rolled back} upon errors.
*
* @return new instance of {@link ReactiveSessionScoped}. Never {@literal null}.
* @deprecated since 2.2. Use {@code @Transactional} or {@link TransactionalOperator}.
*/
@Deprecated
ReactiveSessionScoped inTransaction();
/**
@@ -240,9 +235,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* @param sessionProvider must not be {@literal null}.
* @return new instance of {@link ReactiveSessionScoped}. Never {@literal null}.
* @since 2.1
* @deprecated since 2.2. Use {@code @Transactional} or {@link TransactionalOperator}.
*/
@Deprecated
ReactiveSessionScoped inTransaction(Publisher<ClientSession> sessionProvider);
/**
@@ -626,52 +619,24 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* entity mapping information to determine the collection the query is ran against. Note, that MongoDB limits the
* number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect a
* particular number of results.
* <p>
* MongoDB 4.2 has removed the {@code geoNear} command. This method uses since version 2.2 aggregations and the
* {@code $geoNear} aggregation command to emulate {@code geoNear} command functionality. We recommend using
* aggregations directly:
* </p>
*
* <pre class="code">
* TypedAggregation&lt;T&gt; geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, "dis"))
* .withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
* Flux&lt;Document&gt; results = aggregate(geoNear, Document.class);
* </pre>
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @return the converted {@link GeoResult}s.
* @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0. Use Aggregations with
* {@link Aggregation#geoNear(NearQuery, String)} instead.
*/
@Deprecated
<T> Flux<GeoResult<T>> geoNear(NearQuery near, Class<T> entityClass);
/**
* Returns {@link Flux} of {@link GeoResult} for all entities matching the given {@link NearQuery}. Note, that MongoDB
* limits the number of results by default. Make sure to add an explicit limit to the {@link NearQuery} if you expect
* a particular number of results.
* <p>
* MongoDB 4.2 has removed the {@code geoNear} command. This method uses since version 2.2 aggregations and the
* {@code $geoNear} aggregation command to emulate {@code geoNear} command functionality. We recommend using
* aggregations directly:
* </p>
*
* <pre class="code">
* TypedAggregation&lt;T&gt; geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, "dis"))
* .withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
* Flux&lt;Document&gt; results = aggregate(geoNear, Document.class);
* </pre>
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @param collectionName the collection to trigger the query against. If no collection name is given the entity class
* will be inspected.
* @return the converted {@link GeoResult}s.
* @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0. Use Aggregations with
* {@link Aggregation#geoNear(NearQuery, String)} instead.
*/
@Deprecated
<T> Flux<GeoResult<T>> geoNear(NearQuery near, Class<T> entityClass, String collectionName);
/**
@@ -680,14 +645,11 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @return the converted object that was updated before it was updated.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
<T> Mono<T> findAndModify(Query query, Update update, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -695,15 +657,12 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated before it was updated.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
<T> Mono<T> findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -712,16 +671,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link UpdateDefinition} to apply on matching documents.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parametrized type.
* @return the converted object that was updated. Depending on the value of {@link FindAndModifyOptions#isReturnNew()}
* this will either be the object as it was before the update or as it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
<T> Mono<T> findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -730,17 +686,14 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated. Depending on the value of {@link FindAndModifyOptions#isReturnNew()}
* this will either be the object as it was before the update or as it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
<T> Mono<T> findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
@@ -934,11 +887,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} by querying the collection of the given entity class.
* <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -950,11 +898,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} querying the given collection. The given {@link Query}
* must solely consist of document field references as we lack type information to map potential property references
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support. <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* onto document fields. Use {@link #count(Query, Class, String)} to get full type specific support.
*
* @param query the {@link Query} class that specifies the criteria used to find documents.
* @param collectionName must not be {@literal null} or empty.
@@ -965,11 +909,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
/**
* Returns the number of documents for the given {@link Query} by querying the given collection using the given entity
* class to map the given {@link Query}. <br />
* <strong>NOTE:</strong> Query {@link Query#getSkip() offset} and {@link Query#getLimit() limit} can have direct
* influence on the resulting number of documents found as those values are passed on to the server and potentially
* limit the range and order within which the server performs the count operation. Use an {@literal unpaged} query to
* count all matches.
* class to map the given {@link Query}.
*
* @param query the {@link Query} class that specifies the criteria used to find documents. Must not be
* {@literal null}.
@@ -987,8 +927,8 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
* <p/>
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
@@ -1047,8 +987,8 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
* <p/>
* <p/>
* Insert is used to initially store the object into the database. To update an existing object use the save method.
@@ -1095,8 +1035,8 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the saved object.
@@ -1132,8 +1072,8 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* If your object has an "Id' property, it will be set with the generated Id from MongoDB. If your Id property is a
* String then MongoDB ObjectId will be used to populate that string. Otherwise, the conversion from ObjectId to your
* property type will be handled by Spring's BeanWrapper class that leverages Type Conversion API. See
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's
* Type Conversion"</a> for more details.
* <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation" > Spring's Type
* Conversion"</a> for more details.
*
* @param objectToSave the object to store in the collection. Must not be {@literal null}.
* @return the saved object.
@@ -1163,111 +1103,99 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class)} instead.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #upsert(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #upsert(Query, Update, 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.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> upsert(Query query, Update update, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> upsert(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class)} instead.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class that determines the collection to use.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> updateFirst(Query query, Update update, Class<?> entityClass);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* 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 />
* domain type information. Use {@link #updateFirst(Query, Update, 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.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> updateFirst(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, 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}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> updateFirst(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1275,34 +1203,27 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> updateMulti(Query query, Update update, Class<?> entityClass);
/**
* Updates all objects that are found in the specified collection that matches the query document criteria with 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 #updateMulti(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> updateMulti(Query query, Update update, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1310,16 +1231,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> updateMulti(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Remove the given object from the collection by id.

View File

@@ -15,8 +15,6 @@
*/
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.query.Query;
@@ -120,16 +118,13 @@ public interface ReactiveUpdateOperation {
interface UpdateWithUpdate<T> {
/**
* Set the {@link UpdateDefinition} to be applied.
* Set the {@link org.springframework.data.mongodb.core.query.Update} to be applied.
*
* @param update must not be {@literal null}.
* @return new instance of {@link TerminatingUpdate}. Never {@literal null}.
* @throws IllegalArgumentException if update is {@literal null}.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
TerminatingUpdate<T> apply(UpdateDefinition update);
TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update);
/**
* Specify {@code replacement} object.

View File

@@ -63,7 +63,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
@NonNull ReactiveMongoTemplate template;
@NonNull Class<?> domainType;
Query query;
org.springframework.data.mongodb.core.query.UpdateDefinition update;
org.springframework.data.mongodb.core.query.Update update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@@ -72,10 +72,10 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.UpdateDefinition)
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.Update)
*/
@Override
public TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.UpdateDefinition update) {
public TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update) {
Assert.notNull(update, "Update must not be null!");
@@ -123,7 +123,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
String collectionName = getCollectionName();
return template.findAndModify(query, update, findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType, collectionName);
return template.findAndModify(query, update, findAndModifyOptions, targetType, collectionName);
}
/*
@@ -133,7 +133,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
@Override
public Mono<T> findAndReplace() {
return template.findAndReplace(query, replacement,
findAndReplaceOptions != null ? findAndReplaceOptions : FindAndReplaceOptions.none(), (Class) domainType,
findAndReplaceOptions != null ? findAndReplaceOptions : new FindAndReplaceOptions(), (Class) domainType,
getCollectionName(), targetType);
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2019-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 org.springframework.lang.Nullable;
import com.mongodb.ReadPreference;
/**
* Interface to be implemented by any object that wishes to expose the {@link ReadPreference}.
* <p>
* Typically implemented by cursor or query preparer objects.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.2
*/
public interface ReadPreferenceAware {
/**
* @return {@literal true} if a {@link ReadPreference} is set.
*/
default boolean hasReadPreference() {
return getReadPreference() != null;
}
/**
* @return the {@link ReadPreference} to apply or {@literal null} if none set.
*/
@Nullable
ReadPreference getReadPreference();
}

View File

@@ -29,9 +29,7 @@ import com.mongodb.DB;
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.7
* @deprecated since 2.2. The {@code eval} command has been removed without replacement in MongoDB Server 4.2.0.
*/
@Deprecated
public interface ScriptOperations {
/**

View File

@@ -21,7 +21,9 @@ import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.WriteConcern;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
@@ -33,9 +35,7 @@ import com.mongodb.client.MongoDatabase;
* @author Christoph Strobl
* @author George Moraitis
* @author Mark Paluch
* @deprecated since 2.2 in favor of {@link SimpleMongoClientDbFactory}.
*/
@Deprecated
public class SimpleMongoDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {
/**

View File

@@ -211,15 +211,6 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return delegate.withSession(session);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#isTransactionActive()
*/
@Override
public boolean isTransactionActive() {
return session != null && session.hasActiveTransaction();
}
private MongoDatabase decorateDatabase(MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}

View File

@@ -213,20 +213,6 @@ public class AccumulatorOperators {
return new Sum(append(expression));
}
/**
* Creates new {@link Sum} with all previously added arguments appending the given one. <br />
* <strong>NOTE:</strong> Only possible in {@code $project} stage.
*
* @param value the value to add.
* @return new instance of {@link Sum}.
* @since 2.2
*/
public Sum and(Number value) {
Assert.notNull(value, "Value must not be null!");
return new Sum(append(value));
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.AbstractAggregationExpression#toDocument(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/

View File

@@ -115,17 +115,6 @@ public class Aggregation {
return new Aggregation(operations);
}
/**
* 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
*/
public static AggregationUpdate newUpdate(AggregationOperation... operations) {
return AggregationUpdate.from(Arrays.asList(operations));
}
/**
* Returns a copy of this {@link Aggregation} with the given {@link AggregationOptions} set. Note that options are
* supported in MongoDB version 2.6+.
@@ -192,12 +181,13 @@ public class Aggregation {
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param aggregationOperations must not be {@literal null}.
* @param aggregationOperations must not be {@literal null} or empty.
* @param options must not be {@literal null} or empty.
*/
protected Aggregation(List<AggregationOperation> aggregationOperations, AggregationOptions options) {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
Assert.notNull(options, "AggregationOptions must not be null!");
// check $out is the last operation if it exists
@@ -254,19 +244,6 @@ public class Aggregation {
return new ProjectionOperation(fields);
}
/**
* Creates a new {@link ProjectionOperation} including all top level fields of the given given {@link Class}.
*
* @param type must not be {@literal null}.
* @return new instance of {@link ProjectionOperation}.
* @since 2.2
*/
public static ProjectionOperation project(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
return new ProjectionOperation(type);
}
/**
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
*
@@ -635,7 +612,7 @@ public class Aggregation {
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* 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}.

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.Collections;
import java.util.List;
import org.bson.Document;
/**
@@ -35,23 +32,7 @@ public interface AggregationOperation {
* Turns the {@link AggregationOperation} into a {@link Document} by using the given
* {@link AggregationOperationContext}.
*
* @param context the {@link AggregationOperationContext} to operate within. Must not be {@literal null}.
* @return the Document
* @deprecated since 2.2 in favor of {@link #toPipelineStages(AggregationOperationContext)}.
*/
@Deprecated
Document toDocument(AggregationOperationContext context);
/**
* Turns the {@link AggregationOperation} into list of {@link Document stages} by using the given
* {@link AggregationOperationContext}. This allows a single {@link AggregationOptions} to add additional stages for
* eg. {@code $sort} or {@code $limit}.
*
* @param context the {@link AggregationOperationContext} to operate within. Must not be {@literal null}.
* @return the pipeline stages to run through. Never {@literal null}.
* @since 2.2
*/
default List<Document> toPipelineStages(AggregationOperationContext context) {
return Collections.singletonList(toDocument(context));
}
}

View File

@@ -15,17 +15,8 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import java.beans.PropertyDescriptor;
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;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* The context for an {@link AggregationOperation}.
@@ -42,20 +33,7 @@ public interface AggregationOperationContext {
* @param document will never be {@literal null}.
* @return must not be {@literal null}.
*/
default Document getMappedObject(Document document) {
return getMappedObject(document, null);
}
/**
* Returns the mapped {@link Document}, potentially converting the source considering mapping metadata for the given
* type.
*
* @param document will never be {@literal null}.
* @param type can be {@literal null}.
* @return must not be {@literal null}.
* @since 2.2
*/
Document getMappedObject(Document document, @Nullable Class<?> type);
Document getMappedObject(Document document);
/**
* Returns a {@link FieldReference} for the given field or {@literal null} if the context does not expose the given
@@ -74,33 +52,4 @@ public interface AggregationOperationContext {
* @return
*/
FieldReference getReference(String name);
/**
* Returns the {@link Fields} exposed by the type. May be a {@literal class} or an {@literal interface}. The default
* implementation uses {@link BeanUtils#getPropertyDescriptors(Class) property descriptors} discover fields from a
* {@link Class}.
*
* @param type must not be {@literal null}.
* @return never {@literal null}.
* @since 2.2
* @see BeanUtils#getPropertyDescriptor(Class, String)
*/
default Fields getFields(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
return Fields.fields(Arrays.stream(BeanUtils.getPropertyDescriptors(type)) //
.filter(it -> { // object and default methods
Method method = it.getReadMethod();
if (method == null) {
return false;
}
if (ReflectionUtils.isObjectMethod(method)) {
return false;
}
return !method.isDefault();
}) //
.map(PropertyDescriptor::getName) //
.toArray(String[]::new));
}
}

View File

@@ -24,7 +24,6 @@ import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedFi
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.lang.Nullable;
/**
* Rendering support for {@link AggregationOperation} into a {@link List} of {@link org.bson.Document}.
@@ -53,7 +52,7 @@ class AggregationOperationRenderer {
for (AggregationOperation operation : operations) {
operationDocuments.addAll(operation.toPipelineStages(contextToUse));
operationDocuments.add(operation.toDocument(contextToUse));
if (operation instanceof FieldsExposingAggregationOperation) {
@@ -76,16 +75,15 @@ class AggregationOperationRenderer {
* Simple {@link AggregationOperationContext} that just returns {@link FieldReference}s as is.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
private static class NoOpAggregationOperationContext implements AggregationOperationContext {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document, java.lang.Class)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document)
*/
@Override
public Document getMappedObject(Document document, @Nullable Class<?> type) {
public Document getMappedObject(Document document) {
return document;
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import java.time.Duration;
import java.util.Optional;
import org.bson.Document;
@@ -45,15 +44,11 @@ public class AggregationOptions {
private static final String EXPLAIN = "explain";
private static final String ALLOW_DISK_USE = "allowDiskUse";
private static final String COLLATION = "collation";
private static final String COMMENT = "comment";
private static final String MAX_TIME = "maxTimeMS";
private final boolean allowDiskUse;
private final boolean explain;
private final Optional<Document> cursor;
private final Optional<Collation> collation;
private final Optional<String> comment;
private Duration maxTime = Duration.ZERO;
/**
* Creates a new {@link AggregationOptions}.
@@ -78,28 +73,11 @@ public class AggregationOptions {
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation) {
this(allowDiskUse, explain, cursor, collation, null);
}
/**
* Creates a new {@link AggregationOptions}.
*
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
* @param collation collation for string comparison. Can be {@literal null}.
* @param comment execution comment. Can be {@literal null}.
* @since 2.2
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment) {
this.allowDiskUse = allowDiskUse;
this.explain = explain;
this.cursor = Optional.ofNullable(cursor);
this.collation = Optional.ofNullable(collation);
this.comment = Optional.ofNullable(comment);
}
/**
@@ -130,13 +108,8 @@ public class AggregationOptions {
Document cursor = document.get(CURSOR, Document.class);
Collation collation = document.containsKey(COLLATION) ? Collation.from(document.get(COLLATION, Document.class))
: null;
String comment = document.getString(COMMENT);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
if (document.containsKey(MAX_TIME)) {
options.maxTime = Duration.ofMillis(document.getLong(MAX_TIME));
}
return options;
return new AggregationOptions(allowDiskUse, explain, cursor, collation);
}
/**
@@ -203,24 +176,6 @@ public class AggregationOptions {
return collation;
}
/**
* Get the comment for the aggregation.
*
* @return
* @since 2.2
*/
public Optional<String> getComment() {
return comment;
}
/**
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
* @since 3.0
*/
public Duration getMaxTime() {
return maxTime;
}
/**
* Returns a new potentially adjusted copy for the given {@code aggregationCommandObject} with the configuration
* applied.
@@ -248,10 +203,6 @@ public class AggregationOptions {
collation.map(Collation::toDocument).ifPresent(val -> result.append(COLLATION, val));
}
if (hasExecutionTimeLimit() && !result.containsKey(MAX_TIME)) {
result.append(MAX_TIME, maxTime.toMillis());
}
return result;
}
@@ -268,23 +219,10 @@ public class AggregationOptions {
cursor.ifPresent(val -> document.put(CURSOR, val));
collation.ifPresent(val -> document.append(COLLATION, val.toDocument()));
comment.ifPresent(val -> document.append(COMMENT, val));
if (hasExecutionTimeLimit()) {
document.append(MAX_TIME, maxTime.toMillis());
}
return document;
}
/**
* @return
* @since 3.0
*/
public boolean hasExecutionTimeLimit() {
return !maxTime.isZero() && !maxTime.isNegative();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -309,8 +247,6 @@ public class AggregationOptions {
private boolean explain;
private @Nullable Document cursor;
private @Nullable Collation collation;
private @Nullable String comment;
private @Nullable Duration maxTime;
/**
* Defines whether to off-load intensive sort-operations to disk.
@@ -366,7 +302,6 @@ public class AggregationOptions {
*
* @param collation can be {@literal null}.
* @return
* @since 2.0
*/
public Builder collation(@Nullable Collation collation) {
@@ -374,46 +309,13 @@ public class AggregationOptions {
return this;
}
/**
* Define a comment to describe the execution.
*
* @param comment can be {@literal null}.
* @return
* @since 2.2
*/
public Builder comment(@Nullable String comment) {
this.comment = comment;
return this;
}
/**
* Set the time limit for processing.
*
* @param maxTime {@link Duration#ZERO} is used for the default unbounded behavior. {@link Duration#isNegative()
* Negative} values will be ignored.
* @return this.
* @since 3.0
*/
public Builder maxTime(@Nullable Duration maxTime) {
this.maxTime = maxTime;
return this;
}
/**
* Returns a new {@link AggregationOptions} instance with the given configuration.
*
* @return
*/
public AggregationOptions build() {
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
if (maxTime != null) {
options.maxTime = maxTime;
}
return options;
return new AggregationOptions(allowDiskUse, explain, cursor, collation);
}
}
}

View File

@@ -1,325 +0,0 @@
/*
* Copyright 2019-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.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Abstraction for {@code db.collection.update()} using an aggregation pipeline. Aggregation pipeline updates use a more
* expressive update statement expressing conditional updates based on current field values or updating one field using
* the value of another field(s).
*
* <pre class="code">
* AggregationUpdate update = AggregationUpdate.update().set("average")
* .toValue(ArithmeticOperators.valueOf("tests").avg()).set("grade")
* .toValue(ConditionalOperators
* .switchCases(CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(90)).then("A"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(80)).then("B"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(70)).then("C"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(60)).then("D"))
* .defaultTo("F"));
* </pre>
*
* The above sample is equivalent to the JSON update statement:
*
* <pre class="code">
* db.collection.update(
* { },
* [
* { $set: { average : { $avg: "$tests" } } },
* { $set: { grade: { $switch: {
* branches: [
* { case: { $gte: [ "$average", 90 ] }, then: "A" },
* { case: { $gte: [ "$average", 80 ] }, then: "B" },
* { case: { $gte: [ "$average", 70 ] }, then: "C" },
* { case: { $gte: [ "$average", 60 ] }, then: "D" }
* ],
* default: "F"
* } } } }
* ],
* { multi: true }
* )
* </pre>
*
* @author Christoph Strobl
* @author Mark Paluch
* @see <a href=
* "https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline">MongoDB
* Reference Documentation</a>
* @since 3.0
*/
public class AggregationUpdate extends Aggregation implements UpdateDefinition {
private boolean isolated = false;
private Set<String> keysTouched = new HashSet<>();
/**
* Create new {@link AggregationUpdate}.
*/
protected AggregationUpdate() {
this(new ArrayList<>());
}
/**
* Create new {@link AggregationUpdate} with the given aggregation pipeline to apply.
*
* @param pipeline must not be {@literal null}.
*/
protected AggregationUpdate(List<AggregationOperation> pipeline) {
super(pipeline);
for (AggregationOperation operation : pipeline) {
if (operation instanceof FieldsExposingAggregationOperation) {
((FieldsExposingAggregationOperation) operation).getFields().forEach(it -> {
keysTouched.add(it.getName());
});
}
}
}
/**
* Start defining the update pipeline to execute.
*
* @return new instance of {@link AggregationUpdate}.
*/
public static AggregationUpdate update() {
return new AggregationUpdate();
}
/**
* Create a new AggregationUpdate from the given {@link AggregationOperation}s.
*
* @return new instance of {@link AggregationUpdate}.
*/
public static AggregationUpdate from(List<AggregationOperation> pipeline) {
return new AggregationUpdate(pipeline);
}
/**
* Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input
* documents and newly added fields.
*
* @param setOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set/">$set Aggregation Reference</a>
*/
public AggregationUpdate set(SetOperation setOperation) {
Assert.notNull(setOperation, "SetOperation must not be null!");
setOperation.getFields().forEach(it -> {
keysTouched.add(it.getName());
});
operations.add(setOperation);
return this;
}
/**
* {@code $unset} removes/excludes fields from documents.
*
* @param unsetOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset/">$unset Aggregation
* Reference</a>
*/
public AggregationUpdate unset(UnsetOperation unsetOperation) {
Assert.notNull(unsetOperation, "UnsetOperation must not be null!");
operations.add(unsetOperation);
keysTouched.addAll(unsetOperation.removedFieldNames());
return this;
}
/**
* {@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
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">$replaceWith Aggregation
* Reference</a>
*/
public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) {
Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null!");
operations.add(replaceWithOperation);
return this;
}
/**
* {@code $replaceWith} replaces the input document with the value.
*
* @param value must not be {@literal null}.
* @return this.
*/
public AggregationUpdate replaceWith(Object value) {
Assert.notNull(value, "Value must not be null!");
return replaceWith(ReplaceWithOperation.replaceWithValue(value));
}
/**
* Fluent API variant for {@code $set} adding a single {@link SetOperation pipeline operation} every time. To update
* multiple fields within one {@link SetOperation} use {@link #set(SetOperation)}.
*
* @param key must not be {@literal null}.
* @return new instance of {@link SetValueAppender}.
* @see #set(SetOperation)
*/
public SetValueAppender set(String key) {
Assert.notNull(key, "Key must not be null!");
return new SetValueAppender() {
@Override
public AggregationUpdate toValue(@Nullable Object value) {
return set(SetOperation.builder().set(key).toValue(value));
}
@Override
public AggregationUpdate toValueOf(Object value) {
Assert.notNull(value, "Value must not be null!");
return set(SetOperation.builder().set(key).toValueOf(value));
}
};
}
/**
* Short for {@link #unset(UnsetOperation)}.
*
* @param keys
* @return
*/
public AggregationUpdate unset(String... keys) {
Assert.notNull(keys, "Keys must not be null!");
Assert.noNullElements(keys, "Keys must not contain null elements.");
return unset(new UnsetOperation(Arrays.stream(keys).map(Fields::field).collect(Collectors.toList())));
}
/**
* Prevents a write operation that affects <strong>multiple</strong> documents from yielding to other reads or writes
* once the first document is written. <br />
* Use with {@link org.springframework.data.mongodb.core.MongoOperations#updateMulti(Query, UpdateDefinition, Class)}.
*
* @return never {@literal null}.
*/
public AggregationUpdate isolated() {
isolated = true;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#isIsolated()
*/
@Override
public Boolean isIsolated() {
return isolated;
}
/*
* Returns a update document containing the update pipeline.
* The resulting document needs to be unwrapped to be used with update operations.
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getUpdateObject()
*/
@Override
public Document getUpdateObject() {
return new Document("", toPipeline(Aggregation.DEFAULT_CONTEXT));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#modifies(java.lang.String)
*/
@Override
public boolean modifies(String key) {
return keysTouched.contains(key);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#inc(java.lang.String)
*/
@Override
public void inc(String key) {
set(new SetOperation(key, ArithmeticOperators.valueOf(key).add(1)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getArrayFilters()
*/
@Override
public List<ArrayFilter> getArrayFilters() {
return Collections.emptyList();
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",\n", "[\n", "\n]");
toPipeline(Aggregation.DEFAULT_CONTEXT).stream().map(SerializationUtils::serializeToJsonSafely)
.forEach(joiner::add);
return joiner.toString();
}
/**
* Fluent API AggregationUpdate builder.
*
* @author Christoph Strobl
*/
public interface SetValueAppender {
/**
* Define the target value as is.
*
* @param value can be {@literal null}.
* @return never {@literal null}.
*/
AggregationUpdate toValue(@Nullable Object value);
/**
* Define the target value as value, an {@link AggregationExpression} or a {@link Field} reference.
*
* @param value can be {@literal null}.
* @return never {@literal null}.
*/
AggregationUpdate toValueOf(Object value);
}
}

View File

@@ -511,27 +511,6 @@ public class ArithmeticOperators {
: AccumulatorOperators.StdDevSamp.stdDevSampOf(expression);
}
/**
* Creates new {@link AggregationExpression} that rounds a number to a whole integer or to a specified decimal
* place.
*
* @return new instance of {@link Round}.
* @since 3.0
*/
public Round round() {
return usesFieldRef() ? Round.roundValueOf(fieldReference) : Round.roundValueOf(expression);
}
/**
* Creates new {@link AggregationExpression} that rounds a number to a specified decimal place.
*
* @return new instance of {@link Round}.
* @since 3.0
*/
public Round roundToPlace(int place) {
return round().place(place);
}
private boolean usesFieldRef() {
return fieldReference != null;
}
@@ -1443,100 +1422,4 @@ public class ArithmeticOperators {
return new Trunc(value);
}
}
/**
* {@link Round} rounds a number to a whole integer or to a specified decimal place.
* <ul>
* <li>If {@link Round#place(int)} resolves to a positive integer, {@code $round} rounds to the given decimal
* places.</li>
* <li>If {@link Round#place(int)} resolves to a negative integer, {@code $round} rounds to the left of the
* decimal.</li>
* <li>If {@link Round#place(int)} resolves to a zero, {@code $round} rounds using the first digit to the right of the
* decimal.</li>
* </ul>
*
* @since 3.0
*/
public static class Round extends AbstractAggregationExpression {
private Round(Object value) {
super(value);
}
/**
* Round the value of the field that resolves to an integer, double, decimal, or long.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round roundValueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Round(Collections.singletonList(Fields.field(fieldReference)));
}
/**
* Round the outcome of the given expression hat resolves to an integer, double, decimal, or long.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round roundValueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Round(Collections.singletonList(expression));
}
/**
* Round the given numeric (integer, double, decimal, or long) value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round round(Number value) {
Assert.notNull(value, "Value must not be null!");
return new Round(Collections.singletonList(value));
}
/**
* The place to round to. Can be between -20 and 100, exclusive.
*
* @param place
* @return new instance of {@link Round}.
*/
public Round place(int place) {
return new Round(append(place));
}
/**
* The place to round to defined by an expression that resolves to an integer between -20 and 100, exclusive.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public Round placeOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Round(append(expression));
}
/**
* The place to round to defined by via a field reference that resolves to an integer between -20 and 100,
* exclusive.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public Round placeOf(String fieldReference) {
Assert.notNull(fieldReference, "fieldReference must not be null!");
return new Round(append(Fields.field(fieldReference)));
}
@Override
protected String getMongoMethod() {
return "$round";
}
}
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -34,7 +33,6 @@ import org.springframework.util.Assert;
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Shashank Sharma
* @since 1.0
*/
public class ArrayOperators {
@@ -59,25 +57,13 @@ public class ArrayOperators {
return new ArrayOperatorFactory(expression);
}
/**
* Take the given {@link Collection values} {@link AggregationExpression}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link ArrayOperatorFactory}.
* @since 2.2
*/
public static ArrayOperatorFactory arrayOf(Collection<?> values) {
return new ArrayOperatorFactory(values);
}
/**
* @author Christoph Strobl
*/
public static class ArrayOperatorFactory {
private final @Nullable String fieldReference;
private final @Nullable AggregationExpression expression;
private final @Nullable Collection values;
private final String fieldReference;
private final AggregationExpression expression;
/**
* Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}.
@@ -89,7 +75,6 @@ public class ArrayOperators {
Assert.notNull(fieldReference, "FieldReference must not be null!");
this.fieldReference = fieldReference;
this.expression = null;
this.values = null;
}
/**
@@ -102,21 +87,6 @@ public class ArrayOperators {
Assert.notNull(expression, "Expression must not be null!");
this.fieldReference = null;
this.expression = expression;
this.values = null;
}
/**
* Creates new {@link ArrayOperatorFactory} for given values.
*
* @param values must not be {@literal null}.
* @since 2.2
*/
public ArrayOperatorFactory(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
this.fieldReference = null;
this.expression = null;
this.values = values;
}
/**
@@ -157,12 +127,7 @@ public class ArrayOperators {
}
private ArrayElemAt createArrayElemAt() {
if (usesFieldRef()) {
return ArrayElemAt.arrayOf(fieldReference);
}
return usesExpression() ? ArrayElemAt.arrayOf(expression) : ArrayElemAt.arrayOf(values);
return usesFieldRef() ? ArrayElemAt.arrayOf(fieldReference) : ArrayElemAt.arrayOf(expression);
}
/**
@@ -192,12 +157,7 @@ public class ArrayOperators {
}
private ConcatArrays createConcatArrays() {
if (usesFieldRef()) {
return ConcatArrays.arrayOf(fieldReference);
}
return usesExpression() ? ConcatArrays.arrayOf(expression) : ConcatArrays.arrayOf(values);
return usesFieldRef() ? ConcatArrays.arrayOf(fieldReference) : ConcatArrays.arrayOf(expression);
}
/**
@@ -207,13 +167,7 @@ public class ArrayOperators {
* @return
*/
public AsBuilder filter() {
if (usesFieldRef()) {
return Filter.filter(fieldReference);
}
Assert.state(values != null, "Values must not be null!");
return Filter.filter(new ArrayList<>(values));
return Filter.filter(fieldReference);
}
/**
@@ -222,9 +176,6 @@ public class ArrayOperators {
* @return
*/
public IsArray isArray() {
Assert.state(values == null, "Does it make sense to call isArray on an array? Maybe just skip it?");
return usesFieldRef() ? IsArray.isArray(fieldReference) : IsArray.isArray(expression);
}
@@ -234,12 +185,7 @@ public class ArrayOperators {
* @return
*/
public Size length() {
if (usesFieldRef()) {
return Size.lengthOfArray(fieldReference);
}
return usesExpression() ? Size.lengthOfArray(expression) : Size.lengthOfArray(values);
return usesFieldRef() ? Size.lengthOfArray(fieldReference) : Size.lengthOfArray(expression);
}
/**
@@ -248,12 +194,7 @@ public class ArrayOperators {
* @return
*/
public Slice slice() {
if (usesFieldRef()) {
return Slice.sliceArrayOf(fieldReference);
}
return usesExpression() ? Slice.sliceArrayOf(expression) : Slice.sliceArrayOf(values);
return usesFieldRef() ? Slice.sliceArrayOf(fieldReference) : Slice.sliceArrayOf(expression);
}
/**
@@ -264,13 +205,8 @@ public class ArrayOperators {
* @return
*/
public IndexOfArray indexOf(Object value) {
if (usesFieldRef()) {
return IndexOfArray.arrayOf(fieldReference).indexOf(value);
}
return usesExpression() ? IndexOfArray.arrayOf(expression).indexOf(value)
: IndexOfArray.arrayOf(values).indexOf(value);
return usesFieldRef() ? IndexOfArray.arrayOf(fieldReference).indexOf(value)
: IndexOfArray.arrayOf(expression).indexOf(value);
}
/**
@@ -279,13 +215,7 @@ public class ArrayOperators {
* @return
*/
public ReverseArray reverse() {
if (usesFieldRef()) {
return ReverseArray.reverseArrayOf(fieldReference);
}
return usesExpression() ? ReverseArray.reverseArrayOf(expression)
: ReverseArray.reverseArrayOf(Collections.singletonList(values));
return usesFieldRef() ? ReverseArray.reverseArrayOf(fieldReference) : ReverseArray.reverseArrayOf(expression);
}
/**
@@ -323,12 +253,7 @@ public class ArrayOperators {
* @return
*/
public Zip zipWith(Object... arrays) {
if (usesFieldRef()) {
return Zip.arrayOf(fieldReference).zip(arrays);
}
return (usesExpression() ? Zip.arrayOf(expression) : Zip.arrayOf(values)).zip(arrays);
return (usesFieldRef() ? Zip.arrayOf(fieldReference) : Zip.arrayOf(expression)).zip(arrays);
}
/**
@@ -339,12 +264,7 @@ public class ArrayOperators {
* @return
*/
public In containsValue(Object value) {
if (usesFieldRef()) {
return In.arrayOf(fieldReference).containsValue(value);
}
return (usesExpression() ? In.arrayOf(expression) : In.arrayOf(values)).containsValue(value);
return (usesFieldRef() ? In.arrayOf(fieldReference) : In.arrayOf(expression)).containsValue(value);
}
/**
@@ -356,11 +276,8 @@ public class ArrayOperators {
*/
public ArrayToObject toObject() {
if (usesFieldRef()) {
return ArrayToObject.arrayValueOfToObject(fieldReference);
}
return usesExpression() ? ArrayToObject.arrayValueOfToObject(expression) : ArrayToObject.arrayToObject(values);
return usesFieldRef() ? ArrayToObject.arrayValueOfToObject(fieldReference)
: ArrayToObject.arrayValueOfToObject(expression);
}
/**
@@ -377,20 +294,9 @@ public class ArrayOperators {
Reduce startingWith(Object initialValue);
}
/**
* @return {@literal true} if {@link #fieldReference} is not {@literal null}.
*/
private boolean usesFieldRef() {
return fieldReference != null;
}
/**
* @return {@literal true} if {@link #expression} is not {@literal null}.
* @since 2.2
*/
private boolean usesExpression() {
return expression != null;
}
}
/**
@@ -433,19 +339,6 @@ public class ArrayOperators {
return new ArrayElemAt(Collections.singletonList(expression));
}
/**
* Creates new {@link ArrayElemAt}.
*
* @param values The array members. Must not be {@literal null}.
* @return new instance of {@link ArrayElemAt}.
* @since 2.2
*/
public static ArrayElemAt arrayOf(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return new ArrayElemAt(Collections.singletonList(values));
}
public ArrayElemAt elementAt(int index) {
return new ArrayElemAt(append(index));
}
@@ -503,19 +396,6 @@ public class ArrayOperators {
return new ConcatArrays(Collections.singletonList(expression));
}
/**
* Creates new {@link ConcatArrays}.
*
* @param values The array members. Must not be {@literal null}.
* @return new instance of {@link ConcatArrays}.
* @since 2.2
*/
public static ConcatArrays arrayOf(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return new ConcatArrays(Collections.singletonList(values));
}
public ConcatArrays concat(String arrayFieldReference) {
Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null!");
@@ -860,19 +740,6 @@ public class ArrayOperators {
Assert.notNull(expression, "Expression must not be null!");
return new Size(expression);
}
/**
* Creates new {@link Size}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link Size}.
* @since 2.2
*/
public static Size lengthOfArray(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return new Size(Collections.singletonList(values));
}
}
/**
@@ -915,19 +782,6 @@ public class ArrayOperators {
return new Slice(Collections.singletonList(expression));
}
/**
* Creates new {@link Slice}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link Slice}.
* @since 2.2
*/
public static Slice sliceArrayOf(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return new Slice(Collections.singletonList(values));
}
public Slice itemCount(int nrElements) {
return new Slice(append(nrElements));
}
@@ -998,19 +852,6 @@ public class ArrayOperators {
return new IndexOfArrayBuilder(expression);
}
/**
* Start creating new {@link IndexOfArray}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link IndexOfArray}.
* @since 2.2
*/
public static IndexOfArrayBuilder arrayOf(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return new IndexOfArrayBuilder(values);
}
public IndexOfArray within(Range<Long> range) {
return new IndexOfArray(append(AggregationUtils.toRangeValues(range)));
}
@@ -1165,17 +1006,6 @@ public class ArrayOperators {
public static ReverseArray reverseArrayOf(AggregationExpression expression) {
return new ReverseArray(expression);
}
/**
* Creates new {@link ReverseArray}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link ReverseArray}.
* @since 2.2
*/
public static ReverseArray reverseArrayOf(Collection<?> values) {
return new ReverseArray(values);
}
}
/**
@@ -1528,19 +1358,6 @@ public class ArrayOperators {
return new ZipBuilder(expression);
}
/**
* Start creating new {@link Zip}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link Zip}.
* @since 2.2
*/
public static ZipBuilder arrayOf(Collection<?> values) {
Assert.notNull(values, "Expression must not be null!");
return new ZipBuilder(values);
}
/**
* Create new {@link Zip} and set the {@code useLongestLength} property to {@literal true}.
*
@@ -1625,10 +1442,6 @@ public class ArrayOperators {
* {@link AggregationExpression} for {@code $in}.
*
* @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>
* @since 2.2
*/
public static class In extends AbstractAggregationExpression {
@@ -1647,14 +1460,18 @@ public class ArrayOperators {
* @param fieldReference must not be {@literal null}.
* @return
*/
public static InBuilder arrayOf(String fieldReference) {
public static InBuilder arrayOf(final String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return value -> {
return new InBuilder() {
Assert.notNull(value, "Value must not be null!");
return new In(Arrays.asList(value, Fields.field(fieldReference)));
@Override
public In containsValue(Object value) {
Assert.notNull(value, "Value must not be null!");
return new In(Arrays.asList(value, Fields.field(fieldReference)));
}
};
}
@@ -1664,34 +1481,18 @@ public class ArrayOperators {
* @param expression must not be {@literal null}.
* @return
*/
public static InBuilder arrayOf(AggregationExpression expression) {
public static InBuilder arrayOf(final AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return value -> {
return new InBuilder() {
Assert.notNull(value, "Value must not be null!");
@Override
public In containsValue(Object value) {
return new In(Arrays.asList(value, expression));
};
}
/**
* Support for Aggregation In Search an Element in List of Objects to Filter Start creating {@link In}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link InBuilder}.
* @since 2.2
*/
public static InBuilder arrayOf(Collection<?> values) {
Assert.notNull(values, "Values must not be null!");
return value -> {
Assert.notNull(value, "Value must not be null!");
return new In(Arrays.asList(value, values));
Assert.notNull(value, "Value must not be null!");
return new In(Arrays.asList(value, expression));
}
};
}

View File

@@ -56,11 +56,11 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document, java.lang.Class)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document)
*/
@Override
public Document getMappedObject(Document document, @Nullable Class<?> type) {
return rootContext.getMappedObject(document, type);
public Document getMappedObject(Document document) {
return rootContext.getMappedObject(document);
}
/*
@@ -86,15 +86,6 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
return getReference(null, name);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
*/
@Override
public Fields getFields(Class<?> type) {
return rootContext.getFields(type);
}
/**
* Returns a {@link FieldReference} to the given {@link Field} with the given {@code name}.
*

View File

@@ -46,7 +46,7 @@ public interface Field {
/**
* @return true if the field name references a local value such as {@code $$this}.
* @since 2.2
* @since 2.1.11
*/
default boolean isInternal() {
return false;

View File

@@ -112,7 +112,7 @@ public final class Fields implements Iterable<Field> {
this.fields = verify(fields);
}
private static List<Field> verify(List<Field> fields) {
private static final List<Field> verify(List<Field> fields) {
Map<String, Field> reference = new HashMap<String, Field>();

View File

@@ -15,15 +15,10 @@
*/
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.data.mongodb.core.query.NearQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
/**
@@ -94,15 +89,6 @@ public class GeoNearOperation implements AggregationOperation {
public Document toDocument(AggregationOperationContext context) {
Document command = context.getMappedObject(nearQuery.toDocument());
if(command.containsKey("query")) {
command.replace("query", context.getMappedObject(command.get("query", Document.class)));
}
if(command.containsKey("collation")) {
command.remove("collation");
}
command.put("distanceField", distanceField);
if (StringUtils.hasText(indexKey)) {
@@ -111,28 +97,4 @@ public class GeoNearOperation implements AggregationOperation {
return new Document("$geoNear", command);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toPipelineStages(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public List<Document> toPipelineStages(AggregationOperationContext context) {
Document command = toDocument(context);
Number limit = (Number) command.get("$geoNear", Document.class).remove("num");
List<Document> stages = new ArrayList<>();
stages.add(command);
if(nearQuery.getSkip() != null && nearQuery.getSkip() > 0){
stages.add(new Document("$skip", nearQuery.getSkip()));
}
if(limit != null) {
stages.add(new Document("$limit", limit.longValue()));
}
return stages;
}
}

View File

@@ -128,13 +128,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation
*/
@Override
public ExposedFields getFields() {
List<ExposedField> fields = new ArrayList<>(2);
fields.add(new ExposedField(as, true));
if(depthField != null) {
fields.add(new ExposedField(depthField, true));
}
return ExposedFields.from(fields.toArray(new ExposedField[0]));
return ExposedFields.from(new ExposedField(as, true));
}
/**

View File

@@ -59,15 +59,6 @@ class NestedDelegatingExpressionAggregationOperationContext implements Aggregati
return delegate.getMappedObject(document);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document, java.lang.Class)
*/
@Override
public Document getMappedObject(Document document, Class<?> type) {
return delegate.getMappedObject(document, type);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.Field)
@@ -103,13 +94,4 @@ class NestedDelegatingExpressionAggregationOperationContext implements Aggregati
public FieldReference getReference(String name) {
return new ExpressionFieldReference(delegate.getReference(name));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
*/
@Override
public Fields getFields(Class<?> type) {
return delegate.getFields(type);
}
}

View File

@@ -16,11 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Encapsulates the {@code $out}-operation.
@@ -30,161 +26,18 @@ import org.springframework.util.StringUtils;
*
* @author Nikolay Bogdanov
* @author Christoph Strobl
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/out/">MongoDB Aggregation Framework:
* $out</a>
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/out/">MongoDB Aggregation Framework: $out</a>
*/
public class OutOperation implements AggregationOperation {
private final @Nullable String databaseName;
private final String collectionName;
private final @Nullable Document uniqueKey;
private final @Nullable OutMode mode;
/**
* @param outCollectionName Collection name to export the results. Must not be {@literal null}.
*/
public OutOperation(String outCollectionName) {
this(null, outCollectionName, null, null);
}
/**
* @param databaseName Optional database name the target collection is located in. Can be {@literal null}.
* @param collectionName Collection name to export the results. Must not be {@literal null}. Can be {@literal null}.
* @param uniqueKey Optional unique key spec identify a document in the to collection for replacement or merge.
* @param mode The mode for merging the aggregation pipeline output with the target collection. Can be
* {@literal null}. {@literal null}.
* @since 2.2
*/
private OutOperation(@Nullable String databaseName, String collectionName, @Nullable Document uniqueKey,
@Nullable OutMode mode) {
Assert.notNull(collectionName, "Collection name must not be null!");
this.databaseName = databaseName;
this.collectionName = collectionName;
this.uniqueKey = uniqueKey;
this.mode = mode;
}
/**
* Optionally specify the database of the target collection. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @param database can be {@literal null}. Defaulted to aggregation target database.
* @return new instance of {@link OutOperation}.
* @since 2.2
*/
public OutOperation in(@Nullable String database) {
return new OutOperation(database, collectionName, uniqueKey, mode);
}
/**
* Optionally specify the field that uniquely identifies a document in the target collection. <br />
* For convenience the given {@literal key} can either be a single field name or the Json representation of a key
* {@link Document}.
*
* <pre class="code">
*
* // {
* // "field-1" : 1
* // }
* .uniqueKey("field-1")
*
* // {
* // "field-1" : 1,
* // "field-2" : 1
* // }
* .uniqueKey("{ 'field-1' : 1, 'field-2' : 1}")
* </pre>
*
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @param key can be {@literal null}. Server uses {@literal _id} when {@literal null}.
* @return new instance of {@link OutOperation}.
* @since 2.2
*/
public OutOperation uniqueKey(@Nullable String key) {
Document uniqueKey = key == null ? null : BsonUtils.toDocumentOrElse(key, it -> new Document(it, 1));
return new OutOperation(databaseName, collectionName, uniqueKey, mode);
}
/**
* Optionally specify the fields that uniquely identifies a document in the target collection. <br />
*
* <pre class="code">
*
* // {
* // "field-1" : 1
* // "field-2" : 1
* // }
* .uniqueKeyOf(Arrays.asList("field-1", "field-2"))
* </pre>
*
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @param fields must not be {@literal null}.
* @return new instance of {@link OutOperation}.
* @since 2.2
*/
public OutOperation uniqueKeyOf(Iterable<String> fields) {
Assert.notNull(fields, "Fields must not be null!");
Document uniqueKey = new Document();
fields.forEach(it -> uniqueKey.append(it, 1));
return new OutOperation(databaseName, collectionName, uniqueKey, mode);
}
/**
* Specify how to merge the aggregation output with the target collection. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @param mode must not be {@literal null}.
* @return new instance of {@link OutOperation}.
* @since 2.2
*/
public OutOperation mode(OutMode mode) {
Assert.notNull(mode, "Mode must not be null!");
return new OutOperation(databaseName, collectionName, uniqueKey, mode);
}
/**
* Replace the target collection. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @return new instance of {@link OutOperation}.
* @see OutMode#REPLACE_COLLECTION
* @since 2.2
*/
public OutOperation replaceCollection() {
return mode(OutMode.REPLACE_COLLECTION);
}
/**
* Replace/Upsert documents in the target collection. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @return new instance of {@link OutOperation}.
* @see OutMode#REPLACE
* @since 2.2
*/
public OutOperation replaceDocuments() {
return mode(OutMode.REPLACE);
}
/**
* Insert documents to the target collection. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.2 or later.
*
* @return new instance of {@link OutOperation}.
* @see OutMode#INSERT
* @since 2.2
*/
public OutOperation insertDocuments() {
return mode(OutMode.INSERT);
Assert.notNull(outCollectionName, "Collection name must not be null!");
this.collectionName = outCollectionName;
}
/*
@@ -193,62 +46,6 @@ public class OutOperation implements AggregationOperation {
*/
@Override
public Document toDocument(AggregationOperationContext context) {
if (!requiresMongoDb42Format()) {
return new Document("$out", collectionName);
}
Assert.state(mode != null, "Mode must not be null!");
Document $out = new Document("to", collectionName) //
.append("mode", mode.getMongoMode());
if (StringUtils.hasText(databaseName)) {
$out.append("db", databaseName);
}
if (uniqueKey != null) {
$out.append("uniqueKey", uniqueKey);
}
return new Document("$out", $out);
}
private boolean requiresMongoDb42Format() {
return StringUtils.hasText(databaseName) || mode != null || uniqueKey != null;
}
/**
* The mode for merging the aggregation pipeline output.
*
* @author Christoph Strobl
* @since 2.2
*/
public enum OutMode {
/**
* Write documents to the target collection. Errors if a document same uniqueKey already exists.
*/
INSERT("insertDocuments"),
/**
* Update on any document in the target collection with the same uniqueKey.
*/
REPLACE("replaceDocuments"),
/**
* Replaces the to collection with the output from the aggregation pipeline. Cannot be in a different database.
*/
REPLACE_COLLECTION("replaceCollection");
private String mode;
OutMode(String mode) {
this.mode = mode;
}
public String getMongoMode() {
return mode;
}
return new Document("$out", collectionName);
}
}

View File

@@ -25,7 +25,6 @@ import java.util.Set;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.lang.Nullable;
/**
* {@link AggregationOperationContext} implementation prefixing non-command keys on root level with the given prefix.
@@ -64,15 +63,6 @@ public class PrefixingDelegatingAggregationOperationContext implements Aggregati
return doPrefix(delegate.getMappedObject(document));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document, java.lang.Class)
*/
@Override
public Document getMappedObject(Document document, @Nullable Class<?> type) {
return doPrefix(delegate.getMappedObject(document, type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.Field)
@@ -91,15 +81,6 @@ public class PrefixingDelegatingAggregationOperationContext implements Aggregati
return delegate.getReference(name);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
*/
@Override
public Fields getFields(Class<?> type) {
return delegate.getFields(type);
}
@SuppressWarnings("unchecked")
private Document doPrefix(Document source) {

View File

@@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond;
@@ -74,16 +73,6 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields));
}
/**
* Creates a new {@link ProjectionOperation} including all top level fields of the given {@link Class type}.
*
* @param type must not be {@literal null}.
* @since 2.2
*/
public ProjectionOperation(Class<?> type) {
this(NONE, Collections.singletonList(new TypeProjection(type)));
}
/**
* Copy constructor to allow building up {@link ProjectionOperation} instances from already existing
* {@link Projection}s.
@@ -177,48 +166,6 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
return new ProjectionOperation(this.projections, FieldProjection.from(fields, true));
}
/**
* Includes the current {@link ProjectionOperation} as an array with given name. <br />
* If you want to specify array values directly use {@link #andArrayOf(Object...)}.
*
* @param name the target property name.
* @return new instance of {@link ProjectionOperation}.
* @since 2.2
*/
public ProjectionOperation asArray(String name) {
return new ProjectionOperation(Collections.emptyList(),
Collections.singletonList(new ArrayProjection(Fields.field(name), (List) this.projections)));
}
/**
* Includes the given values ({@link Field field references}, {@link AggregationExpression expression}, plain values)
* as an array. <br />
* The target property name needs to be set via {@link ArrayProjectionOperationBuilder#as(String)}.
*
* @param values must not be {@literal null}.
* @return new instance of {@link ArrayProjectionOperationBuilder}.
* @throws IllegalArgumentException if the required argument it {@literal null}.
* @since 2.2
*/
public ArrayProjectionOperationBuilder andArrayOf(Object... values) {
ArrayProjectionOperationBuilder builder = new ArrayProjectionOperationBuilder(this);
for (Object value : values) {
if (value instanceof Field) {
builder.and((Field) value);
} else if (value instanceof AggregationExpression) {
builder.and((AggregationExpression) value);
} else {
builder.and(value);
}
}
return builder;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
@@ -1548,8 +1495,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
final Field aliasedField = Fields.field(alias, this.field.getName());
return new OperationProjection(aliasedField, operation, values.toArray()) {
/*
* (non-Javadoc)
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.OperationProjection#getField()
*/
@Override
@@ -1749,164 +1695,9 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
this.expression = expression;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document(field.getName(), expression.toDocument(context));
}
}
/**
* A {@link Projection} including all top level fields of the given target type mapped to include potentially
* deviating field names.
*
* @since 2.2
* @author Christoph Strobl
*/
static class TypeProjection extends Projection {
private final Class<?> type;
TypeProjection(Class<?> type) {
super(Fields.field(type.getSimpleName()));
this.type = type;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
Document projections = new Document();
Fields fields = context.getFields(type);
fields.forEach(it -> projections.append(it.getName(), 1));
return context.getMappedObject(projections, type);
}
}
/**
* Builder for {@code array} projections.
*
* @author Christoph Strobl
* @since 2.2
*/
public static class ArrayProjectionOperationBuilder {
private ProjectionOperation target;
private final List<Object> projections;
public ArrayProjectionOperationBuilder(ProjectionOperation target) {
this.target = target;
this.projections = new ArrayList<>();
}
/**
* Creates a new {@link ArrayProjectionOperationBuilder} with the current {@link Projection}s and the given one.
*
* @param expression
* @return
*/
public ArrayProjectionOperationBuilder and(AggregationExpression expression) {
Assert.notNull(expression, "AggregationExpression must not be null!");
this.projections.add(expression);
return this;
}
/**
* Creates a new {@link ArrayProjectionOperationBuilder} with the current {@link Projection}s and the given one.
*
* @param field
* @return
*/
public ArrayProjectionOperationBuilder and(Field field) {
Assert.notNull(field, "Field must not be null!");
this.projections.add(field);
return this;
}
/**
* Creates a new {@link ArrayProjectionOperationBuilder} with the current {@link Projection}s and the given one.
*
* @param value
* @return
*/
public ArrayProjectionOperationBuilder and(Object value) {
this.projections.add(value);
return this;
}
/**
* Create the {@link ProjectionOperation} for the array property with given {@literal name}.
*
* @param name The target property name. Must not be {@literal null}.
* @return new instance of {@link ArrayProjectionOperationBuilder}.
*/
public ProjectionOperation as(String name) {
return new ProjectionOperation(target.projections,
Collections.singletonList(new ArrayProjection(Fields.field(name), this.projections)));
}
}
/**
* @author Christoph Strobl
* @since 2.2
*/
static class ArrayProjection extends Projection {
private final Field targetField;
private final List<Object> projections;
public ArrayProjection(Field targetField, List<Object> projections) {
super(targetField);
this.targetField = targetField;
this.projections = projections;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document(targetField.getName(),
projections.stream().map(it -> toArrayEntry(it, context)).collect(Collectors.toList()));
}
private Object toArrayEntry(Object projection, AggregationOperationContext ctx) {
if (projection instanceof Field) {
return ctx.getReference((Field) projection).toString();
}
if (projection instanceof AggregationExpression) {
return ((AggregationExpression) projection).toDocument(ctx);
}
if (projection instanceof FieldProjection) {
return ctx.getReference(((FieldProjection) projection).getExposedField().getTarget()).toString();
}
if (projection instanceof Projection) {
((Projection) projection).toDocument(ctx);
}
return projection;
}
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2019-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 org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
/**
* A {@link TypeBasedAggregationOperationContext} with less restrictive field reference handling, suppressing
* {@link InvalidPersistentPropertyPath} exceptions when resolving mapped field names.
*
* @author Christoph Strobl
* @since 3.0
*/
public class RelaxedTypeBasedAggregationOperationContext extends TypeBasedAggregationOperationContext {
/**
* Creates a new {@link TypeBasedAggregationOperationContext} for the given type, {@link MappingContext} and
* {@link QueryMapper}.
*
* @param type must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
* @param mapper must not be {@literal null}.
*/
public RelaxedTypeBasedAggregationOperationContext(Class<?> type,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, QueryMapper mapper) {
super(type, mappingContext, mapper);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext#getReferenceFor(rg.springframework.data.mongodb.core.aggregation.Field)
*/
@Override
protected FieldReference getReferenceFor(Field field) {
try {
return super.getReferenceFor(field);
} catch (InvalidPersistentPropertyPath e) {
return new DirectFieldReference(new ExposedField(field, true));
}
}
}

View File

@@ -33,8 +33,7 @@ import org.springframework.util.Assert;
* @author Mark Paluch
* @author Christoph Strobl
* @since 1.10
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/">MongoDB Aggregation
* Framework: $replaceRoot</a>
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/">MongoDB Aggregation Framework: $replaceRoot</a>
*/
public class ReplaceRootOperation implements FieldsExposingAggregationOperation {
@@ -83,7 +82,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation
*/
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$replaceRoot", new Document("newRoot", getReplacement().toDocumentExpression(context)));
return new Document("$replaceRoot", new Document("newRoot", replacement.toDocumentExpression(context)));
}
/* (non-Javadoc)
@@ -94,16 +93,6 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation
return ExposedFields.from();
}
/**
* Obtain the {@link Replacement}.
*
* @return never {@literal null}.
* @since 3.0
*/
protected Replacement getReplacement() {
return replacement;
}
/**
* Builder for {@link ReplaceRootOperation}.
*

View File

@@ -1,95 +0,0 @@
/*
* Copyright 2019-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.Collection;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.util.Assert;
/**
* Encapsulates the aggregation framework {@code $replaceRoot}-operation. <br />
* The operation replaces all existing fields including the {@code id} field with @{code $replaceWith}. This way it is
* possible to promote an embedded document to the top-level or specify a new document.
*
* @author Christoph Strobl
* @since 3.0
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">MongoDB Aggregation
* Framework: $replaceWith</a>
*/
public class ReplaceWithOperation extends ReplaceRootOperation {
/**
* Creates new instance of {@link ReplaceWithOperation}.
*
* @param replacement must not be {@literal null}.
*/
public ReplaceWithOperation(Replacement replacement) {
super(replacement);
}
/**
* Creates new instance of {@link ReplaceWithOperation}.
*
* @param value must not be {@literal null}.
* @return new instance of {@link ReplaceWithOperation}.
*/
public static ReplaceWithOperation replaceWithValue(Object value) {
return new ReplaceWithOperation((ctx) -> value);
}
/**
* Creates new instance of {@link ReplaceWithOperation} treating a given {@link String} {@literal value} as a
* {@link Field field reference}.
*
* @param value must not be {@literal null}.
* @return
*/
public static ReplaceWithOperation replaceWithValueOf(Object value) {
Assert.notNull(value, "Value must not be null!");
return new ReplaceWithOperation((ctx) -> {
Object target = value instanceof String ? Fields.field((String) value) : value;
return computeValue(target, ctx);
});
}
private static Object computeValue(Object value, AggregationOperationContext context) {
if (value instanceof Field) {
return context.getReference((Field) value).toString();
}
if (value instanceof AggregationExpression) {
return ((AggregationExpression) value).toDocument(context);
}
if (value instanceof Collection) {
return ((Collection) value).stream().map(it -> computeValue(it, context)).collect(Collectors.toList());
}
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
return context.getMappedObject(new Document("$replaceWith", getReplacement().toDocumentExpression(context)));
}
}

View File

@@ -1,250 +0,0 @@
/*
* Copyright 2019-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.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.data.mongodb.core.aggregation.SetOperation.FieldAppender.ValueAppender;
import org.springframework.lang.Nullable;
/**
* Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input
* documents and newly added fields.
*
* <pre class="code">
* SetOperation.set("totalHomework").toValue("A+").and().set("totalQuiz").toValue("B-")
* </pre>
*
* @author Christoph Strobl
* @since 3.0
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set/">MongoDB Aggregation Framework:
* $set</a>
*/
public class SetOperation implements InheritsFieldsAggregationOperation {
private Map<Object, Object> valueMap;
private ExposedFields exposedFields = ExposedFields.empty();
/**
* Create new instance of {@link SetOperation} adding map keys as exposed fields.
*
* @param source must not be {@literal null}.
*/
private SetOperation(Map<Object, Object> source) {
this.valueMap = new LinkedHashMap<>(source);
for (Object key : source.keySet()) {
this.exposedFields = add(key);
}
}
/**
* Create new instance of {@link SetOperation}
*
* @param field must not be {@literal null}.
* @param value can be {@literal null}.
*/
public SetOperation(Object field, @Nullable Object value) {
this(Collections.singletonMap(field, value));
}
/**
* Define the {@link SetOperation} via {@link FieldAppender}.
*
* @return new instance of {@link FieldAppender}.
*/
public static FieldAppender builder() {
return new FieldAppender();
}
/**
* Concatenate another field to set.
*
* @param field must not be {@literal null}.
* @return new instance of {@link ValueAppender}.
*/
public static ValueAppender set(String field) {
return new FieldAppender().set(field);
}
/**
* Append the value for a specific field to the operation.
*
* @param field the target field to set.
* @param value the value to assign.
* @return new instance of {@link SetOperation}.
*/
public SetOperation set(Object field, Object value) {
LinkedHashMap<Object, Object> target = new LinkedHashMap<>(this.valueMap);
target.put(field, value);
return new SetOperation(target);
}
/**
* Concatenate additional fields to set.
*
* @return new instance of {@link FieldAppender}.
*/
public FieldAppender and() {
return new FieldAppender(this.valueMap);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
exposedFields, context);
if (valueMap.size() == 1) {
return context
.getMappedObject(new Document("$set", toSetEntry(valueMap.entrySet().iterator().next(), operationContext)));
}
Document $set = new Document();
valueMap.entrySet().stream().map(it -> toSetEntry(it, operationContext)).forEach($set::putAll);
return context.getMappedObject(new Document("$set", $set));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
*/
@Override
public ExposedFields getFields() {
return exposedFields;
}
private ExposedFields add(Object field) {
if (field instanceof Field) {
return exposedFields.and(new ExposedField((Field) field, true));
}
if (field instanceof String) {
return exposedFields.and(new ExposedField(Fields.field((String) field), true));
}
throw new IllegalArgumentException(String.format("Expected %s to be a field/property.", field));
}
private static Document toSetEntry(Entry<Object, Object> entry, AggregationOperationContext context) {
String field = entry.getKey() instanceof String ? context.getReference((String) entry.getKey()).getRaw()
: context.getReference((Field) entry.getKey()).getRaw();
Object value = computeValue(entry.getValue(), context);
return new Document(field, value);
}
private static Object computeValue(Object value, AggregationOperationContext context) {
if (value instanceof Field) {
return context.getReference((Field) value).toString();
}
if (value instanceof AggregationExpression) {
return ((AggregationExpression) value).toDocument(context);
}
if (value instanceof Collection) {
return ((Collection) value).stream().map(it -> computeValue(it, context)).collect(Collectors.toList());
}
return value;
}
/**
* @author Christoph Strobl
* @since 3.0
*/
public static class FieldAppender {
private final Map<Object, Object> valueMap;
private FieldAppender() {
this.valueMap = new LinkedHashMap<>();
}
private FieldAppender(Map<Object, Object> source) {
this.valueMap = new LinkedHashMap<>(source);
}
/**
* Define the field to set.
*
* @param field must not be {@literal null}.
* @return new instance of {@link ValueAppender}.
*/
public ValueAppender set(String field) {
return new ValueAppender() {
@Override
public SetOperation toValue(Object value) {
valueMap.put(field, value);
return FieldAppender.this.build();
}
@Override
public SetOperation toValueOf(Object value) {
valueMap.put(field, value instanceof String ? Fields.fields((String) value) : value);
return FieldAppender.this.build();
}
};
}
private SetOperation build() {
return new SetOperation(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 SetOperation}.
*/
SetOperation toValue(@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 SetOperation}.
*/
SetOperation toValueOf(Object value);
}
}
}

View File

@@ -17,11 +17,9 @@ package org.springframework.data.mongodb.core.aggregation;
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
@@ -29,7 +27,6 @@ import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldRefe
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -73,16 +70,7 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
*/
@Override
public Document getMappedObject(Document document) {
return getMappedObject(document, type);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(org.bson.Document, java.lang.Class)
*/
@Override
public Document getMappedObject(Document document, @Nullable Class<?> type) {
return mapper.getMappedObject(document, type != null ? mappingContext.getPersistentEntity(type) : null);
return mapper.getMappedObject(document, mappingContext.getPersistentEntity(type));
}
/*
@@ -91,6 +79,8 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
*/
@Override
public FieldReference getReference(Field field) {
PropertyPath.from(field.getTarget(), type);
return getReferenceFor(field);
}
@@ -103,31 +93,7 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
return getReferenceFor(field(name));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
*/
@Override
public Fields getFields(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
if (entity == null) {
return AggregationOperationContext.super.getFields(type);
}
List<String> fields = new ArrayList<>();
for (MongoPersistentProperty property : entity) {
fields.add(property.getName());
}
return Fields.fields(fields.toArray(new String[0]));
}
protected FieldReference getReferenceFor(Field field) {
private FieldReference getReferenceFor(Field field) {
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext
.getPersistentPropertyPath(field.getTarget(), type);

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2019-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.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Removes fields from documents.
*
* @author Christoph Strobl
* @since 3.0
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset/">MongoDB Aggregation Framework:
* $unset</a>
*/
public class UnsetOperation implements InheritsFieldsAggregationOperation {
private final Collection<Object> fields;
/**
* Create new instance of {@link UnsetOperation}.
*
* @param fields must not be {@literal null}.
*/
public UnsetOperation(Collection<Object> fields) {
Assert.notNull(fields, "Fields must not be null!");
Assert.noNullElements(fields, "Fields must not contain null values.");
this.fields = fields;
}
/**
* Create new instance of {@link UnsetOperation}.
*
* @param fields must not be {@literal null}.
* @return new instance of {@link UnsetOperation}.
*/
public static UnsetOperation unset(String... fields) {
return new UnsetOperation(Arrays.asList(fields));
}
/**
* Also unset the given fields.
*
* @param fields must not be {@literal null}.
* @return new instance of {@link UnsetOperation}.
*/
public UnsetOperation and(String... fields) {
List<Object> target = new ArrayList<>(this.fields);
CollectionUtils.mergeArrayIntoCollection(fields, target);
return new UnsetOperation(target);
}
/**
* Also unset the given fields.
*
* @param fields must not be {@literal null}.
* @return new instance of {@link UnsetOperation}.
*/
public UnsetOperation and(Field... fields) {
List<Object> target = new ArrayList<>(this.fields);
CollectionUtils.mergeArrayIntoCollection(fields, target);
return new UnsetOperation(target);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
*/
@Override
public ExposedFields getFields() {
return ExposedFields.from();
}
Collection<String> removedFieldNames() {
List<String> fieldNames = new ArrayList<>(fields.size());
for (Object it : fields) {
if (it instanceof Field) {
fieldNames.add(((Field) it).getName());
} else {
fieldNames.add(it.toString());
}
}
return fieldNames;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
if (fields.size() == 1) {
return new Document("$unset", computeFieldName(fields.iterator().next(), context));
}
return new Document("$unset",
fields.stream().map(it -> computeFieldName(it, context)).collect(Collectors.toList()));
}
private Object computeFieldName(Object field, AggregationOperationContext context) {
if (field instanceof Field) {
return context.getReference((Field) field).getRaw();
}
if (field instanceof AggregationExpression) {
return ((AggregationExpression) field).toDocument(context);
}
if (field instanceof String) {
return context.getReference((String) field).getRaw();
}
return field;
}
}

View File

@@ -16,15 +16,12 @@
package org.springframework.data.mongodb.core.convert;
import java.math.BigInteger;
import java.util.Date;
import org.bson.types.Code;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.ConverterBuilder;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
@@ -96,24 +93,6 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(Date.class, Long.class)) {
conversionService
.addConverter(ConverterBuilder.writing(Date.class, Long.class, Date::getTime).getWritingConverter());
}
if (!conversionService.canConvert(Long.class, Date.class)) {
conversionService.addConverter(ConverterBuilder.reading(Long.class, Date.class, Date::new).getReadingConverter());
}
if (!conversionService.canConvert(ObjectId.class, Date.class)) {
conversionService.addConverter(
ConverterBuilder.reading(ObjectId.class, Date.class, objectId -> new Date(objectId.getTimestamp()))
.getReadingConverter());
}
conversionService
.addConverter(ConverterBuilder.reading(Code.class, String.class, Code::getCode).getReadingConverter());
conversions.registerConvertersIn(conversionService);
}

View File

@@ -15,16 +15,13 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.UnaryOperator;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.convert.TypeAliasAccessor;
@@ -61,58 +58,21 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
private final TypeAliasAccessor<Bson> accessor;
private final @Nullable String typeKey;
private UnaryOperator<Class<?>> writeTarget = UnaryOperator.identity();
/**
* Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code _class}.
*/
public DefaultMongoTypeMapper() {
this(DEFAULT_TYPE_KEY);
}
/**
* Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}.
*
* @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints.
*/
public DefaultMongoTypeMapper(@Nullable String typeKey) {
this(typeKey, Collections.singletonList(new SimpleTypeInformationMapper()));
this(typeKey, Arrays.asList(new SimpleTypeInformationMapper()));
}
/**
* Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}.
*
* @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints.
* @param mappingContext the mapping context.
*/
public DefaultMongoTypeMapper(@Nullable String typeKey,
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext) {
this(typeKey, new DocumentTypeAliasAccessor(typeKey), mappingContext,
Collections.singletonList(new SimpleTypeInformationMapper()));
Arrays.asList(new SimpleTypeInformationMapper()));
}
/**
* Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. Uses
* {@link UnaryOperator} to apply {@link CustomConversions}.
*
* @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints.
* @param mappingContext the mapping context to look up types using type hints.
* @see MappingMongoConverter#getWriteTarget(Class)
*/
public DefaultMongoTypeMapper(@Nullable String typeKey,
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext, UnaryOperator<Class<?>> writeTarget) {
this(typeKey, new DocumentTypeAliasAccessor(typeKey), mappingContext,
Collections.singletonList(new SimpleTypeInformationMapper()));
this.writeTarget = writeTarget;
}
/**
* Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. Uses
* {@link TypeInformationMapper} to map type hints.
*
* @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints.
* @param mappers
*/
public DefaultMongoTypeMapper(@Nullable String typeKey, List<? extends TypeInformationMapper> mappers) {
this(typeKey, new DocumentTypeAliasAccessor(typeKey), null, mappers);
}
@@ -160,15 +120,6 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
accessor.writeTypeTo(result, new Document("$in", restrictedMappedTypes));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#getWriteTargetTypeFor(java.lang.Class)
*/
@Override
public Class<?> getWriteTargetTypeFor(Class<?> source) {
return writeTarget.apply(source);
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.DefaultTypeMapper#getFallbackTypeFor(java.lang.Object)

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