Compare commits

...

329 Commits

Author SHA1 Message Date
Mark Paluch
994a10006e Restructure content a bit. 2023-09-07 11:31:35 +02:00
Mark Paluch
161ec9aacb Polishing.
Move examples into test source root to ensure proper compilation. Remove lingering asciidoc resources.
Merge entity callbacks into lifecycle events. Add links from MongoDB overview.
2023-09-07 10:59:00 +02:00
Christoph Strobl
300ea4d332 update observability section 2023-09-07 09:38:35 +02:00
Christoph Strobl
2c9acd5eff update metrics and stuff 2023-09-07 09:31:05 +02:00
Christoph Strobl
3dcd0a3ca2 move session and transactions into one 2023-09-06 15:48:59 +02:00
Christoph Strobl
d7094416a4 next on list is transactions 2023-09-06 15:21:33 +02:00
Christoph Strobl
4994b04b52 move over gridfs and remove the remaining bits of reactive mongo 2023-09-06 13:13:33 +02:00
Christoph Strobl
fc4e7f8ad4 fix collection management and move on to gridfs support 2023-09-06 13:00:13 +02:00
Christoph Strobl
6094bd1480 rework aggregation framework 2023-09-06 12:30:33 +02:00
Christoph Strobl
ddc8843fc1 query operations 2023-09-06 11:47:27 +02:00
Christoph Strobl
ab4773931b update crud operations 2023-09-06 11:17:45 +02:00
Christoph Strobl
0a44a2d028 Update template config section 2023-09-06 09:52:52 +02:00
Christoph Strobl
4921c9376e update samples an anchors in template API 2023-09-06 09:36:46 +02:00
Christoph Strobl
823e0d02fc make it beautiful 2023-09-05 15:41:22 +02:00
Christoph Strobl
59052bd8ef first time a site that can actually be viewed 2023-09-05 14:04:10 +02:00
Christoph Strobl
4f8d420b43 moar movement 2023-09-05 10:21:26 +02:00
Christoph Strobl
c08176ac85 move execution callbacks to main API section 2023-09-05 09:12:24 +02:00
Christoph Strobl
da0d49d261 move counting and query options 2023-09-05 09:02:14 +02:00
Christoph Strobl
e2326618a1 move geospatial and full text to query ops 2023-09-04 15:46:00 +02:00
Christoph Strobl
678b0a65b2 move fluent template API and distinct queries 2023-09-04 14:30:18 +02:00
Christoph Strobl
ac82885697 done with template curd ops. next: -> template query api 2023-09-04 14:10:46 +02:00
Christoph Strobl
f052ae1794 template configuration, done. next: -> template crud operations 2023-09-04 11:32:03 +02:00
Christoph Strobl
2f1777e2fb Done with configuration -> next: template 2023-09-04 10:19:19 +02:00
Christoph Strobl
5c1d5abaf7 workding on the configuration section right now 2023-09-01 14:37:55 +02:00
Christoph Strobl
f5d70ca8bb start editing 2023-09-01 11:10:35 +02:00
Christoph Strobl
73050198c5 Enable Section Summary TOC for small pages 2023-08-31 18:22:11 +02:00
Christoph Strobl
ab804d5041 Fix cross references 2023-08-31 18:22:10 +02:00
Christoph Strobl
8cc3d466d8 Remove includes 2023-08-31 18:22:10 +02:00
Christoph Strobl
f289404079 Generate a default navigation 2023-08-31 18:22:10 +02:00
Christoph Strobl
c5c8c41879 Split files 2023-08-31 18:22:10 +02:00
Christoph Strobl
38e5e1dfb4 Fix indentation for all pages 2023-08-31 18:22:09 +02:00
Christoph Strobl
099213bbf2 Copy default antora files 2023-08-31 18:22:09 +02:00
Christoph Strobl
1749f9b485 Insert explicit ids for headers 2023-08-31 18:22:09 +02:00
Christoph Strobl
4f71a78302 Migrate Structure 2023-08-31 18:22:08 +02:00
Christoph Strobl
492f09fbdf Add isReadable method to UnwrappedMongoPersistentProperty.
Closes: #4489
2023-08-31 09:43:58 +02:00
Julia
e3e73f5351 Fix #self @DocumentReference resolution when used in constructor.
This commit enables document reference lookup to use `DocumentReferenceSource` to properly instantiate an entity containig a @DocumentReference `#self` property.

Closes #4484
Original Pull Request: #4486
2023-08-31 07:57:44 +02:00
Mark Paluch
e5aff2645b Polishing.
Refactor duplicate code into callback.

See #4481
2023-08-24 14:01:28 +02:00
Mark Paluch
b3c0fbb02d Guard command completion listener against unsupported observation context.
We now no longer attempt to complete the Observation if the context is not a MongoDB one. For commands that target the admin database and run within a parent observation, we still might have an Observation but that one points to the parent invocation and not the MongoDB one as we do not record commands for the admin database.

Closes #4481
2023-08-24 14:01:28 +02:00
Julia Lee
51de522e88 After release cleanups.
See #4450
2023-08-18 08:59:39 -04:00
Julia Lee
d2842b246f Prepare next development iteration.
See #4450
2023-08-18 08:59:35 -04:00
Julia Lee
1f954f45b4 Release version 4.2 M2 (2023.1.0).
See #4450
2023-08-18 08:50:31 -04:00
Julia Lee
c14d7bf616 Prepare 4.2 M2 (2023.1.0).
See #4450
2023-08-18 08:49:38 -04:00
Mark Paluch
ec7cfa3b8e Polishing.
Update since tags. Add missing Override annotation.

See #4070
Original pull request: #4242
2023-08-17 14:18:11 +02:00
Christoph Strobl
f1cff3cdaa Introduce AggregationVariable type.
This commit introduces a new AggregationVariable type that is intended to better identify variables within a pipeline to avoid mapping failures caused by invalid field names.

Closes #4070
Original pull request: #4242
2023-08-17 14:18:11 +02:00
Christoph Strobl
0fd1273ed9 Update documentation regarding java.time type conversion.
Closes #3482
Original pull request: #4460
2023-08-17 10:45:04 +02:00
Julia Lee
16798cb3c2 Update CI properties.
See #4450
2023-08-14 12:15:21 -04:00
Julia Lee
963072ec0a Upgrade to Maven Wrapper 3.9.4.
See #4470
2023-08-14 08:53:22 -04:00
Julia
7f64c020b4 Polishing for formatting
Original Pull Request: #4455
2023-08-08 08:55:04 -04:00
Julia
a93854fb09 Add integration test to ensure schema validation fails when domain type property values are not encrypted as expected.
Closes #4454
Original Pull Request: #4455
2023-08-08 08:55:04 -04:00
Christoph Strobl
b6cd129c93 Fix schema generation for encrypted fields that are considered domain entities.
This commit makes sure to consider the encrypted annotation on fields that are considered domain type property values, encrypting the entire object if necessary.
2023-08-08 08:55:04 -04:00
Oliver Christen
c532ec343a Correct misspellings in documentation
Closes: #4461
2023-07-31 14:11:04 -04:00
Christoph Strobl
a44e240402 Polishing.
Use previous context instead of root for mapping objects within an Inheriting context. This avoids accidental mapping of fields against the root entity after eg. a projection stage.
Add missing tests for AggregationOperationRenderer to ensure intended context propagation.

Original Pull Request: #4459
2023-07-28 07:20:13 +02:00
Julia Lee
9b82ede047 Fix mapping custom field names in downstream stages in TypedAggregation pipelines.
Use the root AggregationOperationContext in nested ExposedFieldsAggregationOperationContext to properly apply mapping for domain properties that use @Field.

Closes #4443
Original Pull Request: #4459
2023-07-28 07:18:43 +02:00
Julia Lee
e1986373fd Polishing.
Remove duplicate test configuration.

Original Pull Request: #4447
2023-07-17 10:20:59 +02:00
Julia Lee
5407456973 Fix test setup so that temporal conversions use symmetric timezone setting.
Closes: #4446
Original Pull Request: #4447
2023-07-17 10:12:02 +02:00
Mark Paluch
31f0aa348d After release cleanups.
See #4387
2023-07-14 14:57:12 +02:00
Mark Paluch
28abf1c15b Prepare next development iteration.
See #4387
2023-07-14 14:57:10 +02:00
Mark Paluch
2deede7513 Release version 4.2 M1 (2023.1.0).
See #4387
2023-07-14 14:53:18 +02:00
Mark Paluch
5a48825439 Prepare 4.2 M1 (2023.1.0).
See #4387
2023-07-14 14:52:12 +02:00
Mark Paluch
f4a3e293e8 Upgrade to MongoDB driver 4.10.2.
Closes #4445
2023-07-11 15:46:37 +02:00
Mark Paluch
f0697db32b Polishing.
Reformat code, replace known unsupported constructor with UnsupportedOperationException.

See #4432
Original pull request: #4439
2023-07-10 10:48:29 +02:00
Christoph Strobl
2cc5e427bc Delegate Bson conversion to MongoDB Codec.
Instead of reimplementing conversion we now try to delegate to the native MongoDB codec infrastructure using a custom writer that will only capture values without actually pushing values to an output stream.

See #4432
Original pull request: #4439
2023-07-10 10:48:29 +02:00
Christoph Strobl
a8f08bab86 Fix encryption of java.time types.
This commit makes sure to convert java.time types into their BsonValue representation before encrypting.

See #4432
Original pull request: #4439
2023-07-10 10:48:29 +02:00
Christoph Strobl
19211a0f8e Fix decryption when client is using AutoEncryptionSettings#isBypassAutoEncryption().
This commit makes sure to convert already decrypted entries returned by the driver in case the client is configured with encryption settings.

Closes #4432
Original pull request: #4439
2023-07-10 10:48:28 +02:00
Christoph Strobl
9e0c24435c Upgrade mongodb-crypt to 1.8.0
Closes: #4440
2023-07-07 11:40:06 +02:00
Christoph Strobl
19b1e713b2 Upgrade to MongoDB driver 4.10.1
Closes: #4441
2023-07-07 11:39:42 +02:00
Mark Paluch
af26bb6b31 Polishing.
Introduce limit(Limit) method to limit query results applying the Limit domain type.

See #4397
Original pull request: #4398
2023-07-05 12:02:04 +02:00
Christoph Strobl
d78f47f035 Add tests to verify Limit is supported.
Closes #4397
Original pull request: #4398
2023-07-05 12:01:44 +02:00
Mark Paluch
8cd956e90a Update CI properties.
See #4387
2023-07-03 09:50:16 +02:00
Mark Paluch
49cc6a708d Upgrade to Maven Wrapper 3.9.3.
See #4436
2023-07-03 09:49:43 +02:00
Christoph Strobl
0bf472a29b Polishing.
Update tests to make use of ValueSource.
Replace regex based path inspection with segment by segment analysis.

Original Pull Request: #4427
2023-06-28 13:25:08 +02:00
lijixue
2de00cdb2f Fix QueryMapper property path resolution for nested paths containing numeric values.
Prior to this fix a path that contains numeric values used as position parameters would have been stripped in a way that left out the last digit. This could lead to wrong path resolution if the incorrectly constructed property name accidentally matched an existing one.

Closes: #4426
Original Pull Request: #4427
2023-06-28 13:25:08 +02:00
Mark Paluch
05c38b819f Retain scroll direction across keyset scroll requests.
Closes #4413
2023-06-15 15:09:44 +02:00
Christoph Strobl
c5674d9264 Delombok test source.
Closes: #4411
2023-06-14 15:20:07 +02:00
Christoph Strobl
048af85be0 Accept expression as input for filter aggregation operator.
Closes #4394
Original pull request: #4395
2023-06-14 14:19:30 +02:00
Christoph Strobl
110877eb04 Fix converter registration when using driver native time codec.
This commit prevents converters from being used as writing converter causing asymmetric write/read operations.

Closes #4390
Original pull request: #4392
2023-06-14 11:04:10 +02:00
Mark Paluch
5d571005bb Polishing.
Use extended switch syntax.

See #4404
Original pull request: #4412
2023-06-14 10:00:15 +02:00
Christoph Strobl
57688d8642 Polishing.
Mark method potentially returning null as such and remove unused imports.

See #4404
Original pull request: #4412
2023-06-14 10:00:15 +02:00
Christoph Strobl
d6227e52f9 Use exact matching for IN clause with ignore case.
Prior to this change the generated pattern would have matched more entries than it should have. The behavior is now aligned to its counterpart not using the IgnoreCase flag.

Closes #4404
Original pull request: #4412
2023-06-14 10:00:15 +02:00
Mark Paluch
7e6e029352 Upgrade to Maven Wrapper 3.9.2.
See #4410
2023-06-13 08:54:58 +02:00
Mark Paluch
12c4bf6361 Use snapshot and milestone repositories instead of libs-snapshot and libs-milestone.
Closes #4401
2023-06-06 09:47:04 +02:00
Christoph Strobl
98795cb33e Convert BsonUndefined to null value.
Register a reading converter that returns null when attempting to read a value of type BsonUndefined.
Prior to this change users faced a ConverterNotFoundException when source documents contained BsonUndefined.

Resolves: #2350
2023-06-01 09:24:29 +02:00
Christoph Strobl
fa63efcb24 Add tests using $slice on dbref field.
Closes: #2191
2023-05-31 16:11:40 +02:00
Christoph Strobl
5ffaa79f4e Fix code snippet in change streams reference documentation.
Closes: #4376
2023-05-30 13:01:57 +02:00
Christoph Strobl
f775d485c6 Update docker image build instructions.
Closes: #4372
2023-05-30 13:01:57 +02:00
Mark Paluch
370b4145d2 Polishing.
Add assertions and missing Override annotations. Avoid recursive self-call on getClassLoader. Extend documentation.

See #1627
Original pull request: #4389
2023-05-26 14:48:08 +02:00
Christoph Strobl
4b78ef6523 Extend GridFsTemplate and its reactive variant to accept a provided GridFSBucket instance.
Allow to pass in a GridFSBucket from outside to avoid recreating instances on every method call.

Closes #1627
Original pull request:# 4389
2023-05-26 14:48:08 +02:00
Christoph Strobl
5163e544ae After release cleanups.
See #4369
2023-05-12 14:18:53 +02:00
Christoph Strobl
431512a66c Prepare next development iteration.
See #4369
2023-05-12 14:18:51 +02:00
Christoph Strobl
532b460067 Release version 4.1 GA (2023.0.0).
See #4369
2023-05-12 14:14:38 +02:00
Christoph Strobl
af846a962a Prepare 4.1 GA (2023.0.0).
See #4369
2023-05-12 14:14:05 +02:00
Mark Paluch
776dadeac8 Polishing.
Introduce has…() and getRequired…() methods for comment and max time limit to remove code duplications.

See #4374
Original pull request: #4378
2023-05-11 10:13:56 +02:00
Christoph Strobl
629dfc187e Fix missing query options when calling MongoOperations#count.
This commit makes sure to forward maxTimeMsec and comment options from the query to the CountOptions.

Closes: #4374
Original pull request: #4378
2023-05-10 15:19:17 +02:00
Christoph Strobl
289438b1e4 Fix regression in value to String mapping.
Previous versions allow arbitrary values to be mapped to an string property by calling the ObjectToString converter. This behaviour got lost and is not reestablished.

Closes #4371
Original pull request #4373
2023-05-10 14:52:53 +02:00
Oliver Drotbohm
83958ba316 Adapt to ScrollPosition API changes in Spring Data Commons.
Fixes #4377.
Related ticket: #2824.
2023-04-27 19:42:27 +02:00
Christoph Strobl
3a99d4c29a Fix broken links in observability section of reference documentation.
Add back micrometer-docs-generator plugin in version 1.0.1.

Fixes: #4236
2023-04-24 13:04:38 +02:00
Tomasz Forys
561c3d4f39 Instanceof casting simplification.
Closes: #4265
2023-04-24 09:57:40 +02:00
Christoph Ahlers
7f74794a11 Fix inconsistent strong tag usage in javadoc.
Closes: #4177
2023-04-20 15:04:25 +02:00
Thom
238d8c5ed0 Fix link to custom conversion section in reference documentation.
Closes: #4287
2023-04-20 15:04:15 +02:00
Christoph Strobl
c096caac7d Provide context configuration hints for Spring Boot.
Closes: #3381
2023-04-20 15:04:04 +02:00
Christoph Strobl
c794acaf61 Cover missing Aggregation Stages in reference documentation.
Closes: #3938
2023-04-20 15:01:49 +02:00
Christoph Strobl
339db9d1b8 Add 2023.0 release to compatibility matrix.
Closes: #3940
2023-04-20 15:01:39 +02:00
Christoph Strobl
c04fe744cb Update AggregationExpression javadoc.
See: #4370
2023-04-20 15:01:32 +02:00
Greg L. Turnquist
a16558e4a3 After release cleanups.
See #4337
2023-04-14 12:00:02 -05:00
Greg L. Turnquist
0675b052c6 Prepare next development iteration.
See #4337
2023-04-14 11:59:56 -05:00
Greg L. Turnquist
6b85edfa84 Release version 4.1 RC1 (2023.0.0).
See #4337
2023-04-14 11:53:59 -05:00
Greg L. Turnquist
f4ec21792f Prepare 4.1 RC1 (2023.0.0).
See #4337
2023-04-14 11:53:21 -05:00
Mark Paluch
67bd722cfd Polishing.
Extract common code into BulkOperationsSupport. Reorder methods. Add missing verifyComplete to tests.

See #2821
Original pull request: #4342
2023-04-14 14:51:20 +02:00
Christoph Strobl
86dd81f770 Add support for reactive bulk operations.
Closes #2821
Original pull request: #4342
2023-04-14 14:51:19 +02:00
Mark Paluch
2f146dd142 Polishing.
Refine updateOne/updateMulti signatures to accept UpdateDefinition in the generic signature. Use pattern variables and records where applicable. Resolve code duplicates.

See #3872
Original pull request: #4344
2023-04-14 09:34:54 +02:00
Christoph Strobl
0ba857aa22 Add support for AggregationUpdate to BulkOperations.
We now accept `UpdateDefinition` in `BulkOperations` to support custom update definitions and aggregation updates.

Closes #3872
Original pull request: #4344
2023-04-14 09:34:54 +02:00
Mark Paluch
a94ea17e0e Polishing.
Reformat code. Remove unused fields, modifiers and documentation artifacts.

See #4088
Original pull request: #4341
2023-04-14 08:58:42 +02:00
Christoph Strobl
3b99fa0fb4 Skip output for void methods using declarative Aggregations having $out stage.
We now set the skipOutput flag if an annotated Aggregation defines an $out stage and when the method is declared to return no result (void / Mono<Void>, kotlin.Unit)

Closes: #4088
Original pull request: #4341
2023-04-14 08:58:42 +02:00
Christoph Strobl
4b0c0274e8 Enable index modification via IndexOperations.
Introduce IndexOptions that can be used to alter an existing index via IndexOperations.

See: #4348
2023-04-14 08:21:08 +02:00
Christoph Strobl
83217f3413 Polishing.
Add since tags.
Make sure IndexInfo holds hidden information.
Move and add tests.

Original Pull Request: #4349
2023-04-14 08:20:17 +02:00
张行帅
aaa1450b2a Add hidden support for index.
Closes: #4348
Original Pull Request: #4349
2023-04-14 08:18:40 +02:00
Mark Paluch
89c6099a3c Polishing.
Reformat code. Make getAnnotatedHint non-nullable.

See #3230
Original pull request: #4339
2023-04-13 11:51:16 +02:00
Christoph Strobl
7b44f78133 Add Hint annotation.
This commit introduces the new `@Hint` annotation that allows to override MongoDB's default index selection for repository query, update and aggregate operations.

```
@Hint("lastname-idx")
List<Person> findByLastname(String lastname);

@Query(value = "{ 'firstname' : ?0 }", hint="firstname-idx")
List<Person> findByFirstname(String firstname);
```

Closes: #3230
Original pull request: #4339
2023-04-13 11:51:12 +02:00
Christoph Strobl
af2076d4a5 Fix null value handling in ParameterBindingJsonReader#readStringFromExtendedJson.
This commit makes sure to return null for a null parameter value avoiding a potential NPE when parsing data.
In doing so we can ensure object creation is done with the intended value that may or may not lead to a downstream error eg. when trying to create an ObjectId with a null hexString.

Closes: #4282
Original pull request: #4334
2023-04-13 11:32:19 +02:00
Christoph Strobl
79c6427cc9 Update date time parser.
See: #3750
Original pull request: #4334
2023-04-13 11:32:19 +02:00
Christoph Strobl
d54f2e47b0 Follow changes in uuid processing.
Closes: #3750
Original pull request: #4334
2023-04-13 11:32:18 +02:00
Mark Paluch
cc3b33e885 Polishing.
Tweak naming.

Original pull request: #4352
See #4351
2023-04-13 11:12:29 +02:00
Christoph Strobl
b081089df4 Fix AOT processing for lazy-loading Jdk proxies.
This commit makes sure to use the ProxyFactory for retrieving the proxied interfaces. This makes sure to capture the exact interface order required when finally loading the proxy at runtime.

Original pull request: #4352
Closes #4351
2023-04-13 11:12:28 +02:00
Mark Paluch
414cf51f98 Upgrade to MongoDB 4.9.1.
Closes #4362
2023-04-11 15:58:58 +02:00
Mark Paluch
8bbc64317d Upgrade to Maven Wrapper 3.9.1.
See #4356
2023-04-06 16:16:28 +02:00
Oliver Drotbohm
5f48ee5644 Prefer implementing PersistentPropertyAccessor over PersistentPropertyPathAccessor.
In preparation of spring-projects/spring-data-commons#2813 we're moving off the implementation of PersistentPropertyPathAccessor and rather only implement PersistenPropertyAccessor.

Fixes #4354.
2023-04-04 11:10:27 +02:00
Greg L. Turnquist
5733a00f54 Test against Java 20 on CI.
See #4350.
2023-03-29 16:28:08 -05:00
Greg L. Turnquist
894acbb0fc Update CI properties.
See #4337
2023-03-28 13:58:15 -05:00
Christoph Strobl
d70a9ed7c6 Update visibility of ConversionContext.
The ConversionContext should not be package private due to its usage in protected method signatures.

Closes: #4345
2023-03-24 13:40:48 +01:00
Mark Paluch
27de3680b4 Fix reverse keyset scrolling.
We now use gt/lt instead of gte/lte to avoid duplicates during reverse keyset scrolling.

Closes #4343
2023-03-24 11:14:03 +01:00
Christoph Strobl
244b949b6d After release cleanups.
See #4295
2023-03-20 15:05:35 +01:00
Christoph Strobl
29b9123f5b Prepare next development iteration.
See #4295
2023-03-20 15:05:33 +01:00
Christoph Strobl
a2296534c7 Release version 4.1 M3 (2023.0.0).
See #4295
2023-03-20 15:01:47 +01:00
Christoph Strobl
dc8e9d2e0f Prepare 4.1 M3 (2023.0.0).
See #4295
2023-03-20 15:01:19 +01:00
Mark Paluch
25f610cc8a Fix keyset backwards scrolling.
We now correctly scroll backwards by reversing sort order to apply the correct limit and reverse the results again to restore the actual sort order.

Closes #4332
2023-03-20 08:54:50 +01:00
Christoph Strobl
d8c04f0ec9 Use projecting read callback to allow interface projections.
Along the lines fix entity operations proxy handling by reading the underlying map instead of inspecting the proxy interface.
Also make sure to map potential raw fields back to the according property.

See: #4308
Original Pull Request: #4317
2023-03-17 13:07:44 +01:00
Mark Paluch
85826e1fe0 Document limitations around nullable properties.
See: #4308
Original Pull Request: #4317
2023-03-17 13:07:05 +01:00
Mark Paluch
14a722f2fd Add support for reverse scrolling.
Closes #4325
Related to: #4308
Original Pull Request: #4317
2023-03-17 13:06:27 +01:00
Christoph Strobl
9d0afc975a Prevent key extraction if a keyset value is null.
Follow the changes in data commons that renamed scroll to window.
Also error when a certain scroll position does not allow creating a query out of it because of null values.

See: #4308
Original Pull Request: #4317
2023-03-17 13:05:20 +01:00
Mark Paluch
eaa6393798 Add support for keyset extraction of nested property paths.
Closes #4326
Original Pull Request: #4317
2023-03-17 13:04:41 +01:00
Mark Paluch
7d485d732a Add support for scrolling using offset- and keyset-based strategies.
We now support scrolling through large query results using ScrollPosition and Window's of data.

See: #4308
Original Pull Request: #4317
2023-03-17 13:03:53 +01:00
Mark Paluch
aff4e4fd02 Polishing.
Remove duplicate logging in imperative FindOneCallback.

See #4253
Original pull request: #4259
2023-03-17 09:36:55 +01:00
Raghav2211
18413586fb Remove duplicate log in reactive findOne operation.
Closes #4253
Original pull request: #4259
2023-03-17 09:36:53 +01:00
Mark Paluch
aeea743921 Polishing.
Simplify field creation considering simplified projection expressions.

See #3917
Original pull request: #4328
2023-03-16 09:53:50 +01:00
Christoph Strobl
e5bba39c62 Fix field resolution for ExposedFieldsAggregationContext.
This commit fixes an issue where the context is not relaxed and errors on unknown fields if multiple stages of nesting contexts happen.

Closes #3917
Original pull request: #4328
2023-03-16 09:53:27 +01:00
Christoph Strobl
c2f708a37a Fix property value conversion for $in clauses.
This commit fixes an issue where a property value converter is not applied if the query is using an $in clause that compares the value against a collection of potential candidates.

Closes #4080
Original pull request: #4324
2023-03-15 11:32:06 +01:00
Mark Paluch
7f50fe1cb7 Polishing.
Extract duplicates into peek method.

See #4312
Original pull request: #4323
2023-03-15 10:58:43 +01:00
Christoph Strobl
a2127a4da9 Allow reading already resolved references.
This commit adds the ability to read (eg. by an aggregation $lookup) already fully resolved references between documents.
No proxy will be created for lazy loading references and we'll also skip the additional server roundtrip to load the reference by its id.

Closes #4312
Original pull request: #4323
2023-03-15 10:58:37 +01:00
Mark Paluch
67215f1209 Polishing.
Remove caching variant of MongoClientEncryption. Rename types for consistent key alt name scheme. Rename annotation to ExplicitEncrypted.

Add package-info. Improve documentation wording. Reduce visibility of KeyId and KeyAltName to package-private.

Original pull request: #4302
See: #4284
2023-03-15 10:27:37 +01:00
Christoph Strobl
3b33f90e5c Add support for explicit field encryption.
We now support explicit field encryption using mapped entities through the `@ExplicitEncrypted` annotation.

class Person {
  ObjectId id;

  @ExplicitEncrypted(algorithm = AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic, altKeyName = "my-secret-key")
  String socialSecurityNumber;
}

Encryption is applied transparently to all mapped entities leveraging the existing converter infrastructure.

Original pull request: #4302
Closes: #4284
2023-03-15 10:27:37 +01:00
Mark Paluch
3b7b1ace8b Polishing.
Introduce isEmpty method for HintFunction for easier invocation avoiding negations on the call site.

See #3218
Original pull request: #4311
2023-03-06 14:43:07 +01:00
Christoph Strobl
cd63501680 Support hints on Update.
This commit makes sure to read query hints and apply them to the MongoDB UpdateOptions when running an update via Reactive-/MongoTemplate.

Original pull request: #4311
Closes: #3218
2023-03-06 14:37:17 +01:00
Mark Paluch
0020499d4e Polishing.
Reformat code.

See #2750
Original pull request: #4316
2023-03-06 14:31:19 +01:00
Christoph Strobl
f4b85242d4 Polishing.
Favor Base64 over deprecated Base64Utils.

See: #2750
Original pull request: #4316.
2023-03-06 14:28:45 +01:00
Christoph Strobl
a416441427 Support $expr via criteria query.
This commit introduces AggregationExpressionCriteria to be used along with Query to run an $expr operator within the find query.

query(whereExpr(valueOf("spent").greaterThan("budget")))

Closes: #2750
Original pull request: #4316.
2023-03-06 14:28:19 +01:00
Christoph Strobl
e3ef84a56c Fix regression in findAndReplace when using native MongoDB types as domain value.
This commit fixes a regression that prevented native org.bson.Document to serve as source for a findAndReplaceOperation.

Closes: #4300
Original Pull Request: #4310
2023-03-02 09:55:42 +01:00
Mark Paluch
3ab78fc1ed Upgrade to Maven Wrapper 3.9.0.
See #4297
2023-02-20 11:58:01 +01:00
Christoph Strobl
fa0f026410 After release cleanups.
See #4294
2023-02-17 14:25:48 +01:00
Christoph Strobl
9c96a2b2c3 Prepare next development iteration.
See #4294
2023-02-17 14:25:46 +01:00
Christoph Strobl
0986210221 Release version 4.1 M2 (2023.0.0).
See #4294
2023-02-17 14:22:30 +01:00
Christoph Strobl
7d5372f049 Prepare 4.1 M2 (2023.0.0).
See #4294
2023-02-17 14:22:15 +01:00
Christoph Strobl
a5022e9bc4 After release cleanups.
See #4235
2023-02-17 13:31:54 +01:00
Christoph Strobl
aff8fbd62a Prepare next development iteration.
See #4235
2023-02-17 13:31:52 +01:00
Christoph Strobl
633fbceb5a Release version 4.1 M1 (2023.0.0).
See #4235
2023-02-17 13:27:49 +01:00
Christoph Strobl
fb9a0d8482 Prepare 4.1 M1 (2023.0.0).
See #4235
2023-02-17 13:27:08 +01:00
Christoph Strobl
d73807df1b Support ReadConcern and ReadPreference via NearQuery.
Implement ReadConcernAware and ReadPreferenceAware for NearQuery and make sure those get applied when working with the template API.

Original Pull Request: #4288
2023-02-16 14:28:11 +01:00
Mark Paluch
e56f6ce87f Polishing.
Documentation, refine parameter ordering.

Original Pull Request: #4288
2023-02-16 14:28:11 +01:00
Mark Paluch
c5c6fc107c Support ReadConcern & ReadPreference via the Query and Aggregation API.
Add support for setting the ReadConcern and ReadPreference via the Query and Aggregation API.

Closes: #4277, #4286
Original Pull Request: #4288
2023-02-16 14:28:10 +01:00
Christoph Strobl
368c644922 Guard tests for $lookup with let & pipeline
Add guard to skip tests prior to 5.0 server version.

Related to: #3322
2023-02-16 09:33:37 +01:00
Christoph Strobl
4d050f5021 Polishing.
Reuse Let from VariableOperators.
Limit API exposure and favor builders.
Update nullability constraints and assertions.
Update integration tests.
Add unit tests.

Original Pull Request: #4272
2023-02-16 08:33:07 +01:00
sangyongchoi
83923e0e2a Add support for 'let' and 'pipeline' in $lookup
This commit introduces let and pipline to the Lookup aggregation stage.

Closes: #3322
Original Pull Request: #4272
2023-02-16 08:30:21 +01:00
Mark Paluch
25588850dd Disable flakey test.
See #4290
2023-02-14 11:25:06 +01:00
Mark Paluch
55c81f4f54 Adopt to Mockito 5.1 changes.
Closes #4290
2023-02-14 10:50:30 +01:00
Christoph Strobl
ac7551e47f Upgrade to MongoDB driver 4.9.0
Closes: #4289
2023-02-14 07:51:11 +01:00
Mark Paluch
6d3043de9a Update CI properties.
See #4235
2023-01-30 10:49:50 +01:00
Mark Paluch
1a94b6e4ee Upgrade to Maven Wrapper 3.8.7.
See #4281
2023-01-30 10:48:12 +01:00
Mark Paluch
33902b5061 Polishing.
Move QuerydslPredicateExecutor hints to RepositoryRuntimeHints.

See #4244
Original pull request: #4245
2023-01-23 14:08:43 +01:00
Christoph Strobl
d00db4bd40 Add missing hints for Querydsl integration.
This commit adds missing reflection configuration for Querydsl integration. We now also make sure to call the queryMixing getter instead of reading the field via reflection.

Closes #4244
Original pull request: #4245
2023-01-23 14:08:43 +01:00
Christoph Strobl
a5dcbf043a Update links in reference documentation.
We now use the springDocsUrl attribute provided via spring-projects/spring-data-build#1895 to resolve links to framework documentation.

Original Pull Request: #4267
2023-01-18 14:23:22 +01:00
robeatoz
c31203582f Fix parameter and method name in reference documentation.
Closes: #4247
2023-01-16 11:22:29 +01:00
Emre Uygun
f146afecdc Fix typo in reference documentation.
Closes: #4250
2023-01-16 11:19:32 +01:00
Christoph Strobl
324a541a64 Polishing.
Original Pull Request: #4255
2023-01-16 11:17:49 +01:00
Michael Krog
6b71d773d7 Fixes return in Javadoc.
Closes: #4255
2023-01-16 11:15:14 +01:00
Patouche
10447afe0c Fix typo in reference documentation.
Closes: #4268
2023-01-16 10:49:42 +01:00
soumyaPrakashB
c9dfd60f0f Add missing Nullable annotation.
For one of constructor arguments of the AggregationOptions the Nullable annotation for the cursor argument is missing.

Closes: #4256
2023-01-16 10:47:28 +01:00
Mark Paluch
26a8fafd03 Upgrade to MongoDB driver 4.8.2.
Closes #4270
2023-01-13 10:30:01 +01:00
Mark Paluch
00f652a094 Polishing.
Add missing package-info.

See #4248
Original pull request: #4249
2023-01-12 08:47:16 +01:00
Christoph Strobl
d050ae5732 Exclude mongodb and data.mongodb namespaces from reflection contribution.
In some cases the users domain model may hold references to spring data or MongoDB specific types which should not be included in the reflection configuration as they are part of the static runtime hints configuration.

Closes #4248
Original pull request: #4249
2023-01-12 08:47:16 +01:00
Christoph Strobl
8bcab93588 Avoid multiple mapping iterations.
A 2nd pass is no longer needed as the context already does all the work.

Closes: #4043
Original pull request: #4240
2023-01-11 16:04:36 +01:00
Mark Paluch
1839f55055 Polishing.
Introduce HintFunction to encapsulate how hints are applied and to remove code duplications.

See #4238
Original pull request: #4243
2023-01-11 16:02:14 +01:00
Christoph Strobl
4220df5bf8 Accept index names as hint for aggregations.
Closes #4238
Original pull request: #4243
2023-01-11 16:02:05 +01:00
Christoph Strobl
95c6d1531f Fix invalid format specifier in debug statement.
Closes #4241
Original pull request: #4246
2023-01-11 15:29:22 +01:00
Christoph Strobl
b7ed099e06 Update broken links in reference documentation.
Original Pull Request: #4267
2023-01-11 13:46:50 +01:00
Maksymilian Babarowski
7e2e546e55 Update links to Spring Framework reference docs.
Closes: #4267
2023-01-11 13:46:38 +01:00
yangwenjie008
7ce2ebe26e Fix class loader issue with LazyLoadingProxyInterceptor.
Restore original behaviour that was unintentionally changed by modifications related to #4148.

Closes: #4260
Original Pull Request: #4261
2023-01-11 08:49:38 +01:00
Mark Paluch
fbf4d1baa8 Extend license header copyright years to 2023.
See #4264
2023-01-02 09:53:33 +01:00
Christoph Strobl
187f260fe4 Upgrade to MongoDB driver 4.8.1
Closes: #4251
2022-12-12 13:45:20 +01:00
Mark Paluch
04411075b4 Update CI properties.
See #4235
2022-11-18 15:31:10 +01:00
Mark Paluch
459a9c191b After release cleanups.
See #4209
2022-11-18 14:30:20 +01:00
Mark Paluch
137cba8bbb Prepare next development iteration.
See #4209
2022-11-18 14:30:19 +01:00
Mark Paluch
548cbd87b6 Release version 4.0 GA (2022.0.0).
See #4209
2022-11-18 14:26:22 +01:00
Mark Paluch
02647ad125 Prepare 4.0 GA (2022.0.0).
See #4209
2022-11-18 14:26:12 +01:00
Christoph Strobl
fe549f7254 Add Nullable annotation to parameter of overridden equals method.
Closes: #4226
Original pull request: #4277
2022-11-16 10:39:47 +01:00
Christoph Strobl
c069e094e6 Implement equals, hashCode and toString for CollectionOptions.
Closes: #4210
Original pull request: #4277
2022-11-16 10:39:14 +01:00
Christoph Strobl
62f3656ebb Replace upgrading section in documentation with links to the release notes.
Closes: #4228
2022-11-15 09:16:48 +01:00
Christoph Strobl
cbc718e03d Upgrade to MongoDB driver 4.8.0
Closes: #4201
2022-11-15 07:37:11 +01:00
Christoph Strobl
23be69b9ce Upgrade to MongoDB driver 4.8.0-rc0
See: #4201
2022-11-14 08:46:59 +01:00
Christoph Strobl
b7b5b085b3 Remove micrometer docs plugin.
See: spring-projects/spring-data-build#1836
2022-11-14 08:46:59 +01:00
Mark Paluch
2d292c50b0 After release cleanups.
See #4215
2022-11-04 15:26:39 +01:00
Mark Paluch
f959a77890 Prepare next development iteration.
See #4215
2022-11-04 15:26:37 +01:00
Mark Paluch
c04c3d66a3 Release version 4.0 RC2 (2022.0.0).
See #4215
2022-11-04 15:23:17 +01:00
Mark Paluch
b204c1b33e Prepare 4.0 RC2 (2022.0.0).
See #4215
2022-11-04 15:23:06 +01:00
Christoph Strobl
dfa029b341 Guard transaction proxy creation hints.
Closes: #4225
Related: #4221
2022-11-03 12:48:49 +01:00
Christoph Strobl
04a8c47cda Follow API changes in data-commons
Update imports of moved AOT processing types and update reactive wrapper coordinates to new location.

Closes: #4224
See: spring-projects/spring-data-commons#2708
2022-11-02 11:51:11 +01:00
Christoph Strobl
88ea57f2be Provide native hints to create transaction proxies at runtime.
Closes: #4221
2022-11-02 10:26:45 +01:00
Mark Paluch
521bbd2535 Update CI properties.
See #4215
2022-10-31 10:36:34 +01:00
Mark Paluch
0474632640 Upgrade to Java 17.0.4.1_1 and pin base image distribution.
See #4215
2022-10-31 10:27:36 +01:00
Mark Paluch
8aab5e5a01 Use correct boolean type for JSON Schema creation.
We now use the correct JSON type boolean again when creating schemas. Furthermore, we use the bool type for MongoDB $type queries.

Closes #4220
2022-10-27 10:16:58 +02:00
Christoph Strobl
3db5fc728e Polishing.
See: #4211
Original pull request: #4212
2022-10-24 15:11:25 +02:00
Christoph Strobl
b027f15a4c Add missing runtime hint for QuerydslMongoPredicateExecutor.
Closes: #4211
Original pull request: #4212
2022-10-24 15:11:10 +02:00
Mark Paluch
fd0a554d59 Polishing.
Use existing constants.

See #4218
2022-10-24 15:04:50 +02:00
Marcin Grzejszczak
d4daa305a8 Align the context propagation entries with the rest of the portfolio.
Closes #4218
2022-10-24 15:04:33 +02:00
Mark Paluch
2d63d6006d Align conventions with OpenTelemetry spec.
See: #4216
2022-10-21 11:49:03 +02:00
Mark Paluch
5007e68cc1 Polishing.
See: #4216
2022-10-21 11:49:03 +02:00
Mark Paluch
3ea4e0f9dd Update documentation.
See: #4216
2022-10-21 11:49:03 +02:00
Greg L. Turnquist
e9ac77c058 Improve configuration support for Observability integration.
Closes: #4216
2022-10-21 11:27:58 +02:00
Christoph Strobl
daef8b6e8e Add missing reflection hints for generated cglib proxies.
Closes: #4217
2022-10-20 15:59:06 +02:00
Mark Paluch
f671a9bd43 After release cleanups.
See #4175
2022-10-13 17:31:16 +02:00
Mark Paluch
57b52862c8 Prepare next development iteration.
See #4175
2022-10-13 17:31:15 +02:00
Mark Paluch
af917b9465 Release version 4.0 RC1 (2022.0.0).
See #4175
2022-10-13 17:24:25 +02:00
Mark Paluch
ee545487b8 Prepare 4.0 RC1 (2022.0.0).
See #4175
2022-10-13 17:24:03 +02:00
Mark Paluch
240e53794c Fix Javadoc.
See #4139
Original pull request: #4182.
2022-10-12 15:59:33 +02:00
Christoph Strobl
7b6a06888d Update javadoc.
See: #4184
See: #4197
Original pull request: #4203.
2022-10-12 15:25:13 +02:00
Christoph Strobl
034a3528af Preserve given Id on insert.
This commit fixes an issue where an existing Id got replaced with a generated one when using MongoId annotation.

Closes: #4184
Closes: #4197
Original pull request: #4203.
2022-10-12 15:24:50 +02:00
Christoph Strobl
ff28789507 Polishing.
See #4139
Original pull request: #4182.
2022-10-12 15:12:42 +02:00
Christoph Strobl
cdfdeafdac Update aggregation reference documentation.
See #4139
Original pull request: #4182.
2022-10-12 15:12:38 +02:00
Christoph Strobl
16a35e0329 Add support for $densify aggregation stage.
See #4139
Original pull request: #4182.
2022-10-12 15:12:38 +02:00
Christoph Strobl
79f05c3d7f Move Expr operator one level up.
The Expr operator should be held within ExpressionOperators not its factory.

See #4139
Original pull request: #4182.
2022-10-12 15:12:37 +02:00
Christoph Strobl
9217821472 Add support for $locf aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:12:37 +02:00
Christoph Strobl
8d223abd05 Add support for $tsSecond aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:12:37 +02:00
Christoph Strobl
6a973b245f Add support for $tsIncrement aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:12:37 +02:00
Christoph Strobl
714b23e0ce Add support for $sortArray aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:12:37 +02:00
Christoph Strobl
d4a6614c11 Add support for $setField aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:48 +02:00
Christoph Strobl
82ce0abe1a Add support for $getField aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:46 +02:00
Christoph Strobl
dec7c125d6 Add support for $dateTrunc aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:43 +02:00
Christoph Strobl
db12c4ba5a Add support for $dateSubtract aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:41 +02:00
Christoph Strobl
5bbe481e98 Add support for $minN aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:38 +02:00
Christoph Strobl
fb39c31986 Add support for $maxN aggregation operator.
See #4139
Original pull request: #4182.
2022-10-12 15:11:31 +02:00
Christoph Strobl
dd446472bc Polishing.
See #4139
Original pull request: #4182.
2022-10-12 15:11:22 +02:00
Christoph Strobl
72d82d3083 Add support for $top & $topN aggregation operators.
Closes #4139
Original pull request: #4182.
2022-10-12 15:11:20 +02:00
Christoph Strobl
cdfe2a0b59 Add support for $lastN aggregation operator.
Closes #4139
Original pull request: #4182.
2022-10-12 15:11:18 +02:00
Christoph Strobl
5525a4fbf9 Add support for $firstN aggregation operator.
Closes #4139
Original pull request: #4182.
2022-10-12 15:11:15 +02:00
Christoph Strobl
59464f3b3c Add support for $bottomN aggregation operator.
Closes #4139
Original pull request: #4182.
2022-10-12 15:11:09 +02:00
Christoph Strobl
052cfdfd45 Add support for $bottom aggregation operator.
Closes #4139
Original pull request: #4182.
2022-10-12 15:10:40 +02:00
Christoph Strobl
b31c21bb91 Upgrade to MongoDB driver 4.8.0-beta0.
Closes: #4200
2022-10-11 16:47:23 +02:00
Christoph Strobl
5b6e5ca568 Update tests.
Original Pull Request: #4196
2022-10-11 11:53:26 +02:00
gongxuanzhang
1a9136c0c1 Fix json schema type name for boolean.
Was boolean should have been bool.

Closes: #4196
2022-10-11 11:53:25 +02:00
Mark Paluch
59753bb55a Adapt to changed AOT packages in Spring Data Commons.
Closes #4199
2022-10-11 11:39:00 +02:00
Christoph Strobl
8d963fc5da Add option to configure change stream behaviour at collection creation time.
Introduce CollectionChangeStreamOptions which allows to define the changeStreamPreAndPostImages of the createCollection command.

Original Pull Request: #4193
2022-10-10 11:54:32 +02:00
Christoph Strobl
c1de745014 Polishing.
Update javadoc, format imports and add issue references.

Original Pull Request: #4193
2022-10-10 11:54:08 +02:00
myroslav.kosinskyi
aa35aaeb70 Add fullDocumentBeforeChange support for change streams.
Closes: #4187
Original Pull Request: #4193
2022-10-10 11:53:39 +02:00
Mark Paluch
a5725806f5 Remove references to ClassTypeInformation from TypeInformation.
Closes #4195
2022-10-06 16:21:30 +02:00
Christoph Strobl
d715414683 Switch to micrometer 1.10 snapshots.
Follow signature changes.

See: #4191
See: spring-projects/spring-data-build#1810
2022-10-06 15:44:51 +02:00
Christoph Strobl
f2c2451b7d Add hint how to use $search aggregation operator to reference documentation.
Closes: #4183
2022-10-06 13:56:57 +02:00
Christoph Strobl
5b8d0d08ee Update reactive transaction sample in reference documentation.
Closes: #4190
2022-10-06 13:03:56 +02:00
Christoph Ahlers
18186f26e2 Remove unused imports.
Closes: #4178
2022-10-06 10:21:50 +02:00
Christoph Ahlers
10acc14c14 Fix javadoc parameter names.
Closes: #4179
2022-10-04 12:31:34 +02:00
Wan Bachtiar
87effb9013 Fix typo in reference documentation.
Closes: #4180
2022-10-04 12:24:23 +02:00
Mark Paluch
19819680f9 Adopt to SLF4J 2.0 upgrade.
Exclude transitive Micrometer Test dependencies that ship outdated SLF4J implementations.

Closes #4189
2022-09-30 13:38:52 +02:00
Mark Paluch
2d2f67cc93 Prefer Java configuration over XML.
Closes #4186
2022-09-28 15:29:10 +02:00
Seungwoo Jo
e9818fe11a Fix documentation typo in BasicQuery.
Closes #4169
Original pull request: #4170.
2022-09-21 11:15:12 +02:00
Christoph Strobl
a63db5586c Add missing aggregation system variables.
Move inner class SystemVariable to upper level and add missing values (NOW, CLUSTER_TIME, DECEND, PRUNE, KEEP & SEARCH_META)

Original pull request: #4176.
Closes #4145
2022-09-21 10:51:00 +02:00
Mark Paluch
68ab74a5bf Polishing.
Reformat code.

See #4004
Original pull request: #4006.
2022-09-21 10:48:22 +02:00
Christoph Strobl
de33734118 Polishing
Update Javadoc to mention unit of measure for min/maxDistance depending on usage of geoJson.
Also remove unused imports from tests

See #4004
Original pull request: #4006.
2022-09-21 10:48:22 +02:00
Christoph Strobl
c272c7317e Fix rewrite near & nearSphere count queries using geoJson to geoWithin.
$near and $nearSphere queries are not supported via countDocuments and the used aggregation match stage and need to be rewritten to $geoWithin. The existing logic did not cover usage of geoJson types, which is fixed now. In case of nearSphere it is also required to convert the $maxDistance argument (given in meters for geoJson) to radians which is used by $geoWithin $centerSphere.

Closes #4004
Original pull request: #4006.
Related to #2925
2022-09-21 10:48:21 +02:00
Spring Builds
d7fc605f7b After release cleanups.
See #4117
2022-09-19 14:39:07 +00:00
Spring Builds
3b805b9e03 Prepare next development iteration.
See #4117
2022-09-19 14:38:55 +00:00
Spring Builds
91cca3f2c4 Release version 4.0 M6 (2022.0.0).
See #4117
2022-09-19 14:15:22 +00:00
Spring Builds
2de6384d0f Prepare 4.0 M6 (2022.0.0).
See #4117
2022-09-19 14:12:56 +00:00
Christoph Strobl
ab1c0ff7b8 Apply conversion on document reference lookup using nested property.
Closes #4033
Original pull request: #4044.
2022-09-19 09:57:24 +02:00
Christoph Strobl
ae2846c5bf Generate and convert id on insert if explicitly defined.
We now make sure to provide an id value that matches the desired target type when no id is set, and the property defines an explicit conversion target.
Previously a new ObjectId would have been generated which leads to type inconsistencies when querying for _id.

Closes #4026
Original pull request: #4057.
2022-09-19 09:47:18 +02:00
Christoph Strobl
e88c9cf791 Fix issue with reference conversion in updates.
We now make sure to convert references in update operations targeting collection like fields when using eg. the push modifier.

Closes #4041
Original pull request: #4045.
2022-09-19 08:54:26 +02:00
Christoph Strobl
fadca10f62 Support @DocumentReference via Querydsl.
Closes #4037
Original pull request: #4069.
2022-09-16 15:57:30 +02:00
Mark Paluch
40320136f3 Polishing.
See #4061
Original pull request: #4062.
2022-09-16 14:52:00 +02:00
Christoph Strobl
bc575de3b0 Improve exception message when deriving collection name from type.
We now provide a better worded exception message when trying to derive the collection name for a type that is not considered a user types (such as org.bson.Document).
Update the Javadoc to hint to the error.

Closes #4061
Original pull request: #4062.
2022-09-16 14:51:54 +02:00
Christoph Strobl
09b2afa79d Initialize lists with size where possible.
Closes #3941
Original pull request: #3974.
2022-09-16 14:45:34 +02:00
Mark Paluch
96b564eb9a Polishing.
Reformat code.

See #4167.
Original pull request: #4168.
2022-09-16 14:40:34 +02:00
Christoph Strobl
38390d3475 Fix usage of change stream option startAfter.
We now make sure to apply the token to startAfter method of the driver. Before this change it had been incorrectly applied to resumeAfter.

Closes #4167.
Original pull request: #4168.
2022-09-16 14:40:27 +02:00
Mark Paluch
6937bb519b Polishing.
Move off more deprecated API.

See #4164
Original pull request: #4165.
2022-09-16 11:11:17 +02:00
Christoph Strobl
6e4d463053 Move off deprecated API.
Closes #4164
Original pull request: #4165.
2022-09-16 11:11:12 +02:00
Mark Paluch
a9d2050806 Polishing.
Fix generics. Add warning suppressions for nullability checks.

See: #4104
Original pull request: #4156.
2022-09-14 14:06:44 +02:00
Christoph Strobl
6676389062 Fix GeoJson polygon conversion for polygons with inner ring.
Closes: #4104
Original pull request: #4156.
2022-09-14 14:06:35 +02:00
Mark Paluch
81f85b8cca Polishing.
Tweak Javadoc, make ViewOptions.collation final.

See: #2594
Original pull request: #4142.
2022-09-14 11:31:14 +02:00
Christoph Strobl
77f318bd77 Add support to create views via reactive/template API.
This commit introduces support to create MongoDB Views directly via the Reactive-/MongoOperations API.

Closes: #2594
Original pull request: #4142.
2022-09-14 11:30:24 +02:00
Brian Clozel
7c7e70418f Replace deprecated StreamUtils API
As of spring-projects/spring-framework#29125,
`StreamUtils..emptyInput()` is deprecated in favor of
`InputStream.nullInputStream()` from the JDK.

Closes: #4160
2022-09-14 08:14:06 +02:00
Mark Paluch
44a1123034 Adopt to changed Mockk artifact name.
Closes #4161
2022-09-12 14:08:14 +02:00
Mark Paluch
e487c08b0c Polishing.
Reformat pom.xml

See #4161
2022-09-12 10:18:38 +02:00
Mark Paluch
a002d30aa9 Polishing.
Reformat pom.xml

See #4161
2022-09-12 10:17:58 +02:00
Tommy Ludwig
36ddd26edc Adapt to SampleTestRunner refactor.
See: micrometer-metrics/tracing#57
Closes: #4159
2022-09-09 19:13:57 +02:00
Tommy Ludwig
6197655e98 Adapt to ObservationConvention location change
See: micrometer-metrics/micrometer#3387
Closes: #4158
2022-09-09 19:06:28 +02:00
Christoph Strobl
929faea88b Add snapshot plugin repository for micrometer docs.
See: #4151.
2022-09-07 14:51:57 +02:00
Greg L. Turnquist
1fe1c13531 Upgrade to Micrometer 1.10.0-SNAPSHOT.
Closes #4151.
2022-09-07 12:17:16 +02:00
Kirill Gavrilov
838ddb5d26 Align signature of Kotlin extension functions to match Java API.
Closes: #4153
Original Pull Request: #4154
Related issues: #2602 #3187
2022-09-07 09:48:48 +02:00
Christoph Strobl
33c7f0980f Remove usage SynthesizedAnnotation.
Closes: #4155
2022-09-07 08:54:41 +02:00
Mark Paluch
4bbc443a0e Polishing.
Refine assertions.

See #4132
Original pull request: #4147.
2022-08-25 15:45:29 +02:00
Christoph Strobl
655dbc9783 Favor relaxed type mapping over strict one for aggregateStream.
Align aggregation context usage of aggregate and aggregate stream methods.

Closes #4132
Original pull request: #4147.
2022-08-25 15:45:13 +02:00
Christoph Strobl
0d752fd6e6 Introduce dedicated Collation annotation.
The Collation annotation mainly serves as a meta annotation that allows common access to retrieving collation values for annotated queries, aggregations, etc.

Original Pull Request: #4131
2022-08-25 09:03:43 +02:00
Christoph Strobl
8aabf2fa5e Polishing.
Resolve collation from template expression & update issue references + Javadoc.

Original Pull Request: #4131
2022-08-25 09:00:51 +02:00
Stefan Tirea
ff9d338bd7 Add collation for an index via @CompoundIndex and @Index annotations.
Closes #3002, closes #4130

Original Pull Request: #4131
2022-08-25 09:00:17 +02:00
Mark Paluch
2a4ee12363 Document BulkOperations limitations.
Closes #4082
2022-08-23 15:39:59 +02:00
Christoph Strobl
a66438fc20 Resolve cglib proxies during AOT processing.
We now make sure to run the enhancer during AOT which allows the infrastructure to pick up the generated type.
Along the lines we removed the no longer supported asserts for class proxies and followed changes in FW6.

Closes: #4148
2022-08-23 11:48:23 +02:00
Mark Paluch
0ccc037b8e Polishing.
Introduce JUnit extension to declare tests that dirty or provide their state.

See #3817
Original pull request: #3987.
2022-08-23 10:00:16 +02:00
Christoph Strobl
00792192c3 Close clients created during tests.
See #3817
Original pull request: #3987.
2022-08-23 09:54:29 +02:00
Christoph Strobl
e064b505c9 Prevent sync client from being created in reactive test config.
Closes #3817
Original pull request: #3987.
2022-08-23 09:53:58 +02:00
Christoph Strobl
7df2bdf8ff Upgrade to MongoDB driver 4.7.1
Closes: #4144
2022-08-22 07:59:57 +02:00
Mark Paluch
2f9fc1618e Use mongosh instead of mongo CLI.
Switch from the deprecated command to its replacement.

See #4138
2022-08-17 12:56:26 +02:00
Mark Paluch
9e2aecf4ae Polishing.
Fix required Java version.

See #4140
2022-08-17 10:58:13 +02:00
Mark Paluch
c32c4beb59 Remove new & noteworthy section in favor of our release notes.
The release notes now outline new and noteworthy changes.

Closes #4140
2022-08-17 10:51:01 +02:00
Mark Paluch
d48f3ec535 Polishing.
Use && syntax to catch commands that exit with non-success exit codes.

See #4139
2022-08-17 10:44:11 +02:00
Mark Paluch
5f16aecd13 Assert compatibility with MongoDB 6.0.
Closes #4138
2022-08-17 10:42:57 +02:00
Mark Paluch
5fc49b1649 Polishing.
Encapsulate nested object lookup. Refine method signatures and tweak Javadoc.

See #4098
Original pull request: #4133.
2022-08-05 15:59:31 +02:00
Christoph Strobl
1e7dc7ce66 Fix non-association mapping when id value matches already resolved instance of same type.
This commit ensures to fully resolve non association values from the given source document instead of trying attempt a by id lookup in already resolved instances.

Closes: #4098
Original pull request: #4133.
2022-08-05 15:59:04 +02:00
Christoph Strobl
234783f442 Allow referencing the $id field of dbrefs within an aggregation pipeline.
Closes: #4123
Original pull request: #4125.
2022-08-05 14:19:29 +02:00
Sojin
3429350964 Fix AKNOWLEDGED typo in reference documentation.
Two typos found have been updated

Closes #4135
2022-08-05 14:07:54 +02:00
Mark Paluch
d130984bdc Allow disabling entity lifecycle events.
We now support disabling lifecycle events through the Template API to reduce the framework overhead when events are not needed.

Closes #4107
2022-07-20 16:07:01 +02:00
Christoph Strobl
f5378bf825 Upgrade to MongoDB driver 4.7.0
Closes: #4124
2022-07-20 08:13:53 +02:00
Mark Paluch
21057c3d17 Fix DTO projection instantiation.
We now correctly instantiate DTO projection classes by using the actual constructor argument type. Previously, we did not update the conversion context to fetch the correct type but used the type of the DTO projection class instead of the constructor argument.

Closes #4120
2022-07-19 11:17:40 +02:00
Christoph Strobl
d0a98eb71d After release cleanups.
See #4054
2022-07-15 15:30:53 +02:00
Christoph Strobl
bc95c4d390 Prepare next development iteration.
See #4054
2022-07-15 15:30:45 +02:00
1199 changed files with 33661 additions and 12359 deletions

7
.gitignore vendored
View File

@@ -15,4 +15,9 @@ src/ant/.ant-targets-upload-dist.xml
atlassian-ide-plugin.xml
/.gradle/
/.idea/
*.graphml
*.graphml
build/
node_modules
node
package.json
package-lock.json

View File

@@ -1,2 +1,2 @@
#Fri Jun 03 09:32:40 CEST 2022
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
#Mon Aug 14 08:53:22 EDT 2023
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip

View File

@@ -16,7 +16,7 @@ All of these use cases are great reasons to essentially run what the CI server d
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-openjdk8-with-mongodb-4.0:latest /bin/bash`
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-mongodb-github springci/spring-data-openjdk17-with-mongodb-5.0.3:latest /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-mongodb-github`.
+

86
Jenkinsfile vendored
View File

@@ -58,10 +58,48 @@ pipeline {
}
}
}
stage('Publish JDK (Java 17) + MongoDB 6.0') {
when {
anyOf {
changeset "ci/openjdk17-mongodb-6.0/**"
changeset "ci/pipeline.properties"
}
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-with-mongodb-6.0:${p['java.main.tag']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg MONGODB=${p['docker.mongodb.6.0.version']} ci/openjdk17-mongodb-6.0/")
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
image.push()
}
}
}
}
stage('Publish JDK (Java 20) + MongoDB 6.0') {
when {
anyOf {
changeset "ci/openjdk20-mongodb-6.0/**"
changeset "ci/pipeline.properties"
}
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-with-mongodb-6.0:${p['java.next.tag']}", "--build-arg BASE=${p['docker.java.next.image']} --build-arg MONGODB=${p['docker.mongodb.6.0.version']} ci/openjdk20-mongodb-6.0/")
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
image.push()
}
}
}
}
}
}
stage("test: baseline (Java 17)") {
stage("test: baseline (main)") {
when {
beforeAgent(true)
anyOf {
@@ -100,7 +138,7 @@ pipeline {
}
parallel {
stage("test: mongodb 5.0 (Java 17)") {
stage("test: MongoDB 5.0 (main)") {
agent {
label 'data'
}
@@ -121,6 +159,50 @@ pipeline {
}
}
}
stage("test: MongoDB 6.0 (main)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-6.0:${p['java.main.tag']}").inside(p['docker.java.inside.basic']) {
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongosh --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 -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
}
stage("test: MongoDB 6.0 (next)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.image("harbor-repo.vmware.com/dockerhub-proxy-cache/springci/spring-data-with-mongodb-6.0:${p['java.next.tag']}").inside(p['docker.java.inside.basic']) {
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongosh --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 -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
}
}
}
}

View File

@@ -93,142 +93,11 @@ and declare the appropriate dependency version.
</repository>
----
== Upgrading from 2.x
[[upgrading]]
== Upgrading
The 4.0 MongoDB Java Driver does no longer support certain features that have already been deprecated in one of the last minor versions.
Some of the changes affect the initial setup configuration as well as compile/runtime features. We summarized the most typical changes one might encounter.
=== XML Namespace
.Changed XML Namespace Elements and Attributes:
|===
| Element / Attribute | 2.x | 3.x
| `<mongo:mongo-client />`
| Used to create a `com.mongodb.MongoClient`
| Now exposes a `com.mongodb.client.MongoClient`
| `<mongo:mongo-client replica-set="..." />`
| Was a comma delimited list of replica set members (host/port)
| Now defines the replica set name. +
Use `<mongo:client-settings cluster-hosts="..." />` instead
| `<mongo:db-factory writeConcern="..." />`
| NONE, NORMAL, SAFE, FSYNC_SAFE, REPLICAS_SAFE, MAJORITY
| W1, W2, W3, UNAKNOWLEDGED, AKNOWLEDGED, JOURNALED, MAJORITY
|===
.Removed XML Namespace Elements and Attributes:
|===
| Element / Attribute | Replacement in 3.x | Comment
| `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory mongo-client-ref="..." />`
| Referencing a `com.mongodb.client.MongoClient`.
| `<mongo:mongo-client credentials="..." />`
| `<mongo:mongo-client credential="..." />`
| Single authentication data instead of list.
| `<mongo:client-options />`
| `<mongo:client-settings />`
| See `com.mongodb.MongoClientSettings` for details.
|===
.New XML Namespace Elements and Attributes:
|===
| Element | Comment
| `<mongo:db-factory mongo-client-ref="..." />`
| Replacement for `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:mongo-client connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:client-settings />`
| Namespace element for `com.mongodb.MongoClientSettings`.
|===
=== Java Configuration
.Java API changes
|===
| Type | Comment
| `MongoClientFactoryBean`
| Creates `com.mongodb.client.MongoClient` instead of `com.mongodb.MongoClient` +
Uses `MongoClientSettings` instead of `MongoClientOptions`.
| `MongoDataIntegrityViolationException`
| Uses `WriteConcernResult` instead of `WriteResult`.
| `BulkOperationException`
| Uses `MongoBulkWriteException` and `com.mongodb.bulk.BulkWriteError` instead of `BulkWriteException` and `com.mongodb.BulkWriteError`
| `ReactiveMongoClientFactoryBean`
| Uses `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
| `ReactiveMongoClientSettingsFactoryBean`
| Now produces `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
|===
.Removed Java API:
|===
| 2.x | Replacement in 3.x | Comment
| `MongoClientOptionsFactoryBean`
| `MongoClientSettingsFactoryBean`
| Creating a `com.mongodb.MongoClientSettings`.
| `AbstractMongoConfiguration`
| `AbstractMongoClientConfiguration` +
(Available since 2.1)
| Using `com.mongodb.client.MongoClient`.
| `MongoDbFactory#getLegacyDb()`
| -
| -
| `SimpleMongoDbFactory`
| `SimpleMongoClientDbFactory` +
(Available since 2.1)
|
| `MapReduceOptions#getOutputType()`
| `MapReduceOptions#getMapReduceAction()`
| Returns `MapReduceAction` instead of `MapReduceCommand.OutputType`.
| `Meta\|Query` maxScan & snapshot
|
|
|===
=== Other Changes
==== UUID Types
The MongoDB UUID representation can now be configured with different formats.
This has to be done via `MongoClientSettings` as shown in the snippet below.
.UUID Codec Configuration
====
[source,java]
----
static class Config extends AbstractMongoClientConfiguration {
@Override
public void configureClientSettings(MongoClientSettings.Builder builder) {
builder.uuidRepresentation(UuidRepresentation.STANDARD);
}
// ...
}
----
====
Instructions for how to upgrade from earlier versions of Spring Data are provided on the project https://github.com/spring-projects/spring-data-commons/wiki[wiki].
Follow the links in the https://github.com/spring-projects/spring-data-commons/wiki#release-notes[release notes section] to find the version that you want to upgrade to.
[[getting-help]]
== Getting Help
@@ -277,8 +146,8 @@ and accessible from Maven using the Maven configuration noted <<maven-configurat
NOTE: Configuration for Gradle is similar to Maven.
The best way to get started is by creating a Spring Boot project using MongoDB on https://start.spring.io[start.spring.io].
Follow this https://start.spring.io/#type=maven-project&language=java&platformVersion=2.5.4&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-mongodb[link]
to build an imperative application and this https://start.spring.io/#type=maven-project&language=java&platformVersion=2.5.4&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-mongodb-reactive[link]
Follow this https://start.spring.io/#type=maven-project&language=java&platformVersion=3.0.0&packaging=jar&jvmVersion=17&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-mongodb[link]
to build an imperative application and this https://start.spring.io/#type=maven-project&language=java&platformVersion=3.0.0&packaging=jar&jvmVersion=17&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-mongodb-reactive[link]
to build a reactive one.
However, if you want to try out the latest and greatest, Spring Data MongoDB can be easily built with the https://github.com/takari/maven-wrapper[Maven wrapper]
@@ -313,7 +182,7 @@ To initialize the replica set, start a mongo client:
[source,bash]
----
$ $MONGODB_HOME/bin/mongo
MongoDB server version: 5.0.0
MongoDB server version: 6.0.0
...
----
@@ -341,7 +210,7 @@ Now you are ready to build Spring Data MongoDB. Simply enter the following `mvnw
$ ./mvnw clean install
----
If you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.5.0 or above].
If you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.8.0 or above].
_Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull requests, and in particular, please sign
the https://cla.pivotal.io/sign/spring[Contributors Agreement] before your first non-trivial change._

View File

@@ -10,7 +10,7 @@ All of these use cases are great reasons to essentially run what Concourse does
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`
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-mongodb-github springci/spring-data-openjdk17-with-mongodb-5.0.3 /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-mongodb-github`.
+
@@ -23,7 +23,7 @@ Since the container is binding to your source, you can make edits from your IDE
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`
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-openjdk17-with-mongodb-5.0.3 /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`.
@@ -36,4 +36,4 @@ IMPORTANT: `build.sh` doesn't actually push to Artifactory so don't worry about
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.
NOTE: Docker containers can eat up disk space fast! From time to time, run `docker system prune` to clean out old images.

View File

@@ -7,15 +7,16 @@ ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list; \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list; \
sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 656408E390CFB1F5 ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list; \
echo ${TZ} > /etc/timezone;
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
sed -i -e 's/http/https/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 && \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 656408E390CFB1F5 && \
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list && \
echo ${TZ} > /etc/timezone
RUN apt-get update ; \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;
RUN apt-get update && \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

View File

@@ -7,17 +7,18 @@ ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list; \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list; \
sed -i -e 's/http/https/g' /etc/apt/sources.list ; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 wget ; \
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
sed -i -e 's/http/https/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 wget && \
# MongoDB 5.0 release signing key
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv B00A0BD1E2C63C11 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv B00A0BD1E2C63C11 && \
# Needed when MongoDB creates a 5.0 folder.
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-5.0.list; \
echo ${TZ} > /etc/timezone;
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-5.0.list && \
echo ${TZ} > /etc/timezone
RUN apt-get update; \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;
RUN apt-get update && \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

View File

@@ -0,0 +1,24 @@
ARG BASE
FROM ${BASE}
# Any ARG statements before FROM are cleared.
ARG MONGODB
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
sed -i -e 's/http/https/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 wget && \
# MongoDB 6.0 release signing key
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add - && \
# Needed when MongoDB creates a 6.0 folder.
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list && \
echo ${TZ} > /etc/timezone
RUN apt-get update && \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

View File

@@ -0,0 +1,24 @@
ARG BASE
FROM ${BASE}
# Any ARG statements before FROM are cleared.
ARG MONGODB
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
sed -i -e 's/ports.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list && \
sed -i -e 's/http/https/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 wget && \
# MongoDB 6.0 release signing key
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add - && \
# Needed when MongoDB creates a 6.0 folder.
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list && \
echo ${TZ} > /etc/timezone
RUN apt-get update && \
apt-get install -y mongodb-org=${MONGODB} mongodb-org-server=${MONGODB} mongodb-org-shell=${MONGODB} mongodb-org-mongos=${MONGODB} mongodb-org-tools=${MONGODB} && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

View File

@@ -1,18 +1,21 @@
# Java versions
java.main.tag=17.0.2_8-jdk
java.main.tag=17.0.8_7-jdk-focal
java.next.tag=20-jdk-jammy
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
# Supported versions of MongoDB
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
docker.mongodb.4.4.version=4.4.23
docker.mongodb.5.0.version=5.0.19
docker.mongodb.6.0.version=6.0.8
# Supported versions of Redis
docker.redis.6.version=6.2.6
docker.redis.6.version=6.2.13
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.12
docker.cassandra.3.version=3.11.15
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home

34
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-M5</version>
<version>4.2.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.0.0-M5</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<modules>
@@ -26,8 +26,8 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>3.0.0-M5</springdata.commons>
<mongo>4.7.0-beta0</mongo>
<springdata.commons>3.2.0-SNAPSHOT</springdata.commons>
<mongo>4.10.2</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -145,33 +145,19 @@
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-M5</version>
<version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.
@@ -96,15 +96,14 @@ class MongoResultsWriter implements ResultsWriter {
for (Object key : doc.keySet()) {
Object value = doc.get(key);
if (value instanceof Document) {
value = fixDocumentKeys((Document) value);
} else if (value instanceof BasicDBObject) {
value = fixDocumentKeys(new Document((BasicDBObject) value));
if (value instanceof Document document) {
value = fixDocumentKeys(document);
} else if (value instanceof BasicDBObject basicDBObject) {
value = fixDocumentKeys(new Document(basicDBObject));
}
if (key instanceof String) {
if (key instanceof String newKey) {
String newKey = (String) key;
if (newKey.contains(".")) {
newKey = newKey.replace('.', ',');
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -15,86 +15,41 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-M5</version>
<version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<project.root>${basedir}/..</project.root>
<dist.key>SDMONGO</dist.key>
<!-- Observability -->
<micrometer-docs-generator.inputPath>${maven.multiModuleProjectDirectory}/spring-data-mongodb/</micrometer-docs-generator.inputPath>
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
<micrometer-docs-generator.outputPath>${maven.multiModuleProjectDirectory}/target/</micrometer-docs-generator.outputPath>
<antora.playbook>${project.basedir}/../src/main/antora/antora-playbook.yml</antora.playbook>
</properties>
<build>
<resources>
<resource>
<directory>${project.basedir}/../src/main/antora/resources/antora-resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-metrics-metadata</id>
<phase>prepare-package</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>io.micrometer.docs.metrics.DocsFromSources
</mainClass>
</configuration>
</execution>
<execution>
<id>generate-tracing-metadata</id>
<phase>prepare-package</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>io.micrometer.docs.spans.DocsFromSources
</mainClass>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-docs-generator-spans</artifactId>
<version>${micrometer-docs-generator}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-docs-generator-metrics</artifactId>
<version>${micrometer-docs-generator}</version>
<type>jar</type>
</dependency>
</dependencies>
<configuration>
<includePluginDependencies>true</includePluginDependencies>
<arguments>
<argument>${micrometer-docs-generator.inputPath}</argument>
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
<argument>${micrometer-docs-generator.outputPath}</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<configuration>
<attributes>
<mongo-reactivestreams>${mongo.reactivestreams}
</mongo-reactivestreams>
<reactor>${reactor}</reactor>
</attributes>
</configuration>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-maven-plugin</artifactId>
</plugin>
</plugins>

View File

@@ -1,391 +1,410 @@
<?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"
<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>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-mongodb</artifactId>
<artifactId>spring-data-mongodb</artifactId>
<name>Spring Data MongoDB - Core</name>
<description>MongoDB support for Spring Data</description>
<name>Spring Data MongoDB - Core</name>
<description>MongoDB support for Spring Data</description>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.0.0-M5</version>
<relativePath>../pom.xml</relativePath>
</parent>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<objenesis>1.3</objenesis>
<equalsverifier>1.7.8</equalsverifier>
<java-module-name>spring.data.mongodb</java-module-name>
<project.root>${basedir}/..</project.root>
<multithreadedtc>1.01</multithreadedtc>
</properties>
<properties>
<objenesis>1.3</objenesis>
<equalsverifier>1.7.8</equalsverifier>
<java-module-name>spring.data.mongodb</java-module-name>
<project.root>${basedir}/..</project.root>
<multithreadedtc>1.01</multithreadedtc>
</properties>
<dependencies>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<optional>true</optional>
</dependency>
<!-- reactive -->
<!-- reactive -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>${mongo}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>${mongo}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo.reactivestreams}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>${mongo.reactivestreams}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-crypt</artifactId>
<version>1.8.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava3}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<optional>true</optional>
</dependency>
<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava3}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>${jakarta-annotation-api}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-se</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>${jakarta-annotation-api}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-spi</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-se</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-impl</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-spi</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<!-- JSR 303 Validation -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${validation}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-impl</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis}</version>
<optional>true</optional>
</dependency>
<!-- JSR 303 Validation -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${validation}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.1.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.1.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>${equalsverifier}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>${equalsverifier}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.schauderhaft.degraph</groupId>
<artifactId>degraph-check</artifactId>
<version>0.1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>edu.umd.cs.mtc</groupId>
<artifactId>multithreadedtc</artifactId>
<version>${multithreadedtc}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.schauderhaft.degraph</groupId>
<artifactId>degraph-check</artifactId>
<version>0.1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.5.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>edu.umd.cs.mtc</groupId>
<artifactId>multithreadedtc</artifactId>
<version>${multithreadedtc}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.5.3</version>
<scope>test</scope>
</dependency>
<!-- Kotlin extension -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<optional>true</optional>
</dependency>
<!-- Kotlin extension -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<version>${mockk}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk-jvm</artifactId>
<version>${mockk}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-integration-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-test</artifactId>
<scope>test</scope>
</dependency>
<!-- jMolecules -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-integration-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmolecules</groupId>
<artifactId>jmolecules-ddd</artifactId>
<version>${jmolecules}</version>
<scope>test</scope>
</dependency>
<!-- jMolecules -->
</dependencies>
<dependency>
<groupId>org.jmolecules</groupId>
<artifactId>jmolecules-ddd</artifactId>
<version>${jmolecules}</version>
<scope>test</scope>
</dependency>
<build>
</dependencies>
<plugins>
<build>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt}</version>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-test-sources</outputDirectory>
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/PerformanceTests.java</exclude>
<exclude>**/ReactivePerformanceTests.java</exclude>
</excludes>
<systemPropertyVariables>
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
<reactor.trace.cancel>true</reactor.trace.cancel>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt}</version>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-test-sources
</outputDirectory>
<processor>
org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/PerformanceTests.java</exclude>
<exclude>**/ReactivePerformanceTests.java</exclude>
</excludes>
<systemPropertyVariables>
<java.util.logging.config.file>
src/test/resources/logging.properties
</java.util.logging.config.file>
<reactor.trace.cancel>true</reactor.trace.cancel>
</systemPropertyVariables>
</configuration>
</plugin>
</build>
</plugins>
</build>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 the original author or authors.
* Copyright 2021-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 the original author or authors.
* Copyright 2021-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.
@@ -353,7 +353,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
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("closed = %b, ", session.getServerSession().isClosed());
debugString += String.format("clusterTime = %s", session.getClusterTime());
} else {
debugString += "id = n/a";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.
@@ -361,7 +361,7 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction
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("closed = %b, ", session.getServerSession().isClosed());
debugString += String.format("clusterTime = %s", session.getClusterTime());
} else {
debugString += "id = n/a";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,13 @@
package org.springframework.data.mongodb;
import org.springframework.dao.UncategorizedDataAccessException;
import org.springframework.lang.Nullable;
public class UncategorizedMongoDbException extends UncategorizedDataAccessException {
private static final long serialVersionUID = -2336595514062364929L;
public UncategorizedMongoDbException(String msg, Throwable cause) {
public UncategorizedMongoDbException(String msg, @Nullable Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,19 @@ package org.springframework.data.mongodb.aot;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.TypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.data.annotation.Reference;
import org.springframework.data.aot.TypeUtils;
import org.springframework.data.mongodb.core.convert.LazyLoadingProxyFactory;
import org.springframework.data.mongodb.core.convert.LazyLoadingProxyFactory.LazyLoadingInterceptor;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
@@ -36,11 +38,11 @@ import org.springframework.data.mongodb.core.mapping.DocumentReference;
* @author Christoph Strobl
* @since 4.0
*/
class LazyLoadingProxyAotProcessor {
public class LazyLoadingProxyAotProcessor {
private boolean generalLazyLoadingProxyContributed = false;
void registerLazyLoadingProxyIfNeeded(Class<?> type, GenerationContext generationContext) {
public void registerLazyLoadingProxyIfNeeded(Class<?> type, GenerationContext generationContext) {
Set<Field> refFields = getFieldsWithAnnotationPresent(type, Reference.class);
if (refFields.isEmpty()) {
@@ -63,9 +65,7 @@ class LazyLoadingProxyAotProcessor {
if (field.getType().isInterface()) {
List<Class<?>> interfaces = new ArrayList<>(
TypeUtils.resolveTypesInSignature(ResolvableType.forField(field, type)));
interfaces.add(0, org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class);
Arrays.asList(LazyLoadingProxyFactory.prepareFactory(field.getType()).getProxiedInterfaces()));
interfaces.add(org.springframework.aop.SpringProxy.class);
interfaces.add(org.springframework.aop.framework.Advised.class);
interfaces.add(org.springframework.core.DecoratingProxy.class);
@@ -73,9 +73,12 @@ class LazyLoadingProxyAotProcessor {
generationContext.getRuntimeHints().proxies().registerJdkProxy(interfaces.toArray(Class[]::new));
} else {
generationContext.getRuntimeHints().proxies().registerClassProxy(field.getType(), builder -> {
builder.proxiedInterfaces(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class);
});
Class<?> proxyClass = LazyLoadingProxyFactory.resolveProxyType(field.getType(),
LazyLoadingInterceptor::none);
// see: spring-projects/spring-framework/issues/29309
generationContext.getRuntimeHints().reflection().registerType(proxyClass,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS);
}
});
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,29 @@ package org.springframework.data.mongodb.aot;
import java.util.function.Predicate;
import org.springframework.data.aot.TypeUtils;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.util.ReactiveWrappers;
import org.springframework.data.util.ReactiveWrappers.ReactiveLibrary;
import org.springframework.data.util.TypeUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* @author Christoph Strobl
* @since 4.0
*/
class MongoAotPredicates {
public class MongoAotPredicates {
static final Predicate<Class<?>> IS_SIMPLE_TYPE = (type) -> MongoSimpleTypes.HOLDER.isSimpleType(type) || TypeUtils.type(type).isPartOf("org.bson");
public static final Predicate<Class<?>> IS_SIMPLE_TYPE = (type) -> MongoSimpleTypes.HOLDER.isSimpleType(type) || TypeUtils.type(type).isPartOf("org.bson");
public static final Predicate<ReactiveLibrary> IS_REACTIVE_LIBARARY_AVAILABLE = (lib) -> ReactiveWrappers.isAvailable(lib);
public static final Predicate<ClassLoader> IS_SYNC_CLIENT_PRESENT = (classLoader) -> ClassUtils.isPresent("com.mongodb.client.MongoClient", classLoader);
public static boolean isReactorPresent() {
return IS_REACTIVE_LIBARARY_AVAILABLE.test(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR);
}
public static boolean isSyncClientPresent(@Nullable ClassLoader classLoader) {
return IS_SYNC_CLIENT_PRESENT.test(classLoader);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.aot;
import static org.springframework.data.mongodb.aot.MongoAotPredicates.*;
import java.util.Arrays;
import org.springframework.aot.hint.MemberCategory;
@@ -29,10 +31,8 @@ import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertC
import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link RuntimeHintsRegistrar} for repository types and entity callbacks.
@@ -43,27 +43,41 @@ import org.springframework.lang.Nullable;
*/
class MongoRuntimeHints implements RuntimeHintsRegistrar {
private static final boolean PROJECT_REACTOR_PRESENT = ReactiveWrappers
.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR);
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerTypes(
Arrays.asList(TypeReference.of(SimpleMongoRepository.class), TypeReference.of(BeforeConvertCallback.class),
TypeReference.of(BeforeSaveCallback.class), TypeReference.of(AfterConvertCallback.class),
TypeReference.of(AfterSaveCallback.class)),
Arrays.asList(TypeReference.of(BeforeConvertCallback.class), TypeReference.of(BeforeSaveCallback.class),
TypeReference.of(AfterConvertCallback.class), TypeReference.of(AfterSaveCallback.class)),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
if (PROJECT_REACTOR_PRESENT) {
registerTransactionProxyHints(hints, classLoader);
if (isReactorPresent()) {
hints.reflection()
.registerTypes(Arrays.asList(TypeReference.of(SimpleReactiveMongoRepository.class),
TypeReference.of(ReactiveBeforeConvertCallback.class), TypeReference.of(ReactiveBeforeSaveCallback.class),
TypeReference.of(ReactiveAfterConvertCallback.class), TypeReference.of(ReactiveAfterSaveCallback.class)),
.registerTypes(Arrays.asList(TypeReference.of(ReactiveBeforeConvertCallback.class),
TypeReference.of(ReactiveBeforeSaveCallback.class), TypeReference.of(ReactiveAfterConvertCallback.class),
TypeReference.of(ReactiveAfterSaveCallback.class)),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
private static void registerTransactionProxyHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
if (MongoAotPredicates.isSyncClientPresent(classLoader)
&& ClassUtils.isPresent("org.springframework.aop.SpringProxy", classLoader)) {
hints.proxies().registerJdkProxy(TypeReference.of("com.mongodb.client.MongoDatabase"),
TypeReference.of("org.springframework.aop.SpringProxy"),
TypeReference.of("org.springframework.core.DecoratingProxy"));
hints.proxies().registerJdkProxy(TypeReference.of("com.mongodb.client.MongoCollection"),
TypeReference.of("org.springframework.aop.SpringProxy"),
TypeReference.of("org.springframework.core.DecoratingProxy"));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 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.
@@ -27,7 +27,6 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.domain.ManagedTypes;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -207,7 +206,7 @@ public abstract class MongoConfigurationSupport {
* {@link org.springframework.data.mongodb.core.index.IndexDefinition} from the entity or not.
*
* @return {@literal false} by default. <br />
* <strong>INFO</strong>: As of 3.x the default is set to {@literal false}; In 2.x it was {@literal true}.
* <strong>INFO:</strong> As of 3.x the default is set to {@literal false}; In 2.x it was {@literal true}.
* @since 2.2
*/
protected boolean autoIndexCreation() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
*/
package org.springframework.data.mongodb.core;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -24,23 +22,16 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.DomainTypeMapping;
import org.springframework.data.mongodb.core.aggregation.CountOperation;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Utility methods to map {@link org.springframework.data.mongodb.core.aggregation.Aggregation} pipeline definitions and
@@ -104,12 +95,7 @@ class AggregationUtil {
* @return
*/
List<Document> createPipeline(Aggregation aggregation, AggregationOperationContext context) {
if (ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
return aggregation.toPipeline(context);
}
return mapAggregationPipeline(aggregation.toPipeline(context));
return aggregation.toPipeline(context);
}
/**
@@ -120,16 +106,7 @@ class AggregationUtil {
* @return
*/
Document createCommand(String collection, Aggregation aggregation, AggregationOperationContext context) {
Document command = aggregation.toDocument(collection, context);
if (!ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
return command;
}
command.put("pipeline", mapAggregationPipeline(command.get("pipeline", List.class)));
return command;
return aggregation.toDocument(collection, context);
}
private List<Document> mapAggregationPipeline(List<Document> pipeline) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,16 +19,30 @@ import java.util.List;
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.Pair;
import com.mongodb.bulk.BulkWriteResult;
/**
* Bulk operations for insert/update/remove actions on a collection. These bulks operation are available since MongoDB
* 2.6 and make use of low level bulk commands on the protocol level. This interface defines a fluent API to add
* multiple single operations or list of similar operations in sequence which can then eventually be executed by calling
* Bulk operations for insert/update/remove actions on a collection. Bulk operations are available since MongoDB 2.6 and
* make use of low level bulk commands on the protocol level. This interface defines a fluent API to add multiple single
* operations or list of similar operations in sequence which can then eventually be executed by calling
* {@link #execute()}.
*
* <pre class="code">
* MongoOperations ops = …;
*
* ops.bulkOps(BulkMode.UNORDERED, Person.class)
* .insert(newPerson)
* .updateOne(where("firstname").is("Joe"), Update.update("lastname", "Doe"))
* .execute();
* </pre>
* <p>
* Bulk operations are issued as one batch that pulls together all insert, update, and delete operations. Operations
* that require individual operation results such as optimistic locking (using {@code @Version}) are not supported and
* the version field remains not populated.
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Minsu Kim
@@ -71,7 +85,19 @@ public interface BulkOperations {
* @param update {@link Update} operation to perform, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations updateOne(Query query, Update update);
default BulkOperations updateOne(Query query, Update update) {
return updateOne(query, (UpdateDefinition) update);
}
/**
* Add a single update to the bulk operation. For the update request, only the first matching document is updated.
*
* @param query update criteria, must not be {@literal null}.
* @param update {@link Update} operation to perform, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperations updateOne(Query query, UpdateDefinition update);
/**
* Add a list of updates to the bulk operation. For each update request, only the first matching document is updated.
@@ -79,7 +105,7 @@ public interface BulkOperations {
* @param updates Update operations to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations updateOne(List<Pair<Query, Update>> updates);
BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates);
/**
* Add a single update to the bulk operation. For the update request, all matching documents are updated.
@@ -88,7 +114,19 @@ public interface BulkOperations {
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations updateMulti(Query query, Update update);
default BulkOperations updateMulti(Query query, Update update) {
return updateMulti(query, (UpdateDefinition) update);
}
/**
* Add a single update to the bulk operation. For the update request, all matching documents are updated.
*
* @param query Update criteria.
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperations updateMulti(Query query, UpdateDefinition update);
/**
* Add a list of updates to the bulk operation. For each update request, all matching documents are updated.
@@ -96,7 +134,7 @@ public interface BulkOperations {
* @param updates Update operations to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations updateMulti(List<Pair<Query, Update>> updates);
BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates);
/**
* Add a single upsert to the bulk operation. An upsert is an update if the set of matching documents is not empty,
@@ -106,7 +144,20 @@ public interface BulkOperations {
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperations upsert(Query query, Update update);
default BulkOperations upsert(Query query, Update update) {
return upsert(query, (UpdateDefinition) update);
}
/**
* Add a single upsert to the bulk operation. An upsert is an update if the set of matching documents is not empty,
* else an insert.
*
* @param query Update criteria.
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperations upsert(Query query, UpdateDefinition update);
/**
* Add a list of upserts to the bulk operation. An upsert is an update if the set of matching documents is not empty,
@@ -138,7 +189,7 @@ public interface BulkOperations {
*
* @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}.
* @return the current {@link BulkOperations} instance with the replacement added, will never be {@literal null}.
* @since 2.2
*/
default BulkOperations replaceOne(Query query, Object replacement) {
@@ -151,7 +202,7 @@ public interface BulkOperations {
* @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}.
* @return the current {@link BulkOperations} instance with the replacement added, will never be {@literal null}.
* @since 2.2
*/
BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options);

View File

@@ -0,0 +1,221 @@
/*
* Copyright 2023 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.List;
import java.util.Optional;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
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.util.Assert;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.DeleteManyModel;
import com.mongodb.client.model.DeleteOneModel;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.UpdateManyModel;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
/**
* Support class for bulk operations.
*
* @author Mark Paluch
* @since 4.1
*/
abstract class BulkOperationsSupport {
private final String collectionName;
BulkOperationsSupport(String collectionName) {
Assert.hasText(collectionName, "CollectionName must not be null nor empty");
this.collectionName = collectionName;
}
/**
* Emit a {@link BeforeSaveEvent}.
*
* @param holder
*/
void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder holder) {
if (holder.model() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.model()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(holder.source(), target, collectionName));
} else if (holder.model() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.model()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(holder.source(), target, collectionName));
}
}
/**
* Emit a {@link AfterSaveEvent}.
*
* @param holder
*/
void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder holder) {
if (holder.model() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.model()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(holder.source(), target, collectionName));
} else if (holder.model() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.model()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(holder.source(), target, collectionName));
}
}
WriteModel<Document> mapWriteModel(Object source, WriteModel<Document> writeModel) {
if (writeModel instanceof UpdateOneModel<Document> model) {
if (source instanceof AggregationUpdate aggregationUpdate) {
List<Document> pipeline = mapUpdatePipeline(aggregationUpdate);
return new UpdateOneModel<>(getMappedQuery(model.getFilter()), pipeline, model.getOptions());
}
return new UpdateOneModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
model.getOptions());
}
if (writeModel instanceof UpdateManyModel<Document> model) {
if (source instanceof AggregationUpdate aggregationUpdate) {
List<Document> pipeline = mapUpdatePipeline(aggregationUpdate);
return new UpdateManyModel<>(getMappedQuery(model.getFilter()), pipeline, model.getOptions());
}
return new UpdateManyModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
model.getOptions());
}
if (writeModel instanceof DeleteOneModel<Document> model) {
return new DeleteOneModel<>(getMappedQuery(model.getFilter()), model.getOptions());
}
if (writeModel instanceof DeleteManyModel<Document> model) {
return new DeleteManyModel<>(getMappedQuery(model.getFilter()), model.getOptions());
}
return writeModel;
}
private List<Document> mapUpdatePipeline(AggregationUpdate source) {
Class<?> type = entity().isPresent() ? entity().map(PersistentEntity::getType).get() : Object.class;
AggregationOperationContext context = new RelaxedTypeBasedAggregationOperationContext(type,
updateMapper().getMappingContext(), queryMapper());
return new AggregationUtil(queryMapper(), queryMapper().getMappingContext()).createPipeline(source, context);
}
/**
* Emit a {@link ApplicationEvent} if event multicasting is enabled.
*
* @param event
*/
protected abstract void maybeEmitEvent(ApplicationEvent event);
/**
* @return the {@link UpdateMapper} to use.
*/
protected abstract UpdateMapper updateMapper();
/**
* @return the {@link QueryMapper} to use.
*/
protected abstract QueryMapper queryMapper();
/**
* @return the associated {@link PersistentEntity}. Can be {@link Optional#empty()}.
*/
protected abstract Optional<? extends MongoPersistentEntity<?>> entity();
protected Bson getMappedUpdate(Bson update) {
return updateMapper().getMappedObject(update, entity());
}
protected Bson getMappedQuery(Bson query) {
return queryMapper().getMappedObject(query, entity());
}
protected static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
BulkWriteOptions options = new BulkWriteOptions();
return switch (bulkMode) {
case ORDERED -> options.ordered(true);
case UNORDERED -> options.ordered(false);
};
}
/**
* @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}.
*/
protected 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;
}
/**
* Value object chaining together an actual source with its {@link WriteModel} representation.
*
* @author Christoph Strobl
*/
record SourceAwareWriteModelHolder(Object source, WriteModel<Document> model) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 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.
@@ -36,21 +36,29 @@ import com.mongodb.client.model.changestream.OperationType;
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Myroslav Kosinskyi
* @since 2.1
*/
public class ChangeStreamEvent<T> {
@SuppressWarnings("rawtypes") //
private static final AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> CONVERTED_UPDATER = AtomicReferenceFieldUpdater
.newUpdater(ChangeStreamEvent.class, Object.class, "converted");
private static final AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> CONVERTED_FULL_DOCUMENT_UPDATER = AtomicReferenceFieldUpdater
.newUpdater(ChangeStreamEvent.class, Object.class, "convertedFullDocument");
@SuppressWarnings("rawtypes") //
private static final AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> CONVERTED_FULL_DOCUMENT_BEFORE_CHANGE_UPDATER = AtomicReferenceFieldUpdater
.newUpdater(ChangeStreamEvent.class, Object.class, "convertedFullDocumentBeforeChange");
private final @Nullable ChangeStreamDocument<Document> raw;
private final Class<T> targetType;
private final MongoConverter converter;
// accessed through CONVERTED_UPDATER.
private volatile @Nullable T converted;
// accessed through CONVERTED_FULL_DOCUMENT_UPDATER.
private volatile @Nullable T convertedFullDocument;
// accessed through CONVERTED_FULL_DOCUMENT_BEFORE_CHANGE_UPDATER.
private volatile @Nullable T convertedFullDocumentBeforeChange;
/**
* @param raw can be {@literal null}.
@@ -147,27 +155,43 @@ public class ChangeStreamEvent<T> {
@Nullable
public T getBody() {
if (raw == null) {
if (raw == null || raw.getFullDocument() == null) {
return null;
}
Document fullDocument = raw.getFullDocument();
return getConvertedFullDocument(raw.getFullDocument());
}
if (fullDocument == null) {
return targetType.cast(fullDocument);
/**
* Get the potentially converted {@link ChangeStreamDocument#getFullDocumentBeforeChange() document} before being changed.
*
* @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocumentBeforeChange()} is
* {@literal null}.
* @since 4.0
*/
@Nullable
public T getBodyBeforeChange() {
if (raw == null || raw.getFullDocumentBeforeChange() == null) {
return null;
}
return getConverted(fullDocument);
return getConvertedFullDocumentBeforeChange(raw.getFullDocumentBeforeChange());
}
@SuppressWarnings("unchecked")
private T getConverted(Document fullDocument) {
return (T) doGetConverted(fullDocument);
private T getConvertedFullDocumentBeforeChange(Document fullDocument) {
return (T) doGetConverted(fullDocument, CONVERTED_FULL_DOCUMENT_BEFORE_CHANGE_UPDATER);
}
private Object doGetConverted(Document fullDocument) {
@SuppressWarnings("unchecked")
private T getConvertedFullDocument(Document fullDocument) {
return (T) doGetConverted(fullDocument, CONVERTED_FULL_DOCUMENT_UPDATER);
}
Object result = CONVERTED_UPDATER.get(this);
private Object doGetConverted(Document fullDocument, AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> updater) {
Object result = updater.get(this);
if (result != null) {
return result;
@@ -176,13 +200,13 @@ public class ChangeStreamEvent<T> {
if (ClassUtils.isAssignable(Document.class, fullDocument.getClass())) {
result = converter.read(targetType, fullDocument);
return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
return updater.compareAndSet(this, null, result) ? result : updater.get(this);
}
if (converter.getConversionService().canConvert(fullDocument.getClass(), targetType)) {
result = converter.getConversionService().convert(fullDocument, targetType);
return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
return updater.compareAndSet(this, null, result) ? result : updater.get(this);
}
throw new IllegalArgumentException(
@@ -195,7 +219,7 @@ public class ChangeStreamEvent<T> {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o)
return true;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.client.model.changestream.FullDocument;
import com.mongodb.client.model.changestream.FullDocumentBeforeChange;
/**
* Options applicable to MongoDB <a href="https://docs.mongodb.com/manual/changeStreams/">Change Streams</a>. Intended
@@ -40,6 +41,7 @@ import com.mongodb.client.model.changestream.FullDocument;
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Myroslav Kosinskyi
* @since 2.1
*/
public class ChangeStreamOptions {
@@ -47,6 +49,7 @@ public class ChangeStreamOptions {
private @Nullable Object filter;
private @Nullable BsonValue resumeToken;
private @Nullable FullDocument fullDocumentLookup;
private @Nullable FullDocumentBeforeChange fullDocumentBeforeChangeLookup;
private @Nullable Collation collation;
private @Nullable Object resumeTimestamp;
private Resume resume = Resume.UNDEFINED;
@@ -74,6 +77,14 @@ public class ChangeStreamOptions {
return Optional.ofNullable(fullDocumentLookup);
}
/**
* @return {@link Optional#empty()} if not set.
* @since 4.0
*/
public Optional<FullDocumentBeforeChange> getFullDocumentBeforeChangeLookup() {
return Optional.ofNullable(fullDocumentBeforeChangeLookup);
}
/**
* @return {@link Optional#empty()} if not set.
*/
@@ -139,12 +150,12 @@ public class ChangeStreamOptions {
return timestamp;
}
if (timestamp instanceof Instant) {
return new BsonTimestamp((int) ((Instant) timestamp).getEpochSecond(), 0);
if (timestamp instanceof Instant instant) {
return new BsonTimestamp((int) instant.getEpochSecond(), 0);
}
if (timestamp instanceof BsonTimestamp) {
return Instant.ofEpochSecond(((BsonTimestamp) timestamp).getTime());
if (timestamp instanceof BsonTimestamp bsonTimestamp) {
return Instant.ofEpochSecond(bsonTimestamp.getTime());
}
throw new IllegalArgumentException(
@@ -153,7 +164,7 @@ public class ChangeStreamOptions {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -170,6 +181,9 @@ public class ChangeStreamOptions {
if (!ObjectUtils.nullSafeEquals(this.fullDocumentLookup, that.fullDocumentLookup)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.fullDocumentBeforeChangeLookup, that.fullDocumentBeforeChangeLookup)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.collation, that.collation)) {
return false;
}
@@ -184,6 +198,7 @@ public class ChangeStreamOptions {
int result = ObjectUtils.nullSafeHashCode(filter);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeToken);
result = 31 * result + ObjectUtils.nullSafeHashCode(fullDocumentLookup);
result = 31 * result + ObjectUtils.nullSafeHashCode(fullDocumentBeforeChangeLookup);
result = 31 * result + ObjectUtils.nullSafeHashCode(collation);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeTimestamp);
result = 31 * result + ObjectUtils.nullSafeHashCode(resume);
@@ -220,6 +235,7 @@ public class ChangeStreamOptions {
private @Nullable Object filter;
private @Nullable BsonValue resumeToken;
private @Nullable FullDocument fullDocumentLookup;
private @Nullable FullDocumentBeforeChange fullDocumentBeforeChangeLookup;
private @Nullable Collation collation;
private @Nullable Object resumeTimestamp;
private Resume resume = Resume.UNDEFINED;
@@ -322,6 +338,32 @@ public class ChangeStreamOptions {
return this;
}
/**
* Set the {@link FullDocumentBeforeChange} lookup to use.
*
* @param lookup must not be {@literal null}.
* @return this.
* @since 4.0
*/
public ChangeStreamOptionsBuilder fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) {
Assert.notNull(lookup, "Lookup must not be null");
this.fullDocumentBeforeChangeLookup = lookup;
return this;
}
/**
* Return the full document before being changed if it is available.
*
* @return this.
* @since 4.0
* @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange)
*/
public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() {
return fullDocumentBeforeChangeLookup(FullDocumentBeforeChange.WHEN_AVAILABLE);
}
/**
* Set the cluster time to resume from.
*
@@ -391,6 +433,7 @@ public class ChangeStreamOptions {
options.filter = this.filter;
options.resumeToken = this.resumeToken;
options.fullDocumentLookup = this.fullDocumentLookup;
options.fullDocumentBeforeChangeLookup = this.fullDocumentBeforeChangeLookup;
options.collation = this.collation;
options.resumeTimestamp = this.resumeTimestamp;
options.resume = this.resume;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.ValidationAction;
import com.mongodb.client.model.ValidationLevel;
@@ -46,10 +47,11 @@ public class CollectionOptions {
private @Nullable Collation collation;
private ValidationOptions validationOptions;
private @Nullable TimeSeriesOptions timeSeriesOptions;
private @Nullable CollectionChangeStreamOptions changeStreamOptions;
private CollectionOptions(@Nullable Long size, @Nullable Long maxDocuments, @Nullable Boolean capped,
@Nullable Collation collation, ValidationOptions validationOptions,
@Nullable TimeSeriesOptions timeSeriesOptions) {
@Nullable Collation collation, ValidationOptions validationOptions, @Nullable TimeSeriesOptions timeSeriesOptions,
@Nullable CollectionChangeStreamOptions changeStreamOptions) {
this.maxDocuments = maxDocuments;
this.size = size;
@@ -57,6 +59,7 @@ public class CollectionOptions {
this.collation = collation;
this.validationOptions = validationOptions;
this.timeSeriesOptions = timeSeriesOptions;
this.changeStreamOptions = changeStreamOptions;
}
/**
@@ -70,7 +73,7 @@ public class CollectionOptions {
Assert.notNull(collation, "Collation must not be null");
return new CollectionOptions(null, null, null, collation, ValidationOptions.none(), null);
return new CollectionOptions(null, null, null, collation, ValidationOptions.none(), null, null);
}
/**
@@ -80,7 +83,7 @@ public class CollectionOptions {
* @since 2.0
*/
public static CollectionOptions empty() {
return new CollectionOptions(null, null, null, null, ValidationOptions.none(), null);
return new CollectionOptions(null, null, null, null, ValidationOptions.none(), null, null);
}
/**
@@ -97,15 +100,28 @@ public class CollectionOptions {
return empty().timeSeries(TimeSeriesOptions.timeSeries(timeField));
}
/**
* Quick way to set up {@link CollectionOptions} for emitting (pre & post) change events.
*
* @return new instance of {@link CollectionOptions}.
* @see #changeStream(CollectionChangeStreamOptions)
* @see CollectionChangeStreamOptions#preAndPostImages(boolean)
* @since 4.0
*/
public static CollectionOptions emitChangedRevisions() {
return empty().changeStream(CollectionChangeStreamOptions.preAndPostImages(true));
}
/**
* Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}. <br />
* <strong>NOTE</strong> Using capped collections requires defining {@link #size(long)}.
* <strong>NOTE:</strong> Using capped collections requires defining {@link #size(long)}.
*
* @return new {@link CollectionOptions}.
* @since 2.0
*/
public CollectionOptions capped() {
return new CollectionOptions(size, maxDocuments, true, collation, validationOptions, null);
return new CollectionOptions(size, maxDocuments, true, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -116,7 +132,8 @@ public class CollectionOptions {
* @since 2.0
*/
public CollectionOptions maxDocuments(long maxDocuments) {
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions);
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -127,7 +144,8 @@ public class CollectionOptions {
* @since 2.0
*/
public CollectionOptions size(long size) {
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions);
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -138,7 +156,8 @@ public class CollectionOptions {
* @since 2.0
*/
public CollectionOptions collation(@Nullable Collation collation) {
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions);
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -258,7 +277,8 @@ public class CollectionOptions {
public CollectionOptions validation(ValidationOptions validationOptions) {
Assert.notNull(validationOptions, "ValidationOptions must not be null");
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions);
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -271,7 +291,22 @@ public class CollectionOptions {
public CollectionOptions timeSeries(TimeSeriesOptions timeSeriesOptions) {
Assert.notNull(timeSeriesOptions, "TimeSeriesOptions must not be null");
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions);
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
* Create new {@link CollectionOptions} with the given {@link TimeSeriesOptions}.
*
* @param changeStreamOptions must not be {@literal null}.
* @return new instance of {@link CollectionOptions}.
* @since 3.3
*/
public CollectionOptions changeStream(CollectionChangeStreamOptions changeStreamOptions) {
Assert.notNull(changeStreamOptions, "ChangeStreamOptions must not be null");
return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions, timeSeriesOptions,
changeStreamOptions);
}
/**
@@ -332,6 +367,70 @@ public class CollectionOptions {
return Optional.ofNullable(timeSeriesOptions);
}
/**
* Get the {@link CollectionChangeStreamOptions} if available.
*
* @return {@link Optional#empty()} if not specified.
* @since 4.0
*/
public Optional<CollectionChangeStreamOptions> getChangeStreamOptions() {
return Optional.ofNullable(changeStreamOptions);
}
@Override
public String toString() {
return "CollectionOptions{" + "maxDocuments=" + maxDocuments + ", size=" + size + ", capped=" + capped
+ ", collation=" + collation + ", validationOptions=" + validationOptions + ", timeSeriesOptions="
+ timeSeriesOptions + ", changeStreamOptions=" + changeStreamOptions + ", disableValidation="
+ disableValidation() + ", strictValidation=" + strictValidation() + ", moderateValidation="
+ moderateValidation() + ", warnOnValidationError=" + warnOnValidationError() + ", failOnValidationError="
+ failOnValidationError() + '}';
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CollectionOptions that = (CollectionOptions) o;
if (!ObjectUtils.nullSafeEquals(maxDocuments, that.maxDocuments)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(size, that.size)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(capped, that.capped)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(collation, that.collation)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(validationOptions, that.validationOptions)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(timeSeriesOptions, that.timeSeriesOptions)) {
return false;
}
return ObjectUtils.nullSafeEquals(changeStreamOptions, that.changeStreamOptions);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(maxDocuments);
result = 31 * result + ObjectUtils.nullSafeHashCode(size);
result = 31 * result + ObjectUtils.nullSafeHashCode(capped);
result = 31 * result + ObjectUtils.nullSafeHashCode(collation);
result = 31 * result + ObjectUtils.nullSafeHashCode(validationOptions);
result = 31 * result + ObjectUtils.nullSafeHashCode(timeSeriesOptions);
result = 31 * result + ObjectUtils.nullSafeHashCode(changeStreamOptions);
return result;
}
/**
* Encapsulation of ValidationOptions options.
*
@@ -426,6 +525,92 @@ public class CollectionOptions {
boolean isEmpty() {
return !Optionals.isAnyPresent(getValidator(), getValidationAction(), getValidationLevel());
}
@Override
public String toString() {
return "ValidationOptions{" + "validator=" + validator + ", validationLevel=" + validationLevel
+ ", validationAction=" + validationAction + '}';
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ValidationOptions that = (ValidationOptions) o;
if (!ObjectUtils.nullSafeEquals(validator, that.validator)) {
return false;
}
if (validationLevel != that.validationLevel)
return false;
return validationAction == that.validationAction;
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(validator);
result = 31 * result + ObjectUtils.nullSafeHashCode(validationLevel);
result = 31 * result + ObjectUtils.nullSafeHashCode(validationAction);
return result;
}
}
/**
* Encapsulation of options applied to define collections change stream behaviour.
*
* @author Christoph Strobl
* @since 4.0
*/
public static class CollectionChangeStreamOptions {
private final boolean preAndPostImages;
private CollectionChangeStreamOptions(boolean emitChangedRevisions) {
this.preAndPostImages = emitChangedRevisions;
}
/**
* Output the version of a document before and after changes (the document pre- and post-images).
*
* @return new instance of {@link CollectionChangeStreamOptions}.
*/
public static CollectionChangeStreamOptions preAndPostImages(boolean emitChangedRevisions) {
return new CollectionChangeStreamOptions(true);
}
public boolean getPreAndPostImages() {
return preAndPostImages;
}
@Override
public String toString() {
return "CollectionChangeStreamOptions{" + "preAndPostImages=" + preAndPostImages + '}';
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CollectionChangeStreamOptions that = (CollectionChangeStreamOptions) o;
return preAndPostImages == that.preAndPostImages;
}
@Override
public int hashCode() {
return (preAndPostImages ? 1 : 0);
}
}
/**
@@ -511,5 +696,40 @@ public class CollectionOptions {
public GranularityDefinition getGranularity() {
return granularity;
}
@Override
public String toString() {
return "TimeSeriesOptions{" + "timeField='" + timeField + '\'' + ", metaField='" + metaField + '\''
+ ", granularity=" + granularity + '}';
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeSeriesOptions that = (TimeSeriesOptions) o;
if (!ObjectUtils.nullSafeEquals(timeField, that.timeField)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(metaField, that.metaField)) {
return false;
}
return ObjectUtils.nullSafeEquals(granularity, that.granularity);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(timeField);
result = 31 * result + ObjectUtils.nullSafeHashCode(metaField);
result = 31 * result + ObjectUtils.nullSafeHashCode(granularity);
return result;
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2023 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.util.Assert;
import com.mongodb.client.MongoCollection;
/**
* Interface for functional preparation of a {@link MongoCollection}.
*
* @author Mark Paluch
* @since 4.1
*/
public interface CollectionPreparer<T> {
/**
* Returns a preparer that always returns its input collection.
*
* @return a preparer that always returns its input collection.
*/
static <T> CollectionPreparer<T> identity() {
return it -> it;
}
/**
* Prepare the {@code collection}.
*
* @param collection the collection to prepare.
* @return the prepared collection.
*/
T prepare(T collection);
/**
* Returns a composed {@code CollectionPreparer} that first applies this preparer to the collection, and then applies
* the {@code after} preparer to the result. If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param after the collection preparer to apply after this function is applied.
* @return a composed {@code CollectionPreparer} that first applies this preparer and then applies the {@code after}
* preparer.
*/
default CollectionPreparer<T> andThen(CollectionPreparer<T> after) {
Assert.notNull(after, "After CollectionPreparer must not be null");
return c -> after.prepare(prepare(c));
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2023 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.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.bson.Document;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.client.MongoCollection;
/**
* Support class for delegate implementations to apply {@link ReadConcern} and {@link ReadPreference} settings upon
* {@link CollectionPreparer preparing a collection}.
*
* @author Mark Paluch
* @since 4.1
*/
class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware {
private final List<Object> sources;
private CollectionPreparerSupport(List<Object> sources) {
this.sources = sources;
}
<T> T doPrepare(T collection, Function<T, ReadConcern> concernAccessor, BiFunction<T, ReadConcern, T> concernFunction,
Function<T, ReadPreference> preferenceAccessor, BiFunction<T, ReadPreference, T> preferenceFunction) {
T collectionToUse = collection;
for (Object source : sources) {
if (source instanceof ReadConcernAware rca && rca.hasReadConcern()) {
ReadConcern concern = rca.getReadConcern();
if (concernAccessor.apply(collectionToUse) != concern) {
collectionToUse = concernFunction.apply(collectionToUse, concern);
}
break;
}
}
for (Object source : sources) {
if (source instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
ReadPreference preference = rpa.getReadPreference();
if (preferenceAccessor.apply(collectionToUse) != preference) {
collectionToUse = preferenceFunction.apply(collectionToUse, preference);
}
break;
}
}
return collectionToUse;
}
@Override
public boolean hasReadConcern() {
for (Object aware : sources) {
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
return true;
}
}
return false;
}
@Override
public ReadConcern getReadConcern() {
for (Object aware : sources) {
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
return rca.getReadConcern();
}
}
return null;
}
@Override
public boolean hasReadPreference() {
for (Object aware : sources) {
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
return true;
}
}
return false;
}
@Override
public ReadPreference getReadPreference() {
for (Object aware : sources) {
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
return rpa.getReadPreference();
}
}
return null;
}
static class CollectionPreparerDelegate extends CollectionPreparerSupport
implements CollectionPreparer<MongoCollection<Document>> {
private CollectionPreparerDelegate(List<Object> sources) {
super(sources);
}
public static CollectionPreparerDelegate of(ReadPreferenceAware... awares) {
return of((Object[]) awares);
}
public static CollectionPreparerDelegate of(Object... mixedAwares) {
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
return (CollectionPreparerDelegate) mixedAwares[0];
}
return new CollectionPreparerDelegate(Arrays.asList(mixedAwares));
}
@Override
public MongoCollection<Document> prepare(MongoCollection<Document> collection) {
return doPrepare(collection, MongoCollection::getReadConcern, MongoCollection::withReadConcern,
MongoCollection::getReadPreference, MongoCollection::withReadPreference);
}
}
static class ReactiveCollectionPreparerDelegate extends CollectionPreparerSupport
implements CollectionPreparer<com.mongodb.reactivestreams.client.MongoCollection<Document>> {
private ReactiveCollectionPreparerDelegate(List<Object> sources) {
super(sources);
}
public static ReactiveCollectionPreparerDelegate of(ReadPreferenceAware... awares) {
return of((Object[]) awares);
}
public static ReactiveCollectionPreparerDelegate of(Object... mixedAwares) {
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
return (ReactiveCollectionPreparerDelegate) mixedAwares[0];
}
return new ReactiveCollectionPreparerDelegate(Arrays.asList(mixedAwares));
}
@Override
public com.mongodb.reactivestreams.client.MongoCollection<Document> prepare(
com.mongodb.reactivestreams.client.MongoCollection<Document> collection) {
return doPrepare(collection, //
com.mongodb.reactivestreams.client.MongoCollection::getReadConcern,
com.mongodb.reactivestreams.client.MongoCollection::withReadConcern,
com.mongodb.reactivestreams.client.MongoCollection::getReadPreference,
com.mongodb.reactivestreams.client.MongoCollection::withReadPreference);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@ import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.MetricConversion;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -38,7 +38,7 @@ import org.springframework.util.ObjectUtils;
*/
class CountQuery {
private Document source;
private final Document source;
private CountQuery(Document source) {
this.source = source;
@@ -64,18 +64,15 @@ class CountQuery {
for (Map.Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Document && requiresRewrite(entry.getValue())) {
if (entry.getValue() instanceof Document document && requiresRewrite(entry.getValue())) {
Document theValue = (Document) entry.getValue();
target.putAll(createGeoWithin(entry.getKey(), theValue, source.get("$and")));
target.putAll(createGeoWithin(entry.getKey(), document, source.get("$and")));
continue;
}
if (entry.getValue() instanceof Collection && requiresRewrite(entry.getValue())) {
if (entry.getValue() instanceof Collection<?> collection && requiresRewrite(entry.getValue())) {
Collection<?> source = (Collection<?>) entry.getValue();
target.put(entry.getKey(), rewriteCollection(source));
target.put(entry.getKey(), rewriteCollection(collection));
continue;
}
@@ -96,12 +93,12 @@ class CountQuery {
*/
private boolean requiresRewrite(Object valueToInspect) {
if (valueToInspect instanceof Document) {
return requiresRewrite((Document) valueToInspect);
if (valueToInspect instanceof Document document) {
return requiresRewrite(document);
}
if (valueToInspect instanceof Collection) {
return requiresRewrite((Collection) valueToInspect);
if (valueToInspect instanceof Collection<?> collection) {
return requiresRewrite(collection);
}
return false;
@@ -110,7 +107,7 @@ class CountQuery {
private boolean requiresRewrite(Collection<?> collection) {
for (Object o : collection) {
if (o instanceof Document && requiresRewrite((Document) o)) {
if (o instanceof Document document && requiresRewrite(document)) {
return true;
}
}
@@ -139,8 +136,8 @@ class CountQuery {
Collection<Object> rewrittenCollection = new ArrayList<>(source.size());
for (Object item : source) {
if (item instanceof Document && requiresRewrite(item)) {
rewrittenCollection.add(CountQuery.of((Document) item).toQueryDocument());
if (item instanceof Document document && requiresRewrite(item)) {
rewrittenCollection.add(CountQuery.of(document).toQueryDocument());
} else {
rewrittenCollection.add(item);
}
@@ -157,12 +154,14 @@ class CountQuery {
* @param $and potentially existing {@code $and} condition.
* @return the rewritten query {@link Document}.
*/
@SuppressWarnings("unchecked")
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;
Number maxDistance = getMaxDistance(source, $near, spheric);
List<Object> $centerMax = Arrays.asList(toCenterCoordinates($near), maxDistance);
Document $geoWithinMax = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMax));
@@ -176,23 +175,51 @@ class CountQuery {
Document $geoWithinMin = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMin));
List<Document> criteria = new ArrayList<>();
List<Document> criteria;
if ($and != null) {
if ($and instanceof Collection) {
criteria.addAll((Collection) $and);
Collection<Document> andElements = (Collection<Document>) $and;
criteria = new ArrayList<>(andElements.size() + 2);
criteria.addAll(andElements);
} else {
throw new IllegalArgumentException(
"Cannot rewrite query as it contains an '$and' element that is not a Collection: Offending element: "
+ $and);
}
} else {
criteria = new ArrayList<>(2);
}
criteria.add(new Document("$nor", Collections.singletonList(new Document(key, $geoWithinMin))));
criteria.add(new Document(key, $geoWithinMax));
return new Document("$and", criteria);
}
private static Number getMaxDistance(Document source, Object $near, boolean spheric) {
Number maxDistance = Double.MAX_VALUE;
if (source.containsKey("$maxDistance")) { // legacy coordinate pair
return (Number) source.get("$maxDistance");
}
if ($near instanceof Document nearDoc) {
if (nearDoc.containsKey("$maxDistance")) {
maxDistance = (Number) nearDoc.get("$maxDistance");
// geojson is in Meters but we need radians x/(6378.1*1000)
if (spheric && nearDoc.containsKey("$geometry")) {
maxDistance = MetricConversion.metersToRadians(maxDistance.doubleValue());
}
}
}
return maxDistance;
}
private static boolean containsNear(Document source) {
return source.containsKey("$near") || source.containsKey("$nearSphere");
}
@@ -212,14 +239,20 @@ class CountQuery {
return value;
}
if (value instanceof Point) {
return Arrays.asList(((Point) value).getX(), ((Point) value).getY());
if (value instanceof Point point) {
return Arrays.asList(point.getX(), point.getY());
}
if (value instanceof Document && ((Document) value).containsKey("x")) {
if (value instanceof Document document) {
Document point = (Document) value;
return Arrays.asList(point.get("x"), point.get("y"));
if (document.containsKey("x")) {
return Arrays.asList(document.get("x"), document.get("y"));
}
if (document.containsKey("$geometry")) {
Document geoJsonPoint = document.get("$geometry", Document.class);
return geoJsonPoint.get("coordinates");
}
}
return value;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,42 +16,47 @@
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
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 org.springframework.util.ObjectUtils;
import com.mongodb.MongoBulkWriteException;
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.DeleteOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.ReplaceOptions;
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}.
@@ -67,7 +72,7 @@ import com.mongodb.client.model.*;
* @author Jacob Botuck
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperations {
private final MongoOperations mongoOperations;
private final String collectionName;
@@ -75,7 +80,6 @@ class DefaultBulkOperations implements BulkOperations {
private final List<SourceAwareWriteModelHolder> models = new ArrayList<>();
private @Nullable WriteConcern defaultWriteConcern;
private BulkWriteOptions bulkOptions;
/**
@@ -90,6 +94,7 @@ class DefaultBulkOperations implements BulkOperations {
DefaultBulkOperations(MongoOperations mongoOperations, String collectionName,
BulkOperationContext bulkOperationContext) {
super(collectionName);
Assert.notNull(mongoOperations, "MongoOperations must not be null");
Assert.hasText(collectionName, "CollectionName must not be null nor empty");
Assert.notNull(bulkOperationContext, "BulkOperationContext must not be null");
@@ -97,7 +102,7 @@ class DefaultBulkOperations implements BulkOperations {
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.bulkOperationContext = bulkOperationContext;
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.bulkMode());
}
/**
@@ -132,21 +137,20 @@ class DefaultBulkOperations implements BulkOperations {
}
@Override
@SuppressWarnings("unchecked")
public BulkOperations updateOne(Query query, Update update) {
public BulkOperations updateOne(Query query, UpdateDefinition update) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
return updateOne(Collections.singletonList(Pair.of(query, update)));
return update(query, update, false, false);
}
@Override
public BulkOperations updateOne(List<Pair<Query, Update>> updates) {
public BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) {
Assert.notNull(updates, "Updates must not be null");
for (Pair<Query, Update> update : updates) {
for (Pair<Query, UpdateDefinition> update : updates) {
update(update.getFirst(), update.getSecond(), false, false);
}
@@ -154,21 +158,22 @@ class DefaultBulkOperations implements BulkOperations {
}
@Override
@SuppressWarnings("unchecked")
public BulkOperations updateMulti(Query query, Update update) {
public BulkOperations updateMulti(Query query, UpdateDefinition update) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
return updateMulti(Collections.singletonList(Pair.of(query, update)));
update(query, update, false, true);
return this;
}
@Override
public BulkOperations updateMulti(List<Pair<Query, Update>> updates) {
public BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) {
Assert.notNull(updates, "Updates must not be null");
for (Pair<Query, Update> update : updates) {
for (Pair<Query, UpdateDefinition> update : updates) {
update(update.getFirst(), update.getSecond(), false, true);
}
@@ -176,7 +181,7 @@ class DefaultBulkOperations implements BulkOperations {
}
@Override
public BulkOperations upsert(Query query, Update update) {
public BulkOperations upsert(Query query, UpdateDefinition update) {
return update(query, update, true, true);
}
@@ -248,7 +253,7 @@ class DefaultBulkOperations implements BulkOperations {
return result;
} finally {
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.bulkMode());
}
}
@@ -267,9 +272,8 @@ class DefaultBulkOperations implements BulkOperations {
bulkOptions);
} catch (RuntimeException ex) {
if (ex instanceof MongoBulkWriteException) {
if (ex instanceof MongoBulkWriteException mongoBulkWriteException) {
MongoBulkWriteException mongoBulkWriteException = (MongoBulkWriteException) ex;
if (mongoBulkWriteException.getWriteConcernError() != null) {
throw new DataIntegrityViolationException(ex.getMessage(), ex);
}
@@ -284,17 +288,17 @@ class DefaultBulkOperations implements BulkOperations {
maybeEmitBeforeSaveEvent(it);
if (it.getModel() instanceof InsertOneModel) {
if (it.model() instanceof InsertOneModel<Document> model) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeInvokeBeforeSaveCallback(it.getSource(), target);
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = model.getDocument();
maybeInvokeBeforeSaveCallback(it.source(), target);
} else if (it.model() instanceof ReplaceOneModel<Document> model) {
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeInvokeBeforeSaveCallback(it.getSource(), target);
Document target = model.getReplacement();
maybeInvokeBeforeSaveCallback(it.source(), target);
}
return mapWriteModel(it.getModel());
return mapWriteModel(it.source(), it.model());
}
/**
@@ -306,7 +310,7 @@ class DefaultBulkOperations implements BulkOperations {
* @param multi whether to issue a multi-update.
* @return the {@link BulkOperations} with the update registered.
*/
private BulkOperations update(Query query, Update update, boolean upsert, boolean multi) {
private BulkOperations update(Query query, UpdateDefinition update, boolean upsert, boolean multi) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
@@ -322,53 +326,30 @@ class DefaultBulkOperations implements BulkOperations {
return this;
}
private WriteModel<Document> mapWriteModel(WriteModel<Document> writeModel) {
if (writeModel instanceof UpdateOneModel) {
UpdateOneModel<Document> model = (UpdateOneModel<Document>) writeModel;
return new UpdateOneModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
model.getOptions());
}
if (writeModel instanceof UpdateManyModel) {
UpdateManyModel<Document> model = (UpdateManyModel<Document>) writeModel;
return new UpdateManyModel<>(getMappedQuery(model.getFilter()), getMappedUpdate(model.getUpdate()),
model.getOptions());
}
if (writeModel instanceof DeleteOneModel) {
DeleteOneModel<Document> model = (DeleteOneModel<Document>) writeModel;
return new DeleteOneModel<>(getMappedQuery(model.getFilter()), model.getOptions());
}
if (writeModel instanceof DeleteManyModel) {
DeleteManyModel<Document> model = (DeleteManyModel<Document>) writeModel;
return new DeleteManyModel<>(getMappedQuery(model.getFilter()), model.getOptions());
}
return writeModel;
@Override
protected void maybeEmitEvent(ApplicationEvent event) {
bulkOperationContext.publishEvent(event);
}
private Bson getMappedUpdate(Bson update) {
return bulkOperationContext.getUpdateMapper().getMappedObject(update, bulkOperationContext.getEntity());
@Override
protected UpdateMapper updateMapper() {
return bulkOperationContext.updateMapper();
}
private Bson getMappedQuery(Bson query) {
return bulkOperationContext.getQueryMapper().getMappedObject(query, bulkOperationContext.getEntity());
@Override
protected QueryMapper queryMapper() {
return bulkOperationContext.queryMapper();
}
@Override
protected Optional<? extends MongoPersistentEntity<?>> entity() {
return bulkOperationContext.entity();
}
private Document getMappedObject(Object source) {
if (source instanceof Document) {
return (Document) source;
if (source instanceof Document document) {
return document;
}
Document sink = new Document();
@@ -381,268 +362,83 @@ class DefaultBulkOperations implements BulkOperations {
models.add(new SourceAwareWriteModelHolder(source, model));
}
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder holder) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
}
}
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder holder) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
}
}
private void maybeInvokeAfterSaveCallback(SourceAwareWriteModelHolder holder) {
if (holder.getModel() instanceof InsertOneModel) {
if (holder.model() instanceof InsertOneModel<Document> model) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = model.getDocument();
maybeInvokeAfterSaveCallback(holder.source(), target);
} else if (holder.model() instanceof ReplaceOneModel<Document> model) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
Document target = model.getReplacement();
maybeInvokeAfterSaveCallback(holder.source(), target);
}
}
private <E extends MongoMappingEvent<T>, T> E maybeEmitEvent(E event) {
if (bulkOperationContext.getEventPublisher() == null) {
return event;
}
bulkOperationContext.getEventPublisher().publishEvent(event);
return event;
private void publishEvent(MongoMappingEvent<?> event) {
bulkOperationContext.publishEvent(event);
}
private Object maybeInvokeBeforeConvertCallback(Object value) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(BeforeConvertCallback.class, value, collectionName);
return bulkOperationContext.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);
return bulkOperationContext.callback(BeforeSaveCallback.class, value, mappedDocument, collectionName);
}
private Object maybeInvokeAfterSaveCallback(Object value, Document mappedDocument) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(AfterSaveCallback.class, value, mappedDocument,
collectionName);
}
private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
BulkWriteOptions options = new BulkWriteOptions();
switch (bulkMode) {
case ORDERED:
return options.ordered(true);
case UNORDERED:
return options.ordered(false);
}
throw new IllegalStateException("BulkMode was null");
return bulkOperationContext.callback(AfterSaveCallback.class, value, mappedDocument, collectionName);
}
/**
* @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
* {@link BulkOperationContext} holds information about {@link BulkMode} the entity in use as well as references to
* {@link QueryMapper} and {@link UpdateMapper}.
*
* @author Christoph Strobl
* @since 2.0
*/
static final class BulkOperationContext {
record BulkOperationContext(BulkMode bulkMode, Optional<? extends MongoPersistentEntity<?>> entity,
QueryMapper queryMapper, UpdateMapper updateMapper, @Nullable ApplicationEventPublisher eventPublisher,
@Nullable EntityCallbacks entityCallbacks) {
private final BulkMode bulkMode;
private final Optional<? extends MongoPersistentEntity<?>> entity;
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final ApplicationEventPublisher eventPublisher;
private final EntityCallbacks entityCallbacks;
BulkOperationContext(BulkOperations.BulkMode bulkMode, Optional<? extends MongoPersistentEntity<?>> entity,
QueryMapper queryMapper, UpdateMapper updateMapper, ApplicationEventPublisher eventPublisher,
EntityCallbacks entityCallbacks) {
this.bulkMode = bulkMode;
this.entity = entity;
this.queryMapper = queryMapper;
this.updateMapper = updateMapper;
this.eventPublisher = eventPublisher;
this.entityCallbacks = entityCallbacks;
public boolean skipEntityCallbacks() {
return entityCallbacks == null;
}
public BulkMode getBulkMode() {
return this.bulkMode;
public boolean skipEventPublishing() {
return eventPublisher == null;
}
public Optional<? extends MongoPersistentEntity<?>> getEntity() {
return this.entity;
}
@SuppressWarnings("rawtypes")
public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) {
public QueryMapper getQueryMapper() {
return this.queryMapper;
}
public UpdateMapper getUpdateMapper() {
return this.updateMapper;
}
public ApplicationEventPublisher getEventPublisher() {
return this.eventPublisher;
}
public EntityCallbacks getEntityCallbacks() {
return this.entityCallbacks;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BulkOperationContext that = (BulkOperationContext) o;
if (bulkMode != that.bulkMode)
return false;
if (!ObjectUtils.nullSafeEquals(this.entity, that.entity)) {
return false;
if (skipEntityCallbacks()) {
return entity;
}
if (!ObjectUtils.nullSafeEquals(this.queryMapper, that.queryMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.updateMapper, that.updateMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.eventPublisher, that.eventPublisher)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.entityCallbacks, that.entityCallbacks);
return entityCallbacks.callback(callbackType, entity, collectionName);
}
@Override
public int hashCode() {
int result = bulkMode != null ? bulkMode.hashCode() : 0;
result = 31 * result + ObjectUtils.nullSafeHashCode(entity);
result = 31 * result + ObjectUtils.nullSafeHashCode(queryMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(updateMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(eventPublisher);
result = 31 * result + ObjectUtils.nullSafeHashCode(entityCallbacks);
return result;
@SuppressWarnings("rawtypes")
public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Document document,
String collectionName) {
if (skipEntityCallbacks()) {
return entity;
}
return entityCallbacks.callback(callbackType, entity, document, collectionName);
}
public String toString() {
return "DefaultBulkOperations.BulkOperationContext(bulkMode=" + this.getBulkMode() + ", entity="
+ this.getEntity() + ", queryMapper=" + this.getQueryMapper() + ", updateMapper=" + this.getUpdateMapper()
+ ", eventPublisher=" + this.getEventPublisher() + ", entityCallbacks=" + this.getEntityCallbacks() + ")";
public void publishEvent(ApplicationEvent event) {
if (skipEventPublishing()) {
return;
}
eventPublisher.publishEvent(event);
}
}
/**
* Value object chaining together an actual source with its {@link WriteModel} representation.
*
* @since 2.2
* @author Christoph Strobl
*/
private static final class SourceAwareWriteModelHolder {
private final Object source;
private final WriteModel<Document> model;
SourceAwareWriteModelHolder(Object source, WriteModel<Document> model) {
this.source = source;
this.model = model;
}
public Object getSource() {
return this.source;
}
public WriteModel<Document> getModel() {
return this.model;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SourceAwareWriteModelHolder that = (SourceAwareWriteModelHolder) o;
if (!ObjectUtils.nullSafeEquals(this.source, that.source)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.model, that.model);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(model);
result = 31 * result + ObjectUtils.nullSafeHashCode(source);
return result;
}
public String toString() {
return "DefaultBulkOperations.SourceAwareWriteModelHolder(source=" + this.getSource() + ", model="
+ this.getModel() + ")";
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.List;
import org.bson.Document;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
@@ -29,6 +30,7 @@ import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCollection;
@@ -155,6 +157,20 @@ public class DefaultIndexOperations implements IndexOperations {
}
@Override
public void alterIndex(String name, org.springframework.data.mongodb.core.index.IndexOptions options) {
Document indexOptions = new Document("name", name);
indexOptions.putAll(options.toDocument());
Document result = mongoOperations
.execute(db -> db.runCommand(new Document("collMod", collectionName).append("index", indexOptions)));
if(NumberUtils.convertNumberToTargetClass(result.get("ok", (Number) 0), Integer.class) != 1) {
throw new UncategorizedMongoDbException("Index '%s' could not be modified. Response was %s".formatted(name, result.toJson()), null);
}
}
public void dropAllIndexes() {
dropIndex("*");
}
@@ -172,7 +188,8 @@ public class DefaultIndexOperations implements IndexOperations {
private List<IndexInfo> getIndexData(MongoCursor<Document> cursor) {
List<IndexInfo> indexInfoList = new ArrayList<>();
int available = cursor.available();
List<IndexInfo> indexInfoList = available > 0 ? new ArrayList<>(available) : new ArrayList<>();
while (cursor.hasNext()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 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.

View File

@@ -0,0 +1,390 @@
/*
* Copyright 2023 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 reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
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.BeforeConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback;
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.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.DeleteManyModel;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateManyModel;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.reactivestreams.client.MongoCollection;
/**
* Default implementation for {@link ReactiveBulkOperations}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 4.1
*/
class DefaultReactiveBulkOperations extends BulkOperationsSupport implements ReactiveBulkOperations {
private final ReactiveMongoOperations mongoOperations;
private final String collectionName;
private final ReactiveBulkOperationContext bulkOperationContext;
private final List<Mono<SourceAwareWriteModelHolder>> models = new ArrayList<>();
private @Nullable WriteConcern defaultWriteConcern;
private BulkWriteOptions bulkOptions;
/**
* Creates a new {@link DefaultReactiveBulkOperations} for the given {@link MongoOperations}, collection name and
* {@link ReactiveBulkOperationContext}.
*
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
* @param bulkOperationContext must not be {@literal null}.
*/
DefaultReactiveBulkOperations(ReactiveMongoOperations mongoOperations, String collectionName,
ReactiveBulkOperationContext bulkOperationContext) {
super(collectionName);
Assert.notNull(mongoOperations, "MongoOperations must not be null");
Assert.hasText(collectionName, "CollectionName must not be null nor empty");
Assert.notNull(bulkOperationContext, "BulkOperationContext must not be null");
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.bulkOperationContext = bulkOperationContext;
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.bulkMode());
}
/**
* Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}.
*
* @param defaultWriteConcern can be {@literal null}.
*/
void setDefaultWriteConcern(@Nullable WriteConcern defaultWriteConcern) {
this.defaultWriteConcern = defaultWriteConcern;
}
@Override
public ReactiveBulkOperations insert(Object document) {
Assert.notNull(document, "Document must not be null");
this.models.add(Mono.just(document).flatMap(it -> {
maybeEmitEvent(new BeforeConvertEvent<>(it, collectionName));
return maybeInvokeBeforeConvertCallback(it);
}).map(it -> new SourceAwareWriteModelHolder(it, new InsertOneModel<>(getMappedObject(it)))));
return this;
}
@Override
public ReactiveBulkOperations insert(List<? extends Object> documents) {
Assert.notNull(documents, "Documents must not be null");
documents.forEach(this::insert);
return this;
}
@Override
public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
update(query, update, false, false);
return this;
}
@Override
public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
update(query, update, false, true);
return this;
}
@Override
public ReactiveBulkOperations upsert(Query query, UpdateDefinition update) {
return update(query, update, true, true);
}
@Override
public ReactiveBulkOperations remove(Query query) {
Assert.notNull(query, "Query must not be null");
DeleteOptions deleteOptions = new DeleteOptions();
query.getCollation().map(Collation::toMongoCollation).ifPresent(deleteOptions::collation);
this.models.add(Mono.just(query)
.map(it -> new SourceAwareWriteModelHolder(it, new DeleteManyModel<>(it.getQueryObject(), deleteOptions))));
return this;
}
@Override
public ReactiveBulkOperations remove(List<Query> removes) {
Assert.notNull(removes, "Removals must not be null");
for (Query query : removes) {
remove(query);
}
return this;
}
@Override
public ReactiveBulkOperations 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);
this.models.add(Mono.just(replacement).flatMap(it -> {
maybeEmitEvent(new BeforeConvertEvent<>(it, collectionName));
return maybeInvokeBeforeConvertCallback(it);
}).map(it -> new SourceAwareWriteModelHolder(it,
new ReplaceOneModel<>(getMappedQuery(query.getQueryObject()), getMappedObject(it), replaceOptions))));
return this;
}
@Override
public Mono<BulkWriteResult> execute() {
try {
return mongoOperations.execute(collectionName, this::bulkWriteTo).next();
} finally {
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.bulkMode());
}
}
private Mono<BulkWriteResult> bulkWriteTo(MongoCollection<Document> collection) {
if (defaultWriteConcern != null) {
collection = collection.withWriteConcern(defaultWriteConcern);
}
Flux<SourceAwareWriteModelHolder> concat = Flux.concat(models).flatMap(it -> {
if (it.model()instanceof InsertOneModel<Document> iom) {
Document target = iom.getDocument();
maybeEmitBeforeSaveEvent(it);
return maybeInvokeBeforeSaveCallback(it.source(), target)
.map(afterCallback -> new SourceAwareWriteModelHolder(afterCallback, mapWriteModel(afterCallback, iom)));
} else if (it.model()instanceof ReplaceOneModel<Document> rom) {
Document target = rom.getReplacement();
maybeEmitBeforeSaveEvent(it);
return maybeInvokeBeforeSaveCallback(it.source(), target)
.map(afterCallback -> new SourceAwareWriteModelHolder(afterCallback, mapWriteModel(afterCallback, rom)));
}
return Mono.just(new SourceAwareWriteModelHolder(it.source(), mapWriteModel(it.source(), it.model())));
});
MongoCollection<Document> theCollection = collection;
return concat.collectList().flatMap(it -> {
return Mono
.from(theCollection
.bulkWrite(it.stream().map(SourceAwareWriteModelHolder::model).collect(Collectors.toList()), bulkOptions))
.doOnSuccess(state -> {
it.forEach(this::maybeEmitAfterSaveEvent);
}).flatMap(state -> {
List<Mono<Object>> monos = it.stream().map(this::maybeInvokeAfterSaveCallback).collect(Collectors.toList());
return Flux.concat(monos).then(Mono.just(state));
});
});
}
/**
* Performs update and upsert bulk operations.
*
* @param query the {@link Query} to determine documents to update.
* @param update the {@link Update} to perform, must not be {@literal null}.
* @param upsert whether to upsert.
* @param multi whether to issue a multi-update.
* @return the {@link BulkOperations} with the update registered.
*/
private ReactiveBulkOperations update(Query query, UpdateDefinition update, boolean upsert, boolean multi) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
UpdateOptions options = computeUpdateOptions(query, update, upsert);
this.models.add(Mono.just(update).map(it -> {
if (multi) {
return new SourceAwareWriteModelHolder(update,
new UpdateManyModel<>(query.getQueryObject(), it.getUpdateObject(), options));
}
return new SourceAwareWriteModelHolder(update,
new UpdateOneModel<>(query.getQueryObject(), it.getUpdateObject(), options));
}));
return this;
}
@Override
protected void maybeEmitEvent(ApplicationEvent event) {
bulkOperationContext.publishEvent(event);
}
@Override
protected UpdateMapper updateMapper() {
return bulkOperationContext.updateMapper();
}
@Override
protected QueryMapper queryMapper() {
return bulkOperationContext.queryMapper();
}
@Override
protected Optional<? extends MongoPersistentEntity<?>> entity() {
return bulkOperationContext.entity();
}
private Document getMappedObject(Object source) {
if (source instanceof Document) {
return (Document) source;
}
Document sink = new Document();
mongoOperations.getConverter().write(source, sink);
return sink;
}
private Mono<Object> maybeInvokeAfterSaveCallback(SourceAwareWriteModelHolder holder) {
if (holder.model() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.model()).getDocument();
return maybeInvokeAfterSaveCallback(holder.source(), target);
} else if (holder.model() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.model()).getReplacement();
return maybeInvokeAfterSaveCallback(holder.source(), target);
}
return Mono.just(holder.source());
}
private Mono<Object> maybeInvokeBeforeConvertCallback(Object value) {
return bulkOperationContext.callback(ReactiveBeforeConvertCallback.class, value, collectionName);
}
private Mono<Object> maybeInvokeBeforeSaveCallback(Object value, Document mappedDocument) {
return bulkOperationContext.callback(ReactiveBeforeSaveCallback.class, value, mappedDocument, collectionName);
}
private Mono<Object> maybeInvokeAfterSaveCallback(Object value, Document mappedDocument) {
return bulkOperationContext.callback(ReactiveAfterSaveCallback.class, value, mappedDocument, collectionName);
}
/**
* {@link ReactiveBulkOperationContext} holds information about {@link BulkMode} the entity in use as well as
* references to {@link QueryMapper} and {@link UpdateMapper}.
*
* @author Christoph Strobl
* @since 2.0
*/
record ReactiveBulkOperationContext(BulkMode bulkMode, Optional<? extends MongoPersistentEntity<?>> entity,
QueryMapper queryMapper, UpdateMapper updateMapper, @Nullable ApplicationEventPublisher eventPublisher,
@Nullable ReactiveEntityCallbacks entityCallbacks) {
public boolean skipEntityCallbacks() {
return entityCallbacks == null;
}
public boolean skipEventPublishing() {
return eventPublisher == null;
}
@SuppressWarnings("rawtypes")
public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) {
if (skipEntityCallbacks()) {
return Mono.just(entity);
}
return entityCallbacks.callback(callbackType, entity, collectionName);
}
@SuppressWarnings("rawtypes")
public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, Document document,
String collectionName) {
if (skipEntityCallbacks()) {
return Mono.just(entity);
}
return entityCallbacks.callback(callbackType, entity, document, collectionName);
}
public void publishEvent(ApplicationEvent event) {
if (skipEventPublishing()) {
return;
}
eventPublisher.publishEvent(event);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
@@ -29,6 +30,7 @@ import org.springframework.data.mongodb.core.index.ReactiveIndexOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import com.mongodb.client.model.IndexOptions;
@@ -104,6 +106,22 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
}).next();
}
@Override
public Mono<Void> alterIndex(String name, org.springframework.data.mongodb.core.index.IndexOptions options) {
return mongoOperations.execute(db -> {
Document indexOptions = new Document("name", name);
indexOptions.putAll(options.toDocument());
return Flux.from(db.runCommand(new Document("collMod", collectionName).append("index", indexOptions)))
.doOnNext(result -> {
if(NumberUtils.convertNumberToTargetClass(result.get("ok", (Number) 0), Integer.class) != 1) {
throw new UncategorizedMongoDbException("Index '%s' could not be modified. Response was %s".formatted(name, result.toJson()), null);
}
});
}).then();
}
@Nullable
private MongoPersistentEntity<?> lookupPersistentEntity(String collection) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2022 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@ import org.bson.types.ObjectId;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
import org.springframework.data.mongodb.core.script.NamedMongoScript;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
@@ -151,7 +150,7 @@ class DefaultScriptOperations implements ScriptOperations {
return args;
}
List<Object> convertedValues = new ArrayList<Object>(args.length);
List<Object> convertedValues = new ArrayList<>(args.length);
for (Object arg : args) {
convertedValues.add(arg instanceof String && quote ? String.format("'%s'", arg)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2022 the original author or authors.
* Copyright 2015-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 the original author or authors.
* Copyright 2021-2023 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.

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2022-2023 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.context.ApplicationEventPublisher;
import org.springframework.lang.Nullable;
/**
* Delegate class to encapsulate lifecycle event configuration and publishing.
*
* @author Mark Paluch
* @since 4.0
* @see ApplicationEventPublisher
*/
class EntityLifecycleEventDelegate {
private @Nullable ApplicationEventPublisher publisher;
private boolean eventsEnabled = true;
public void setPublisher(@Nullable ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public boolean isEventsEnabled() {
return eventsEnabled;
}
public void setEventsEnabled(boolean eventsEnabled) {
this.eventsEnabled = eventsEnabled;
}
/**
* Publish an application event if event publishing is enabled.
*
* @param event the application event.
*/
public void publishEvent(Object event) {
if (canPublishEvent()) {
publisher.publishEvent(event);
}
}
private boolean canPublishEvent() {
return publisher != null && eventsEnabled;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,12 @@ package org.springframework.data.mongodb.core;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import org.bson.BsonNull;
import org.bson.Document;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.convert.CustomConversions;
@@ -29,6 +30,8 @@ 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.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mongodb.core.CollectionOptions.TimeSeriesOptions;
@@ -45,9 +48,11 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.EntityProjection;
import org.springframework.data.projection.EntityProjectionIntrospector;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.TargetAware;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -57,6 +62,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.client.model.ChangeStreamPreAndPostImagesOptions;
import com.mongodb.client.model.CreateCollectionOptions;
import com.mongodb.client.model.TimeSeriesGranularity;
import com.mongodb.client.model.ValidationOptions;
@@ -114,15 +120,19 @@ class EntityOperations {
Assert.notNull(entity, "Bean must not be null");
if (entity instanceof TargetAware targetAware) {
return new SimpleMappedEntity((Map<String, Object>) targetAware.getTarget(), this);
}
if (entity instanceof String) {
return new UnmappedEntity(parse(entity.toString()));
return new UnmappedEntity(parse(entity.toString()), this);
}
if (entity instanceof Map) {
return new SimpleMappedEntity((Map<String, Object>) entity);
return new SimpleMappedEntity((Map<String, Object>) entity, this);
}
return MappedEntity.of(entity, context);
return MappedEntity.of(entity, context, this);
}
/**
@@ -139,14 +149,14 @@ class EntityOperations {
Assert.notNull(conversionService, "ConversionService must not be null");
if (entity instanceof String) {
return new UnmappedEntity(parse(entity.toString()));
return new UnmappedEntity(parse(entity.toString()), this);
}
if (entity instanceof Map) {
return new SimpleMappedEntity((Map<String, Object>) entity);
return new SimpleMappedEntity((Map<String, Object>) entity, this);
}
return AdaptibleMappedEntity.of(entity, context, conversionService);
return AdaptibleMappedEntity.of(entity, context, conversionService, this);
}
/**
@@ -174,7 +184,14 @@ class EntityOperations {
"No class parameter provided, entity collection can't be determined");
}
return context.getRequiredPersistentEntity(entityClass).getCollection();
MongoPersistentEntity<?> persistentEntity = context.getPersistentEntity(entityClass);
if (persistentEntity == null) {
throw new MappingException(String.format(
"Cannot determine collection name from type '%s'. Is it a store native type?", entityClass.getName()));
}
return persistentEntity.getCollection();
}
public Query getByIdInQuery(Collection<?> entities) {
@@ -276,6 +293,11 @@ class EntityOperations {
* @see EntityProjectionIntrospector#introspect(Class, Class)
*/
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType, Class<D> entityType) {
MongoPersistentEntity<?> persistentEntity = queryMapper.getMappingContext().getPersistentEntity(entityType);
if (persistentEntity == null && !resultType.isInterface() || ClassUtils.isAssignable(Document.class, resultType)) {
return (EntityProjection) EntityProjection.nonProjecting(resultType);
}
return introspector.introspect(resultType, entityType);
}
@@ -334,6 +356,9 @@ class EntityOperations {
result.timeSeriesOptions(options);
});
collectionOptions.getChangeStreamOptions().ifPresent(it -> result
.changeStreamPreAndPostImagesOptions(new ChangeStreamPreAndPostImagesOptions(it.getPreAndPostImages())));
return result;
}
@@ -352,6 +377,7 @@ class EntityOperations {
* A representation of information about an entity.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @since 2.1
*/
interface Entity<T> {
@@ -370,6 +396,16 @@ class EntityOperations {
*/
Object getId();
/**
* Returns the property value for {@code key}.
*
* @param key
* @return
* @since 4.1
*/
@Nullable
Object getPropertyValue(String key);
/**
* Returns the {@link Query} to find the entity by its identifier.
*
@@ -440,6 +476,15 @@ class EntityOperations {
* @since 2.1.2
*/
boolean isNew();
/**
* @param sortObject
* @return
* @since 4.1
* @throws IllegalStateException if a sort key yields {@literal null}.
*/
Map<String, Object> extractKeys(Document sortObject, Class<?> sourceType);
}
/**
@@ -461,7 +506,7 @@ class EntityOperations {
T populateIdIfNecessary(@Nullable Object id);
/**
* Initializes the version property of the of the current entity if available.
* Initializes the version property of the current entity if available.
*
* @return the entity with the version property updated if available.
*/
@@ -487,9 +532,11 @@ class EntityOperations {
private static class UnmappedEntity<T extends Map<String, Object>> implements AdaptibleEntity<T> {
private final T map;
private final EntityOperations entityOperations;
protected UnmappedEntity(T map) {
protected UnmappedEntity(T map, EntityOperations entityOperations) {
this.map = map;
this.entityOperations = entityOperations;
}
@Override
@@ -499,7 +546,12 @@ class EntityOperations {
@Override
public Object getId() {
return map.get(ID_FIELD);
return getPropertyValue(ID_FIELD);
}
@Override
public Object getPropertyValue(String key) {
return map.get(key);
}
@Override
@@ -523,8 +575,8 @@ class EntityOperations {
@Override
public MappedDocument toMappedDocument(MongoWriter<? super T> writer) {
return MappedDocument.of(map instanceof Document //
? (Document) map //
return MappedDocument.of(map instanceof Document document //
? document //
: new Document(map));
}
@@ -553,12 +605,50 @@ class EntityOperations {
public boolean isNew() {
return map.get(ID_FIELD) != null;
}
@Override
public Map<String, Object> extractKeys(Document sortObject, Class<?> sourceType) {
Map<String, Object> keyset = new LinkedHashMap<>();
MongoPersistentEntity<?> sourceEntity = entityOperations.context.getPersistentEntity(sourceType);
if (sourceEntity != null && sourceEntity.hasIdProperty()) {
keyset.put(sourceEntity.getRequiredIdProperty().getName(), getId());
} else {
keyset.put(ID_FIELD, getId());
}
for (String key : sortObject.keySet()) {
Object value = resolveValue(key, sourceEntity);
if (value == null) {
throw new IllegalStateException(
String.format("Cannot extract value for key %s because its value is null", key));
}
keyset.put(key, value);
}
return keyset;
}
@Nullable
private Object resolveValue(String key, @Nullable MongoPersistentEntity<?> sourceEntity) {
if (sourceEntity == null) {
return BsonUtils.resolveValue(map, key);
}
PropertyPath from = PropertyPath.from(key, sourceEntity.getTypeInformation());
PersistentPropertyPath<MongoPersistentProperty> persistentPropertyPath = entityOperations.context
.getPersistentPropertyPath(from);
return BsonUtils.resolveValue(map, persistentPropertyPath.toDotPath(p -> p.getFieldName()));
}
}
private static class SimpleMappedEntity<T extends Map<String, Object>> extends UnmappedEntity<T> {
protected SimpleMappedEntity(T map) {
super(map);
protected SimpleMappedEntity(T map, EntityOperations entityOperations) {
super(map, entityOperations);
}
@Override
@@ -566,8 +656,8 @@ class EntityOperations {
public MappedDocument toMappedDocument(MongoWriter<? super T> writer) {
T bean = getBean();
bean = (T) (bean instanceof Document //
? (Document) bean //
bean = (T) (bean instanceof Document document//
? document //
: new Document(bean));
Document document = new Document();
writer.write(bean, document);
@@ -581,23 +671,26 @@ class EntityOperations {
private final MongoPersistentEntity<?> entity;
private final IdentifierAccessor idAccessor;
private final PersistentPropertyAccessor<T> propertyAccessor;
private final EntityOperations entityOperations;
protected MappedEntity(MongoPersistentEntity<?> entity, IdentifierAccessor idAccessor,
PersistentPropertyAccessor<T> propertyAccessor) {
PersistentPropertyAccessor<T> propertyAccessor, EntityOperations entityOperations) {
this.entity = entity;
this.idAccessor = idAccessor;
this.propertyAccessor = propertyAccessor;
this.entityOperations = entityOperations;
}
private static <T> MappedEntity<T> of(T bean,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
EntityOperations entityOperations) {
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(bean.getClass());
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(bean);
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
return new MappedEntity<>(entity, identifierAccessor, propertyAccessor);
return new MappedEntity<>(entity, identifierAccessor, propertyAccessor, entityOperations);
}
@Override
@@ -610,6 +703,11 @@ class EntityOperations {
return idAccessor.getRequiredIdentifier();
}
@Override
public Object getPropertyValue(String key) {
return propertyAccessor.getProperty(entity.getRequiredPersistentProperty(key));
}
@Override
public Query getByIdQuery() {
@@ -687,6 +785,60 @@ class EntityOperations {
public boolean isNew() {
return entity.isNew(propertyAccessor.getBean());
}
@Override
public Map<String, Object> extractKeys(Document sortObject, Class<?> sourceType) {
Map<String, Object> keyset = new LinkedHashMap<>();
MongoPersistentEntity<?> sourceEntity = entityOperations.context.getPersistentEntity(sourceType);
if (sourceEntity != null && sourceEntity.hasIdProperty()) {
keyset.put(sourceEntity.getRequiredIdProperty().getName(), getId());
} else {
keyset.put(entity.getRequiredIdProperty().getName(), getId());
}
for (String key : sortObject.keySet()) {
Object value;
if (key.indexOf('.') != -1) {
// follow the path across nested levels.
// TODO: We should have a MongoDB-specific property path abstraction to allow diving into Document.
value = getNestedPropertyValue(key);
} else {
value = getPropertyValue(key);
}
if (value == null) {
throw new IllegalStateException(
String.format("Cannot extract value for key %s because its value is null", key));
}
keyset.put(key, value);
}
return keyset;
}
@Nullable
private Object getNestedPropertyValue(String key) {
String[] segments = key.split("\\.");
Entity<?> currentEntity = this;
Object currentValue = BsonNull.VALUE;
for (int i = 0; i < segments.length; i++) {
String segment = segments[i];
currentValue = currentEntity.getPropertyValue(segment);
if (i < segments.length - 1) {
currentEntity = entityOperations.forEntity(currentValue);
}
}
return currentValue != null ? currentValue : BsonNull.VALUE;
}
}
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
@@ -696,9 +848,9 @@ class EntityOperations {
private final IdentifierAccessor identifierAccessor;
private AdaptibleMappedEntity(MongoPersistentEntity<?> entity, IdentifierAccessor identifierAccessor,
ConvertingPropertyAccessor<T> propertyAccessor) {
ConvertingPropertyAccessor<T> propertyAccessor, EntityOperations entityOperations) {
super(entity, identifierAccessor, propertyAccessor);
super(entity, identifierAccessor, propertyAccessor, entityOperations);
this.entity = entity;
this.propertyAccessor = propertyAccessor;
@@ -707,14 +859,14 @@ class EntityOperations {
private static <T> AdaptibleEntity<T> of(T bean,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
ConversionService conversionService) {
ConversionService conversionService, EntityOperations entityOperations) {
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(bean.getClass());
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(bean);
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
return new AdaptibleMappedEntity<>(entity, identifierAccessor,
new ConvertingPropertyAccessor<>(propertyAccessor, conversionService));
new ConvertingPropertyAccessor<>(propertyAccessor, conversionService), entityOperations);
}
@Nullable
@@ -815,6 +967,14 @@ class EntityOperations {
* @since 3.3
*/
TimeSeriesOptions mapTimeSeriesOptions(TimeSeriesOptions options);
/**
* @return the name of the id field.
* @since 4.1
*/
default String getIdKeyName() {
return ID_FIELD;
}
}
/**
@@ -937,6 +1097,11 @@ class EntityOperations {
MongoPersistentProperty persistentProperty = entity.getPersistentProperty(name);
return persistentProperty != null ? persistentProperty.getFieldName() : name;
}
@Override
public String getIdKeyName() {
return entity.getIdProperty().getName();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2023 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.

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