1
0
mirror of synced 2026-05-24 21:23:18 +00:00

Compare commits

...

228 Commits

Author SHA1 Message Date
Christoph Strobl 2f54bdec01 Release version 5.1 GA (2023.0.0).
See #2532
2023-05-12 14:14:39 +02:00
Christoph Strobl b3d582cd93 Prepare 5.1 GA (2023.0.0).
See #2532
2023-05-12 14:14:08 +02:00
Pierre Mazieres @Semarchy 9c497c2dea Fix assignment in NativeQueryBuilder.
Original Pull Request: #2543
Closes #2542
2023-05-10 18:31:04 +02:00
Peter-Josef Meisch 406961c13b Allow SSL configuration with the Elasticsearch CA fingerprint.
Original Pull Request #2540
Closes #2539
2023-04-29 18:43:50 +02:00
Peter-Josef Meisch 17ecce4362 Documentation update.
Original Pull Request #2537
Closes #2536
2023-04-23 22:16:53 +02:00
Peter-Josef Meisch bbd5e8119a Component templates.
Original Pull Request #2534
Closes #1458
2023-04-18 20:58:07 +02:00
Peter-Josef Meisch df7a614638 dependency updates.
Original Pull Request #2531
Closes #2530
2023-04-15 12:37:50 +02:00
Greg L. Turnquist 90db137548 After release cleanups.
See #2500
2023-04-14 12:00:04 -05:00
Greg L. Turnquist aa3a3df2f3 Prepare next development iteration.
See #2500
2023-04-14 11:59:59 -05:00
Greg L. Turnquist de437cd7da Release version 5.1 RC1 (2023.0.0).
See #2500
2023-04-14 11:54:01 -05:00
Greg L. Turnquist 5ac83f696e Prepare 5.1 RC1 (2023.0.0).
See #2500
2023-04-14 11:53:25 -05:00
Peter-Josef Meisch 6100c2491a Cleanup.
Original Pull Request #2526
Closes #2525
2023-04-12 22:19:19 +02:00
Peter-Josef Meisch d597baccd3 Upgrade to Elasticsearch 8.7.0.
Original Pull Request #2524
Closes #2523
2023-04-11 20:45:52 +02:00
Peter-Josef Meisch dfc1be286c Polishing 2023-04-10 11:22:45 +02:00
JKatzwinkel 1f7fa77c15 Fix inner hits metadata mapping.
Original Pull Request #2522
Closes #2521
2023-04-10 11:18:26 +02:00
Mark Paluch 699c0ef265 Upgrade to Maven Wrapper 3.9.1.
See #2518
2023-04-06 16:16:37 +02:00
Peter-Josef Meisch 9771b9c1d6 clarify PR submission 2023-04-05 08:07:27 +02:00
Peter-Josef Meisch 437279f637 remove erroneous linebreak 2023-04-04 17:45:28 +02:00
Peter-Josef Meisch ed1c416cd3 Javadoc
Original Pull Request #2516
Closes #2515
2023-04-03 20:20:13 +02:00
Peter-Josef Meisch a5fb7a3c76 Remove old methods from configuration.
Original Pull Request #2513
Closes #2512
2023-04-02 20:16:13 +02:00
Greg L. Turnquist 7f772703d3 Test against Java 20 on CI.
See #2507.
2023-03-30 11:40:56 -05:00
Greg L. Turnquist 43eb40e45e Upgrade to BlockHound 1.0.7.RELEASE.
Closes #2510.
2023-03-30 11:39:57 -05:00
Greg L. Turnquist 75bf578b31 Update CI properties.
See #2500
2023-03-28 13:58:23 -05:00
Peter-Josef Meisch 675b77982b Allow field names with dots.
Original Pull Request #2504
Closes #2502
2023-03-22 19:51:00 +01:00
Christoph Strobl d95af9fcfa After release cleanups.
See #2464
2023-03-20 15:05:35 +01:00
Christoph Strobl 96985fa14a Prepare next development iteration.
See #2464
2023-03-20 15:05:34 +01:00
Christoph Strobl ff1f25b0b2 Release version 5.1 M3 (2023.0.0).
See #2464
2023-03-20 15:01:48 +01:00
Christoph Strobl d3e95815d9 Prepare 5.1 M3 (2023.0.0).
See #2464
2023-03-20 15:01:20 +01:00
Peter-Josef Meisch 797dbb5a18 Improve save(Flux<T>) implementations.
Original Pull Request #2497
Closes #2496
Closes #2492
2023-03-19 15:01:24 +01:00
Mark Paluch a7d6b9df6d Adopt to newly introduced ParameterAccessor.getScrollPosition().
Closes #2494
2023-03-16 15:26:47 +01:00
Peter-Josef Meisch ade90328d3 ValueConverters can be derived from AbstractPropertyValueConverter.
Original Pull Request #2490
Closes #2489
2023-03-05 20:54:20 +01:00
Peter-Josef Meisch ec77b3a082 Add option to not write version to document source.
Original Pull Request #2487
Closes #2466
2023-03-05 09:53:41 +01:00
Peter-Josef Meisch 63cebd7038 Fix putMapping request creation .
Original Pull Request #2483
Closes #2487
2023-02-26 15:41:12 +01:00
Peter-Josef Meisch e7c9bf20f6 Add option to specify if empty property should not be sent to Elasticsearch.
Original Pull Request #2482
Closes #2290
2023-02-25 17:50:12 +01:00
Peter-Josef Meisch b4c3e25a60 Move scripted fields from native query up to base query.
Original Pull Request #2481
Closes #2477
2023-02-25 14:43:32 +01:00
Peter-Josef Meisch 43ab49b5fa refactor tests
Original Pull Request #2480
Closes #2479
2023-02-25 13:38:50 +01:00
Peter-Josef Meisch c9b8b1af19 Adapt custom routing.
Original Pull Request #2474
Closes #2087
2023-02-21 20:17:45 +01:00
Mark Paluch 9c80dc9ae8 Upgrade to Maven Wrapper 3.9.0.
See #2471
2023-02-20 11:58:19 +01:00
Peter-Josef Meisch 03ffb07827 Upgrade to Elasticsearch 8.6.2.
Original Pull Request #2470
Closes #2468
2023-02-19 12:57:18 +01:00
Christoph Strobl d29cf7788b After release cleanups.
See #2463
2023-02-17 14:25:49 +01:00
Christoph Strobl 71e46c8277 Prepare next development iteration.
See #2463
2023-02-17 14:25:47 +01:00
Christoph Strobl fc1e8d3cf6 Release version 5.1 M2 (2023.0.0).
See #2463
2023-02-17 14:22:31 +01:00
Christoph Strobl c84a8bbc12 Prepare 5.1 M2 (2023.0.0).
See #2463
2023-02-17 14:22:18 +01:00
Christoph Strobl 97a9176775 After release cleanups.
See #2367
2023-02-17 13:31:54 +01:00
Christoph Strobl 7eff8f5dd1 Prepare next development iteration.
See #2367
2023-02-17 13:31:53 +01:00
Christoph Strobl 6d4247312d Release version 5.1 M1 (2023.0.0).
See #2367
2023-02-17 13:27:50 +01:00
Christoph Strobl 81f52244a8 Prepare 5.1 M1 (2023.0.0).
See #2367
2023-02-17 13:27:11 +01:00
Peter-Josef Meisch e741df9c7c Support KNN queries in NativeQuery.
Original Pull Request #2458
Closes #2433
2023-02-14 20:45:16 +01:00
Peter-Josef Meisch 3875bb6abf Upgrade to Elasticsearch 8.6.1.
Original Pull Request #2455

Closes #2452
2023-02-14 18:34:19 +01:00
Peter-Josef Meisch 1860d7353a Allow for Spring Data Elasticsearch queries to be added to NativeQuery.
Original Pull Request #2451
#Closes 2391
2023-02-12 17:50:55 +01:00
Peter-Josef Meisch 4f30a492b9 @Query annotated repository method does not use Sort parameter.
Original Pull Request #2450
Closes #2449
2023-02-10 22:46:39 +01:00
Peter-Josef Meisch 6805fff1fa Option to add docvalue_fields to a search query.
Original Pull Request #2446
#Closes 2316
2023-02-06 20:39:00 +01:00
Urs Keller 0971acfe25 Fix ReactiveIndicesTemplate: Refresh not called on bound indices.
Original Pull Request #2444
Closes #2441
2023-02-03 19:56:47 +01:00
Zhenxi 36805c3ecb Fix typo in documentation.
Original Pull Request #2443
Closes #2442
2023-02-03 18:31:16 +01:00
Mark Paluch b75d2eb01f Update CI properties.
See #2367
2023-01-30 10:49:51 +01:00
Mark Paluch f94f2b0618 Upgrade to Maven Wrapper 3.8.7.
See #2440
2023-01-30 10:48:19 +01:00
Peter-Josef Meisch 82607b3d4d Add Option to not store the id property in the document source.
Original Pull Request #2438
Closes #2364
2023-01-29 16:51:41 +01:00
Peter-Josef Meisch cf9b106c31 Polishing 2023-01-26 19:07:21 +01:00
Hamid Rahimi b0c2ce3084 Overload multiSearch method to support list of IndexCoordinates.
Original Pull Request #2436
Closes #2434
2023-01-26 19:03:35 +01:00
Peter-Josef Meisch b3f9bdb80f Support returning the name of the index an entity was persisted to.
Original Pull Request #2435
Closes #2112
2023-01-25 20:17:26 +01:00
Peter-Josef Meisch d9bf76fb31 Fix reading response runtime field by mapping.
Original Pull Request #2432
Closes #2431
2023-01-22 19:15:12 +01:00
Peter-Josef Meisch 4d11a56e84 Upgrade to Elasticsearch 8.6.0.
Original Pull Request #2430
Closes #2426
2023-01-20 09:05:53 +01:00
Peter-Josef Meisch 73d5d623dd Enable custom similarity value.
Original Pull Request #2429
Closes #2424
2023-01-19 21:08:40 +01:00
ezequielantunez 5a36f5e1e8 Add Query by Example feature.
Original Pull Request #2422
Closes #2418
2023-01-11 21:50:19 +01:00
Peter-Josef Meisch 44a5c7545f AOT/Native support.
Original Pull Request #2423
Closes #2419
2023-01-08 15:43:32 +01:00
Peter-Josef Meisch 28489ffee8 findAllById returns all requested documents.
Original Pull Request #2421
Closes #2417
2023-01-04 17:13:22 +01:00
Mark Paluch 605c83f628 Extend license header copyright years to 2023.
See #2416
2023-01-02 09:53:40 +01:00
Peter-Josef Meisch f3a6a42b82 Upgrade Elasticsearch to 8.5.3 (old client to 7.17.8).
Original Pull Request #2411
Closes #2403
2022-12-30 23:30:51 +01:00
Peter-Josef Meisch efd394370a Implement search by template.
Original Pull Request #2410
Closes #1891
2022-12-30 19:11:17 +01:00
Scooby 4d7d0955f9 IndexQuery's id get ignored in bulkIndexOperation.
Original Pull Request #2407
Closdes #2405
2022-12-27 19:53:51 +01:00
Peter-Josef Meisch 2fb90621a2 Polishing. 2022-12-23 14:20:56 +01:00
Hao bc7667fdbc Update documentation.
Original Pull Request #2400
Closes #2404
2022-12-23 14:19:28 +01:00
Peter-Josef Meisch 2ea568d2e4 Rename QueryBuilders to avoid name clash wih Elasticsearch class.
Original Pull Request #2397
Closes #2390
2022-12-16 20:15:22 +01:00
Peter-Josef Meisch c460a5f37d Add update(entity, index) operation.
Original Pull Request #2394
Closes #2385
2022-12-14 08:07:48 +01:00
Peter-Josef Meisch e1c8a2adeb Switch reactive unpaged search from scroll to pit with search_after.
Original Pull Request #2393
Closes #1685
2022-12-12 20:23:39 +01:00
Peter-Josef Meisch 014aa3dbf6 Add reactive search batch size option to query.
Original Pull Request #2392
Closes #2061
2022-12-07 13:07:05 +01:00
Peter-Josef Meisch 9446d726bc Fix NPE in ElasticsearchExceptionTranslator.
Original Pull Request #2389
Closes #2388
2022-12-05 13:13:27 +01:00
Peter-Josef Meisch 1fa6c9f3e5 Support Kotlin Flow as possible return type in repository functions.
Original Pull Request #2387
Closes #2386
2022-12-03 22:46:55 +01:00
Peter-Josef Meisch 3c7ce7413a Upgrade to Elasticsearch 8.5.2.
Original Pull Request #2382
Closes #2380
2022-11-26 09:24:13 +01:00
Peter-Josef Meisch f6c6f64f04 Change AliasData filter property to Query.
Original Pull Request #2374
Closes #2073
2022-11-19 19:38:26 +01:00
Peter-Josef Meisch 9f63369c4b Remove all indices after testing.
Original Pull Request #2373
Closes #2372
2022-11-19 15:12:32 +01:00
Peter-Josef Meisch bae182b162 Upgrade to Elasticsearch 8.5.1.
Original Pull Request #2370
Closes #2368
2022-11-18 19:53:54 +01:00
Mark Paluch 8db4e430d7 Update CI properties.
See #2367
2022-11-18 15:31:11 +01:00
Mark Paluch c1de8b1e85 After release cleanups.
See #2336
2022-11-18 14:30:20 +01:00
Mark Paluch c7b8848afe Prepare next development iteration.
See #2336
2022-11-18 14:30:19 +01:00
Mark Paluch 0cf1c08d6e Release version 5.0 GA (2022.0.0).
See #2336
2022-11-18 14:26:23 +01:00
Mark Paluch 81cac1f505 Prepare 5.0 GA (2022.0.0).
See #2336
2022-11-18 14:26:13 +01:00
Peter-Josef Meisch ab8cbdf4d9 Bulk operations must prefer index set on individual query.
Original Pull Request #2363
Closes #2362
2022-11-15 20:55:21 +01:00
Peter-Josef Meisch d7e42fcb76 Update documentation.
Original Pull Request #2361
Closes #2360
2022-11-10 21:41:10 +01:00
Tiny f8ddf16c0c Update Dynamic.java
Original Pull Request #2357
Closes #2359
2022-11-10 20:09:56 +01:00
Jens Schauder f79a8016d1 Replace New and Noteworthy with links to release notes.
Closes #2358
See spring-projects/spring-data-relational#1351
2022-11-10 15:24:36 +01:00
Mark Paluch 7d38d6c615 Remove unused test dependencies.
Also, pin CDI version to 3.0.

Closes #2355
2022-11-08 10:23:38 +01:00
Peter-Josef Meisch ceca98492b Deprecate ClientLogger.
Original Pull Request #2354
Closes #2353
2022-11-05 17:15:24 +01:00
Mark Paluch 08f86d8c22 After release cleanups.
See #2341
2022-11-04 15:26:39 +01:00
Mark Paluch 487a599bbb Prepare next development iteration.
See #2341
2022-11-04 15:26:38 +01:00
Mark Paluch 1f4689297e Release version 5.0 RC2 (2022.0.0).
See #2341
2022-11-04 15:23:17 +01:00
Mark Paluch 327774b8fa Prepare 5.0 RC2 (2022.0.0).
See #2341
2022-11-04 15:23:07 +01:00
Peter-Josef Meisch 96c9aa8af6 Remove client specific code.
Original Pull Request #2352
Closes #2351
2022-11-03 20:44:25 +01:00
Peter-Josef Meisch 049bcf504e Use custom refresh policy in reactive repository with all clients.
Original Pull Request #2350
Closes #2349
2022-11-03 19:49:48 +01:00
Peter-Josef Meisch 744b3ed6b4 Upgrade to Elasticsearch 8.5.0.
Original Pull Request #2348
Closes #2345
Closes #2300
2022-11-03 18:31:48 +01:00
Mark Paluch 5da1994b40 Update CI properties.
See #2341
2022-10-31 10:36:35 +01:00
Peter-Josef Meisch 14ab00f5b8 adapt auditing configuration.
Original Pull Request #2344
Closes #2343
2022-10-30 22:02:45 +01:00
Peter-Josef Meisch 883741e9c1 Remove javax nullability annotations.
Original Pull Request #2342
Closes #2340
2022-10-22 16:07:03 +02:00
Peter-Josef Meisch e67150a55b Fix repository methods value converting.
Original Pull Request #2339
Closes #2338
2022-10-19 21:52:49 +02:00
Mark Paluch f21285d33c After release cleanups.
See #2298
2022-10-13 17:31:16 +02:00
Mark Paluch 657741cadd Prepare next development iteration.
See #2298
2022-10-13 17:31:15 +02:00
Mark Paluch 73a6a1214d Release version 5.0 RC1 (2022.0.0).
See #2298
2022-10-13 17:24:25 +02:00
Mark Paluch bf4d730328 Prepare 5.0 RC1 (2022.0.0).
See #2298
2022-10-13 17:24:04 +02:00
Peter-Josef Meisch 03ecc48b09 Escape backslash in StringQuery.
Original Pull Request 
Closes #2326
2022-10-11 22:36:43 +02:00
Peter-Josef Meisch c05146adce Upgrade to Elasticsearch 8.4.3.
Original Pull Request #2329
Closes #2323
2022-10-10 20:55:28 +02:00
Peter-Josef Meisch c59395d36d Adapt to Junit changes.
Original Pull Request #2325
Closes #2324
2022-10-07 09:00:15 +02:00
Gonçalo Montalvão Marques ae88ea3506 Fix typos in documentation.
Original Pull Request #2321
Closes #2320
2022-10-06 12:57:48 +02:00
Peter-Josef Meisch cdb92f6ee4 Enable non-field-backed properties.
Original Pull Request #2319
Closes #1489
2022-10-01 18:02:29 +02:00
Peter-Josef Meisch 5a52d6136f Fix documentation index.
Original Pull Request #2315
Closes #2299
2022-09-28 22:21:23 +02:00
Peter-Josef Meisch fb210e338f Fix copyright year in documentation.
Original Pull Request #2314
Closes #2313
2022-09-27 21:57:43 +02:00
Peter-Josef Meisch 05c6444b71 Internal refactoring stringRepresentation/convertId.
Original Pull Request #2312
Closes #2228
2022-09-27 20:23:33 +02:00
Peter-Josef Meisch 8a1d8deb67 Polishing.
See #2304
2022-09-25 21:43:15 +02:00
puppylpg b038bbe778 Update document partially by specified entity.
Original Pull Request #2310 
Closes #2304
2022-09-25 21:19:14 +02:00
Peter-Josef Meisch 1396f53fde Add search extension plugins parameters to NativeQuery.
Original Pull Request #2307
Closes #2150
2022-09-23 19:37:02 +02:00
Peter-Josef Meisch b4fe01d09b Adapt to Elasticsearch client fix (ES client issue 286).
Original Pull Request #2306
Closes #2165
2022-09-23 07:41:19 +02:00
Peter-Josef Meisch eabde9c543 Adapt to Elasticsearch fix.
Original Pull Request #2302
Closes #2171
2022-09-21 22:14:31 +02:00
Peter-Josef Meisch 120ca8579f Upgrade to Elasticsearch 8.4.2.
Original Pull Request #2301
Closes #2284
2022-09-21 21:55:53 +02:00
Spring Builds 589b2adaca After release cleanups.
See #2222
2022-09-19 14:39:11 +00:00
Spring Builds 77c844d809 Prepare next development iteration.
See #2222
2022-09-19 14:38:59 +00:00
Spring Builds bad6afdecc Release version 5.0 M6 (2022.0.0).
See #2222
2022-09-19 14:15:27 +00:00
Spring Builds a6a5e3f7f5 Prepare 5.0 M6 (2022.0.0).
See #2222
2022-09-19 14:13:03 +00:00
Peter-Josef Meisch b3a7a44db1 Adapt xml-namespace setup to the new Elasticsearch client.
Original Pull Request #2295
Closes #21294
2022-09-10 17:05:32 +02:00
Peter-Josef Meisch ccf136c8e2 CDI implementation uses the new Elasticsearch client.
Original Pull Request #2293
Closes #2292
2022-09-10 11:09:43 +02:00
Peter-Josef Meisch 77da22598a Fix NPE in RequestFactory when language in UpdateQuery is not set (4.4.x).
Original Pull Request #2288
Closes #2287

(cherry picked from commit 988736dd41)
2022-09-03 08:09:19 +02:00
Peter-Josef Meisch 86634ceb38 Fix mapping of property values into a collection.
When reading from Elasticsearch into a property of type Collection<T> (List<T> or Set<T>) the MappingElasticsearchConverter now can read both from the returned JSON:

    an array of T objects - will put the objects in a corresponding collection
    a single T object will put the single object into a corrsponding colletcion

This is implemented and tested for both: entities where the properties have setters and immutable classes that only provide an all-args constructor.

Original Pull Request #2282
Closes #2280
2022-08-29 20:14:11 +02:00
Peter-Josef Meisch 8377f64a8a Fix update call in reactive client (Elasticsearch 7 client)
Original Pull Request #2281 
Closes #2276
2022-08-26 08:06:37 +02:00
Peter-Josef Meisch b549601d34 Polishing documentation.
Adding missing documentation about the new HttpHeaders class

Original Pull Request #2279
See #2277
2022-08-24 18:37:54 +02:00
Peter-Josef Meisch 3298ba21ce Use custom HttpHeaders.
Original Pull Request #2278
Closes #2277
2022-08-24 13:10:27 +02:00
Peter-Josef Meisch 18be558740 Reactive implementation of the point in time API.
This PR adds the reactive implementation for the point in time API that was missing in #2273.

Original Pull Request #2275
Closes #2274
2022-08-20 18:23:50 +02:00
Peter-Josef Meisch 46cd4cd59e Implement the point in time API.
Original Pull Request #2273
Closes #1684
2022-08-17 13:11:11 +02:00
Peter-Josef Meisch a4ed7300d1 Code cleanup (Java 17).
Original Pull Request #2271
Closes #2270
2022-08-13 15:07:47 +02:00
Peter-Josef Meisch b511756b2b Polishing.
See #2265
2022-08-12 07:27:07 +02:00
Peter-Josef Meisch 68ba4cd39b Polishing.
See #2267
2022-08-11 06:53:07 +02:00
cdalexndr 6b39b39d35 support RuntimeField with no script.
Original Pull Request #2268
Closes #2267
2022-08-11 06:31:17 +02:00
Peter-Josef Meisch 4f4c99ec1f Fix javadoc error.
See #2265
2022-08-10 21:48:05 +02:00
Sascha Woo 6cfb8eeea6 [elc] add support for sorting results by SortOptions to NativeQuery.
Original Pull Request #2264
Closes #2263
2022-08-10 21:34:17 +02:00
Peter-Josef Meisch c4910bf399 Polishing.
See #2265
2022-08-10 21:19:46 +02:00
Sascha Woo c93039410a [elc] add convenient methods to improve access to aggregations.
Original Pull Request #2266
Closes #2265
2022-08-10 21:10:17 +02:00
Peter-Josef Meisch 33c9180ccd Reenable test for the new Elasticsearch Client.
Original Pull Request #2261
Closes #2260
2022-08-07 20:33:07 +02:00
Peter-Josef Meisch df5fd0b97c sourcefilter documentation.
Original Pull Request #2258
Closes #2257
2022-08-07 12:22:46 +02:00
Peter-Josef Meisch 44a79093ce Remove deprecated code.
Original Pull request #2256
Closes #2255
2022-08-06 23:16:38 +02:00
Peter-Josef Meisch cf135f4cdd Implement SourceFilters annotation.
Original Pull Request #2254
Closes #1280
Closes #2062
Closes #2146
Closes #2147
Closes #2151
2022-08-06 21:04:13 +02:00
Peter-Josef Meisch 954d8b0f97 Prefix bean names created by the configuration with "elasticsearch".
Original Pull Request #2253
Closes #2252
2022-08-05 21:14:06 +02:00
Peter-Josef Meisch e71c66949a Add missing properties to BaseQueryBuilder.
Original Pull Request #2251
Closes #2250
2022-08-04 17:50:37 +02:00
Peter-Josef Meisch acf02a1dc9 Don't try to write non-writeable properties.
Original Pull Request #2249
Closes #2230
2022-08-04 08:04:19 +02:00
Peter-Josef Meisch 6ad449c9f4 Polishing.
See #2247
2022-08-01 21:37:06 +02:00
lanicc 94ebc40582 Fix the JDK version required to build from the source code.
Original Pull Request #2248
Closes #2247
2022-08-01 21:25:10 +02:00
Peter-Josef Meisch ca39c1d24c Adapt to changes in Elasticsearch client code. (#2246)
Closes #2211
2022-07-31 18:33:43 +02:00
Peter-Josef Meisch 2049726bcb Dependency updates.
Original Pull Request #2245
Closes #2243
2022-07-30 20:42:12 +02:00
Peter-Josef Meisch 7620222f89 Upgrade to Elasticsearch 8.3.3.
Original Pull Request #2244
Closes #2242
2022-07-30 18:34:22 +02:00
Peter-Josef Meisch 1f1076aa8b Don't rely on collection to object conversion in spring-data-commons.
Original Pull Request #2241
Closes #2152
2022-07-28 07:41:07 +02:00
Peter-Josef Meisch 3e950b8053 SimpleElasticsearchRepository still uses erhlc code.
Original Pull Request #2239
Closes #2238
2022-07-24 22:03:01 +02:00
Peter-Josef Meisch 1c31a31e11 Refine the configuration callbacks in the ClientConfiguration.
Original Pull Request #2237
Closes #2149
2022-07-23 15:32:24 +02:00
Peter-Josef Meisch 373be49f97 Fix exists query for imperative repository implementation.
Original Pull Request #2236
Closes #2162
2022-07-22 21:56:24 +02:00
Peter-Josef Meisch 12b332223f Replace ClassTypeInformation usages with TypeInformation.
Original Pull Request #2235
Closes #2234
2022-07-19 22:37:05 +02:00
Peter-Josef Meisch 47c0e186ec Adapt to spring-data-commons changes.
Original Pull Request #2233
Closes #2232
2022-07-19 22:12:56 +02:00
Peter-Josef Meisch 8b69a8a030 Complete searchMsearchRequest for the new client.
Original Pull Request #2231
Closes #2156
2022-07-19 21:31:14 +02:00
Mark Paluch 8bb0d8cb41 Polishing.
Consistently use credential properties.

See #2222
2022-07-18 09:19:36 +02:00
Peter-Josef Meisch 26a2f54c0f Change visibility of InetSocketAddressParser.
Original Pull Request #2227
Closes #2226
2022-07-17 20:15:03 +02:00
Peter-Josef Meisch 1754f59e70 Upgrade to Elasticsearch 8.3.2.
Original Pull Request #2225
Closes #2214
2022-07-17 08:02:23 +02:00
Patrick Baumgartner 8a166a1a40 Fixing Typo.
Original Pull Request #2219
Closes #2223
2022-07-16 17:55:45 +02:00
Christoph Strobl 5c029602b5 After release cleanups.
See #2167
2022-07-15 15:30:55 +02:00
Christoph Strobl 82a545912e Prepare next development iteration.
See #2167
2022-07-15 15:30:47 +02:00
Christoph Strobl 587dd2edda Release version 5.0 M5 (2022.0.0).
See #2167
2022-07-15 15:18:31 +02:00
Christoph Strobl 451a22614d Prepare 5.0 M5 (2022.0.0).
See #2167
2022-07-15 15:17:57 +02:00
Peter-Josef Meisch 688f1722bd make ReactiveElasticsearchTemplate constructor public.
Original Pull Request #2217
Closes #2216
2022-07-12 17:47:55 +02:00
Peter-Josef Meisch b9dacc9a5e implement getAlias variants.
Original Pull Request #2213
Closes #2209
2022-07-09 23:40:34 +02:00
Peter-Josef Meisch e0acc5a2f9 Upgrade to Elasticsearch 8.3.1.
Original Pull Request #2212
Closes #2210
2022-07-06 22:21:47 +02:00
Peter-Josef Meisch 094e79d601 Make the new client the default.
Original Pull Request #2208
Closes #2159
2022-07-03 21:56:21 +02:00
Peter-Josef Meisch 81e2613669 Documentation fixes.
Original Pull Request #2207
Closes #2206
2022-06-28 22:06:00 +02:00
Peter-Josef Meisch d974788ab8 no more additions currently, the new client has the same functionality as the old.
Original Pull Request #2205
Closes #2155
2022-06-28 21:57:33 +02:00
Shijie.Ma 0109a68c32 Fix document about StringQuery.
Original Pull Request #2202
Closes #2204
2022-06-28 06:44:48 +02:00
diamondT 259c43af19 Fix handling of array-of-strings parameters for @Query-annotated queries.
Original Pull Request  #2182
Closes #2135
2022-06-27 20:35:06 +02:00
Peter-Josef Meisch a2802ce716 Upgrade to Elasticsearch 8.2.3.
Original Pull Request #2200
Closes #2198
2022-06-25 20:54:47 +02:00
Peter-Josef Meisch f901380766 Fix updatebyquery request.
Original Pull Request #2197
Closes #2191
2022-06-25 19:55:22 +02:00
Peter-Josef Meisch 1e4b70ba6d Refactor code using Elasticsearch libs.
Original Pull Request #2196
Closes #2157
2022-06-25 18:29:34 +02:00
Peter-Josef Meisch f917fb7a65 Update version documentation.
Original Pull Request #2195
Closes #2194
2022-06-25 17:40:07 +02:00
Peter-Josef Meisch bfc68699e0 Remove deprecated suggest calls from operations.
Original Pull Request #2193 
Closes #2192
2022-06-24 12:51:58 +02:00
Peter-Josef Meisch 96d0781f24 Parse returned suggest data using the new client.
Original Pull Request #2187
Closes #2154
2022-06-22 18:34:47 +02:00
Gonçalo Montalvão Marques 0a0fc75faa fix typos in migration guides documentation.
Original Pull Request #2181
Closes #2180
2022-06-03 19:35:40 +02:00
Mark Paluch ef880e6d3b Update CI properties.
See #2167
2022-06-03 09:34:20 +02:00
Mark Paluch 3835e8e363 Upgrade to Maven Wrapper 3.8.5.
See #2177
2022-06-03 09:32:51 +02:00
Peter-Josef Meisch 97d0566e94 Fix msearch setup after ES fixed it's issue.
Original Pull Request #2176
Closes #2138
2022-06-01 14:48:46 +02:00
Peter-Josef Meisch ac64a6a733 Use Elasticsearch 8.2.2.
Original Pull Request #2174
Closes #2158
2022-05-31 22:13:51 +02:00
panzhenchao c826adb152 Fix incorrect argument check asserts.
Original Pull Request #2169 
Closes #2170
2022-05-27 16:40:38 +02:00
Christoph Strobl 1eaebdec33 Fix pom.xml formatting.
This commit reverts formatting changes introduced via 7cb46b6ab0.

See #2122
2022-05-13 13:46:12 +02:00
Christoph Strobl d37ca4d533 After release cleanups.
See #2122
2022-05-13 10:53:27 +02:00
Christoph Strobl d676ffe066 Prepare next development iteration.
See #2122
2022-05-13 10:53:24 +02:00
Christoph Strobl 1bff5dd749 Release version 5.0 M4 (2022.0.0).
See #2122
2022-05-13 10:44:00 +02:00
Christoph Strobl 7cb46b6ab0 Prepare 5.0 M4 (2022.0.0).
See #2122
2022-05-13 10:43:21 +02:00
Peter-Josef Meisch a86658c397 Add new Elasticsearch client as an alternative to the existing REST client.
Original Pull Request #2160
Closes #1973

(cherry picked from commit c0b26a51f1)
2022-05-12 07:38:44 +02:00
Peter-Josef Meisch 0950dd6c7a Update to Elasticsearch 7.17.3.
Original Pull Request #2145
Closes #2144
2022-04-24 11:53:19 +02:00
Peter-Josef Meisch e1c926e134 Use Range class from spring-data-commons.
Original Pull Request #2137
Closes #2098
2022-04-15 08:42:26 +02:00
Peter-Josef Meisch 6a201b4e34 Add more implementations using the new client.
Original Pull Request #2136
See #1973

(cherry picked from commit 8cef50347e)
2022-04-13 22:34:44 +02:00
Oliver Drotbohm 48e2e9dea7 Adapt to changes in Spring Framework 6.0 HttpStatus.
Fixes #2134.
2022-04-11 17:00:59 +02:00
Peter-Josef Meisch a60f3059e1 Upgrade to Elasticsearch 7.17.2.
Original Pull Request #2131
Closes #2130
2022-04-02 21:14:20 +02:00
Peter-Josef Meisch 3154c74f94 Add info to readme about Elasticsearch versions.
Original Pull Request #2128
Closes #2127
2022-03-31 07:39:19 +02:00
Jing Wei 1407d4d3d6 Upgrade pitest version and add jvmArg.
Original Pull Request: #2126
Closes #2125
2022-03-27 18:57:54 +02:00
Peter-Josef Meisch acd7990fac Default Refresh policy for ReactiveElasticsearchTemplate.
Original Pull Request #2124
Closes #2110
2022-03-24 21:07:55 +01:00
Mark Paluch b10eb75967 Update Jenkinsfile according to Java 17 build pipeline configuration.
See #2122
2022-03-24 09:29:06 +01:00
Mark Paluch a4952a90f8 After release cleanups.
See #2121
2022-03-22 14:07:39 +01:00
Mark Paluch 50d241fcb4 Prepare next development iteration.
See #2121
2022-03-22 14:07:37 +01:00
Mark Paluch d1412f06a4 Release version 5.0 M3 (2022.0.0).
See #2121
2022-03-22 14:00:24 +01:00
Mark Paluch 6bdeca64e2 Prepare 5.0 M3 (2022.0.0).
See #2121
2022-03-22 14:00:02 +01:00
Mark Paluch 9ef35ee46f After release cleanups.
See #2058
2022-03-21 16:44:42 +01:00
Mark Paluch 102d654b92 Prepare next development iteration.
See #2058
2022-03-21 16:44:40 +01:00
Mark Paluch 3c0528796b Release version 5.0 M2 (2022.0.0).
See #2058
2022-03-21 16:35:09 +01:00
Mark Paluch 6a976a666b Prepare 5.0 M2 (2022.0.0).
See #2058
2022-03-21 16:34:38 +01:00
Peter-Josef Meisch 57920d4e27 Merge branch 'main' into 5.0.x 2022-03-20 11:10:23 +01:00
Peter-Josef Meisch 6f6cedf6df Fix javadoc formatting 2022-03-19 20:50:18 +01:00
Peter-Josef Meisch f91a4c18c4 Merge branch 'main' into 5.0.x 2022-03-19 19:56:05 +01:00
Peter-Josef Meisch 80c3a3bbc5 Merge branch 'main' into 5.0.x
# Conflicts:
#	src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplate.java
#	src/test/java/org/springframework/data/elasticsearch/repositories/cdi/CdiRepositoryTests.java
2022-03-19 19:00:27 +01:00
Mark Paluch 126e2b972d Update CI properties.
See #2058
2022-03-17 18:39:19 +01:00
Peter-Josef Meisch 298d4ebce7 adapt to changes in spring data 2022-03-17 18:39:19 +01:00
Greg L. Turnquist a3414ff8e5 Update CI properties.
See #2058
2022-03-17 18:39:18 +01:00
Mark Paluch 7365111244 Polishing.
Refine build triggers.

See #2074
2022-03-17 18:39:16 +01:00
Greg L. Turnquist f70e4f817d Externalize build properties.
See #2074.
2022-03-17 18:38:58 +01:00
Peter-Josef Meisch 0ac56b511c adapt after rebase on master 2022-03-17 18:38:57 +01:00
Mark Paluch 6adae6f61b After release cleanups.
See #2053
2022-03-17 18:38:57 +01:00
Mark Paluch 6da0a78d99 Prepare next development iteration.
See #2053
2022-03-17 18:38:56 +01:00
Mark Paluch e856b974ad Release version 5.0 M1 (2022.0.0).
See #2053
2022-03-17 18:38:56 +01:00
Mark Paluch bb8e593546 Prepare 5.0 M1 (2022.0.0).
See #2053
2022-03-17 18:38:55 +01:00
Peter-Josef Meisch f0c359eb41 Branch 5.0.x. 2022-03-17 18:38:39 +01:00
734 changed files with 21512 additions and 7694 deletions
+1 -2
View File
@@ -9,8 +9,7 @@ Make sure that:
-->
- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).
- [ ] **There is a ticket in the bug tracker for the project in our [issue tracker](https://github.
com/spring-projects/spring-data-elasticsearch/issues)**. Add the issue number to the _Closes #issue-number_ line below
- [ ] **There is a ticket in the bug tracker for the project in our [issue tracker](https://github.com/spring-projects/spring-data-elasticsearch/issues)**. Add the issue number to the _Closes #issue-number_ line below
- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Dont submit any formatting related changes.
- [ ] You submit test cases (unit or integration tests) that back your changes.
- [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).
+2
View File
@@ -24,3 +24,5 @@ target
/zap.env
/localdocker.env
.localdocker-env
+2 -2
View File
@@ -1,3 +1,3 @@
#Tue Feb 22 13:59:12 CET 2022
#Thu Apr 06 16:16:37 CEST 2023
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip
+4 -1
View File
@@ -1,6 +1,9 @@
= Spring Data contribution guidelines
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc[here].
**Please read these carefully!**
Do not submit a Pull Request before having created an issue and having discussed it. This prevents you from doing work that might be rejected.
== Running the test locally
@@ -8,4 +11,4 @@ In order to run the tests locally with `./mvnw test` you need to have docker run
== Class names of the test classes
Tset classes that do depend on the client have either `ERHLC` (when using the deprecated Elasticsearch `RestHighLevelClient`) or `ELC` (the new `ElasticsearchClient`) in their name.
Test classes that do depend on the client have either `ERHLC` (when using the deprecated Elasticsearch `RestHighLevelClient`) or `ELC` (the new `ElasticsearchClient`) in their name.
Vendored
+23 -56
View File
@@ -1,7 +1,7 @@
def p = [:]
node {
checkout scm
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
checkout scm
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
}
pipeline {
@@ -20,8 +20,9 @@ pipeline {
stages {
stage("test: baseline (main)") {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -31,18 +32,14 @@ pipeline {
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
@@ -50,8 +47,9 @@ pipeline {
stage("Test other configurations") {
when {
beforeAgent(true)
allOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -61,44 +59,14 @@ pipeline {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
stage("test: baseline (LTS)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.lts.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
@@ -108,8 +76,9 @@ pipeline {
stage('Release to artifactory') {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -124,17 +93,15 @@ pipeline {
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
+26 -4
View File
@@ -12,13 +12,35 @@ This project is lead and maintained by the community.
== Features
* Spring configuration support using Java based `@Configuration` classes or an XML namespace for a ES clients instances.
* `ElasticsearchRestTemplate` helper class that increases productivity performing common ES operations.
* `ElasticsearchOperations` class and implementations that increases productivity performing common ES operations.
Includes integrated object mapping between documents and POJOs.
* Feature Rich Object Mapping integrated with Springs Conversion Service
* Annotation based mapping metadata
* Automatic implementation of `Repository` interfaces including support for custom search methods.
* CDI support for repositories
== About Elasticsearch versions and clients
=== Elasticsearch 7.17 client libraries
At the end of 2021 Elasticsearch with version 7.17 released the new version of their Java client and deprecated the `RestHighLevelCLient` which was the default way to access Elasticsearch up to then.
Spring Data Elasticsearch will in version 4.4 offer the possibility to optionally use the new client as an alternative to the existing setup using the `RestHighLevelCLient`.
The default client that is used still is the `RestHighLevelCLient`, first because the integration of the new client is not yet complete, the new client still has features missing and bugs which will hopefully be resolved soon.
Second, and more important, the new Elasticsearch client forces users to switch from using `javax.json.spi.JsonProvider` to `jakarta.json.spi.JsonProvider`.
Spring Data Elasticsearch cannot enforce this switch; Spring Boot will switch to `jakarta` with version 3 and then it's safe for Spring Data Elasticsearch to switch to the new client.
So for version 4.4 Spring Data Elasticsearch will keep using the `RestHighLevelCLient` in version 7.17.x (as long as this will be available).
=== Elasticsearch 8 client libraries
In Elasticsearch 8, the `RestHighLevelCLient` has been removed.
This means that a switch to this client version can only be done with the next major upgrade which will be Spring Data Elasticsearch 5, based on Spring Data 3, used by Spring Boot 3, based on Spring 6 and Java 17.
=== Elasticsearch 8 cluster
It should be possible to use the Elasticsearch 7 client to access a cluster running version 8 by setting the appropriate compatibility headers (see the documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration). but I encountered and heard of cases where the response from the server is not parseable by the client although the headers are set, so use with care.
== Code of Conduct
This project is governed by the https://github.com/spring-projects/.github/blob/e3cc2ff230d8f1dca06535aa6b5a4a23815861d4/CODE_OF_CONDUCT.md[Spring Code of Conduct].
@@ -147,7 +169,6 @@ Wed love to help!
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/elasticsearch/docs/current/api/[Javadocs].
* Learn the Spring basics Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation.
If you are just starting out with Spring, try one of the https://spring.io/guides[guides].
* If you are upgrading, check out the https://docs.spring.io/spring-data/elasticsearch/docs/current/changelog.txt[changelog] for "`new and noteworthy`" features.
* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-elasticsearch`].
You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter].
* Report bugs with Spring Data for Elasticsearch at https://github.com/spring-projects/spring-data-elasticsearch/issues[https://github.com/spring-projects/spring-data-elasticsearch/issues].
@@ -168,7 +189,9 @@ Attach a link to your code or a compressed file containing your code.
== Building from Source
You dont need to build from source to use Spring Data (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Data can be easily built with the https://github.com/takari/maven-wrapper[maven wrapper].
You also need JDK 1.8.
You need JDK 17 or above to build the _main_ branch.
For the branches up to and including release 4.4, JDK 8 is required.
[source,bash]
----
@@ -182,7 +205,6 @@ _Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull r
IMPORTANT: When contributing, please make sure an issue exists in https://github.com/spring-projects/spring-data-elasticsearch/issues[issue tracker] and comment on this issue with how you want to address it.
By this we not only know that someone is working on an issue, we can also align architectural questions and possible solutions before work is invested . We so can prevent that much work is put into Pull Requests that have little or no chances of being merged.
=== Building reference documentation
Building the documentation builds also the project without running tests.
+7 -9
View File
@@ -1,23 +1,21 @@
# Java versions
java.main.tag=8u322-b06-jdk
java.next.tag=11.0.14.1_1-jdk
java.lts.tag=17.0.2_8-jdk
java.main.tag=17.0.6_10-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}
docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.lts.tag}
# Supported versions of MongoDB
docker.mongodb.4.0.version=4.0.28
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
docker.mongodb.4.4.version=4.4.18
docker.mongodb.5.0.version=5.0.14
docker.mongodb.6.0.version=6.0.4
# Supported versions of Redis
docker.redis.6.version=6.2.6
docker.redis.6.version=6.2.10
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.12
docker.cassandra.3.version=3.11.14
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
+456 -488
View File
@@ -1,518 +1,486 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.4.0-SNAPSHOT</version>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>5.1.0</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.7.0-SNAPSHOT</version>
</parent>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.1.0</version>
</parent>
<name>Spring Data Elasticsearch</name>
<description>Spring Data Implementation for Elasticsearch</description>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<elasticsearch>7.17.1</elasticsearch>
<elasticsearch-java>7.17.1</elasticsearch-java>
<log4j>2.17.1</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>2.7.0-SNAPSHOT</springdata.commons>
<testcontainers>1.16.2</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<java-module-name>spring.data.elasticsearch</java-module-name>
<!--
properties defining the maven phase for the tests and integration tests
set to "none" to disable the corresponding test execution (-Dmvn.unit-test.goal=none)
default execution for unit-test: "test", for the integration tests: "integration-test"
-->
<mvn.unit-test.goal>test</mvn.unit-test.goal>
<mvn.integration-test-elasticsearch.goal>integration-test</mvn.integration-test-elasticsearch.goal>
<mvn.integration-test-opensearch.goal>none</mvn.integration-test-opensearch.goal>
</properties>
<developers>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
<timezone>+0</timezone>
</developer>
<developer>
<id>cstrobl</id>
<name>Christoph Strobl</name>
<email>cstrobl at pivotal.io</email>
<organization>Pivotal</organization>
<organizationUrl>https://www.pivotal.io</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>mpaluch</id>
<name>Mark Paluch</name>
<email>mpaluch at pivotal.io</email>
<organization>Pivotal</organization>
<organizationUrl>https://www.pivotal.io</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
</developers>
<scm>
<name>Spring Data Elasticsearch</name>
<description>Spring Data Implementation for Elasticsearch</description>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<connection>scm:git:git://github.com/spring-projects/spring-data-elasticsearch.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git
</developerConnection>
</scm>
<ciManagement>
<system>Bamboo</system>
<url>https://build.spring.io/browse/SPRINGDATAES</url>
</ciManagement>
<properties>
<springdata.commons>3.1.0</springdata.commons>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-data-elasticsearch/issues</url>
</issueManagement>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.9</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<elasticsearch-java>8.7.0</elasticsearch-java>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>${netty}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<log4j>2.18.0</log4j>
<!-- netty dependency can be removed once the WebClient code is gone -->
<netty>4.1.90.Final</netty>
<dependencies>
<blockhound-junit>1.0.8.RELEASE</blockhound-junit>
<hoverfly>0.14.4</hoverfly>
<jsonassert>1.5.1</jsonassert>
<testcontainers>1.18.0</testcontainers>
<wiremock>2.35.0</wiremock>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<java-module-name>spring.data.elasticsearch</java-module-name>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!--
properties defining the maven phase for the tests and integration tests
set to "none" to disable the corresponding test execution (-Dmvn.unit-test.goal=none)
default execution for unit-test: "test", for the integration tests: "integration-test"
-->
<mvn.unit-test.goal>test</mvn.unit-test.goal>
<mvn.integration-test-elasticsearch.goal>integration-test</mvn.integration-test-elasticsearch.goal>
</properties>
<!-- SPRING DATA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<developers>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
<timezone>+0</timezone>
</developer>
<developer>
<id>cstrobl</id>
<name>Christoph Strobl</name>
<email>cstrobl at pivotal.io</email>
<organization>Pivotal</organization>
<organizationUrl>https://www.pivotal.io</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>mpaluch</id>
<name>Mark Paluch</name>
<email>mpaluch at pivotal.io</email>
<organization>Pivotal</organization>
<organizationUrl>https://www.pivotal.io</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
</developers>
<!-- Reactive Infrastructure -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<optional>true</optional>
</dependency>
<scm>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<connection>scm:git:git://github.com/spring-projects/spring-data-elasticsearch.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git
</developerConnection>
</scm>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<optional>true</optional>
</dependency>
<ciManagement>
<system>Bamboo</system>
<url>https://build.spring.io/browse/SPRINGDATAES</url>
</ciManagement>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-data-elasticsearch/issues</url>
</issueManagement>
<!-- Elasticsearch RestHighLevelClient, will be removed probably in SDE 5 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- new Elasticsearch client, needs the low-level rest client and json api -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>${elasticsearch-java}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>${elasticsearch}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jcdi_2.0_spec</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>${javax-annotation-api}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-se</artifactId>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>${log4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound-junit-platform</artifactId>
<version>${blockhound-junit}</version>
<scope>test</scope>
</dependency>
<!--
we don't use lombok in Spring Data Elasticsearch anymore. But the dependency is set in the parent project, and so the
lombok compiler stuff is executed regardless of the fact that we don't need it.
On AdoptOpenJdk 16.0.0 this leads to an error, so the project does not build.
Therefore we replace lombok with a jar - that just contains an empty file - that lives in a local maven repository in
src/test/resources/local-maven-repo/
It was installed with
mvn deploy:deploy-file -DgroupId=org.projectlombok -DartifactId=lombok -Dversion=999999 -Durl=file:./src/test/resources/local-maven-repo/ -DrepositoryId=local-maven-repo -DupdateReleaseInfo=true -Dfile=path/to/empty.jar
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--suppress MavenPackageUpdate -->
<version>999999</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.32.0</version>
<scope>test</scope>
<exclusions>
<!-- these exclusions are needed because of Elasticsearch JarHell-->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.specto</groupId>
<artifactId>hoverfly-java-junit5</artifactId>
<version>0.14.1</version>
<scope>test</scope>
</dependency>
<!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions -->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-asm5-shaded</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>elasticsearch</artifactId>
<version>${testcontainers}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/versions.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/versions.properties</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>true</useSystemClassLoader>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
<include>**/*Test.java</include>
</includes>
<systemPropertyVariables>
<es.set.netty.runtime.available.processors>false</es.set.netty.runtime.available.processors>
</systemPropertyVariables>
</configuration>
<executions>
<!-- the default-test execution runs only the unit tests -->
<execution>
<id>default-test</id>
<phase>${mvn.unit-test.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludedGroups>integration-test</excludedGroups>
</configuration>
</execution>
<!-- execution to run the integration tests against Elasticsearch -->
<execution>
<id>integration-test-elasticsearch</id>
<phase>${mvn.integration-test-elasticsearch.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<groups>integration-test</groups>
<systemPropertyVariables>
<sde.integration-test.environment>elasticsearch</sde.integration-test.environment>
</systemPropertyVariables>
</configuration>
</execution>
<!-- execution to run the integration tests against Opensearch -->
<execution>
<id>integration-test-opensearch</id>
<phase>${mvn.integration-test-opensearch.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<groups>integration-test</groups>
<systemPropertyVariables>
<sde.integration-test.environment>opensearch</sde.integration-test.environment>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.5.2</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>0.12</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>${netty}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
<configuration>
<excludedGroups>integration-test</excludedGroups>
<targetClasses>
<param>org.springframework.data.elasticsearch.core.geo.*</param>
</targetClasses>
<excludedMethods>toString</excludedMethods>
</configuration>
</plugin>
</dependencyManagement>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!-- SPRING DATA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<!-- Reactive Infrastructure -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<!-- optional Elasticsearch RestHighLevelClient, deprecated in SDE 5.0 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch-rhlc}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- new Elasticsearch client, needs the low-level rest client and json api -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>${elasticsearch-java}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>${elasticsearch-java}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
<optional>true</optional>
</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-se</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>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-impl</artifactId>
<classifier>jakarta</classifier>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>${log4j}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound-junit-platform</artifactId>
<version>${blockhound-junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>${jsonassert}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>${wiremock}</version>
<scope>test</scope>
<exclusions>
<!-- these exclusions are needed because of Elasticsearch JarHell-->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.specto</groupId>
<artifactId>hoverfly-java-junit5</artifactId>
<version>${hoverfly}</version>
<scope>test</scope>
</dependency>
<!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions -->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-asm5-shaded</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>elasticsearch</artifactId>
<version>${testcontainers}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/versions.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/versions.properties</exclude>
</excludes>
</resource>
</resources>
<profiles>
<profile>
<id>ci</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<checkstyleRules>
<module name="Checker">
<!-- Configure checker to use UTF-8 encoding -->
<property name="charset" value="UTF-8"/>
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
</module>
</checkstyleRules>
<includes>**/*</includes>
<excludes>
.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy
</excludes>
<sourceDirectories>./</sourceDirectories>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>true</useSystemClassLoader>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
<include>**/*Test.java</include>
</includes>
<systemPropertyVariables>
<es.set.netty.runtime.available.processors>false</es.set.netty.runtime.available.processors>
</systemPropertyVariables>
</configuration>
<executions>
<!-- the default-test execution runs only the unit tests -->
<execution>
<id>default-test</id>
<phase>${mvn.unit-test.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludedGroups>integration-test</excludedGroups>
</configuration>
</execution>
<!-- execution to run the integration tests against Elasticsearch -->
<execution>
<id>integration-test-elasticsearch</id>
<phase>${mvn.integration-test-elasticsearch.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<groups>integration-test</groups>
<systemPropertyVariables>
<sde.integration-test.environment>elasticsearch</sde.integration-test.environment>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</build>
<profile>
<id>jdk13+</id>
<!-- on jDK13+, Blockhound needs this JVM flag set -->
<activation>
<jdk>[13,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<profiles>
<profile>
<id>ci</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<checkstyleRules>
<module name="Checker">
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<!-- Configure checker to use UTF-8 encoding -->
<property name="charset" value="UTF-8"/>
<repository>
<id>local-maven-repo</id>
<url>file:///${project.basedir}/src/test/resources/local-maven-repo</url>
</repository>
</repositories>
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
</module>
</checkstyleRules>
<includes>**/*</includes>
<excludes>
.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy
</excludes>
<sourceDirectories>./</sourceDirectories>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
<profile>
<id>jdk13+</id>
<!-- on jDK13+, Blockhound needs this JVM flag set -->
<activation>
<jdk>[13,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>local-maven-repo</id>
<url>file:///${project.basedir}/src/test/resources/local-maven-repo</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
</project>
+3 -1
View File
@@ -5,13 +5,15 @@ BioMed Central Development Team; Oliver Drotbohm; Greg Turnquist; Christoph Stro
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
(C) 2013-2021 The original author(s).
(C) 2013-2022 The original author(s).
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
toc::[]
include::preface.adoc[]
include::{spring-data-commons-docs}/upgrade.adoc[leveloffset=+1]
:leveloffset: +1
include::{spring-data-commons-docs}/repositories.adoc[]
+17 -11
View File
@@ -29,20 +29,26 @@ Requires an installation of https://www.elastic.co/products/elasticsearch[Elasti
[[preface.versions]]
=== Versions
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring Data release train:
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of
Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring
Data release train. The Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
built and tested.
[cols="^,^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
| 2022.0 (Raj) | 4.4.x | 7.17.1 | 5.3.x | 2.7.x
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
| 2021.0 (Pascal) | 4.2.x | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham)footnote:oom[Out of maintenance] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
| Neumannfootnote:oom[] | 4.0.xfootnote:oom[] | 7.6.2 | 5.2.12 |2.3.x
| Moorefootnote:oom[] | 3.2.xfootnote:oom[] |6.8.12 | 5.2.12| 2.2.x
| Lovelacefootnote:oom[] | 3.1.xfootnote:oom[] | 6.2.2 | 5.1.19 |2.1.x
| Kayfootnote:oom[] | 3.0.xfootnote:oom[] | 5.5.0 | 5.0.13 | 2.0.x
| Ingallsfootnote:oom[] | 2.1.xfootnote:oom[] | 2.4.0 | 4.3.25 | 1.5.x
| 2023.0 (Ullmann) | 5.1.x | 8.7.0 | 6.0.x | 3.1.x
| 2022.0 (Turing) | 5.0.x | 8.5.3 | 6.0.x | 3.0.x
| 2021.2 (Raj) | 4.4.xfootnote:oom[Out of maintenance] | 7.17.3 | 5.3.x | 2.7.x
| 2021.1 (Q) | 4.3.xfootnote:oom[] | 7.15.2 | 5.3.x | 2.6.x
| 2021.0 (Pascal) | 4.2.xfootnote:oom[] | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham) | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
| Neumann | 4.0.xfootnote:oom[] | 7.6.2 | 5.2.12 |2.3.x
| Moore | 3.2.xfootnote:oom[] |6.8.12 | 5.2.12| 2.2.x
| Lovelace | 3.1.xfootnote:oom[] | 6.2.2 | 5.1.19 |2.1.x
| Kay | 3.0.xfootnote:oom[] | 5.5.0 | 5.0.13 | 2.0.x
| Ingalls | 2.1.xfootnote:oom[] | 2.4.0 | 4.3.25 | 1.5.x
|===
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming the usage of the <<elasticsearch.clients.rest,high-level REST client>>.
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming
the usage of the <<elasticsearch.operations,ElasticsearchOperations interface>>.
@@ -1,6 +1,7 @@
[[elasticsearch.auditing]]
== Elasticsearch Auditing
[[elasticsearch.auditing.preparing]]
=== Preparing entities
In order for the auditing code to be able to decide whether an entity instance is new, the entity must implement the `Persistable<ID>` interface which is defined as follows:
@@ -54,6 +55,7 @@ public class Person implements Persistable<Long> {
<.> the getter is the required implementation from the interface
<.> an object is new if it either has no `id` or none of fields containing creation attributes are set.
[[elasticsearch.auditing.activating]]
=== Activating auditing
After the entities have been set up and providing the `AuditorAware` - or `ReactiveAuditorAware` - the Auditing must be activated by setting the `@EnableElasticsearchAuditing` on a configuration class:
@@ -3,18 +3,126 @@
This chapter illustrates configuration and usage of supported Elasticsearch client implementations.
Spring Data Elasticsearch operates upon an Elasticsearch client that is connected to a single Elasticsearch node or a cluster.
Although the Elasticsearch Client can be used to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is
connected to a single Elasticsearch node or a cluster.
Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data
Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
[[elasticsearch.clients.rest]]
== High Level REST Client
[[elasticsearch.clients.restclient]]
== Imperative Rest Client
The Java High Level REST Client is the default client of Elasticsearch, it is configured like shown:
To use the imperative (non-reactive) client, a configuration bean must be configured like this:
.High Level REST Client
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
@Configuration
public class MyClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() <.>
.connectedTo("localhost:9200")
.build();
}
}
----
<.> for a detailed description of the builder methods see <<elasticsearch.clients.configuration>>
====
The following beans can then be injected in other Spring components:
====
[source,java]
----
@Autowired
ElasticsearchOperations operations; <.>
@Autowired
ElasticsearchClient elasticsearchClient; <.>
@Autowired
RestClient restClient; <.>
----
<.> an implementation of `ElasticsearchOperations`
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
<.> the low level `RestClient` from the Elasticsearch libraries
====
Basically one should just use the `ElasticsearchOperations` to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.reactiverestclient]]
== Reactive Rest Client
When working with the reactive stack, the configuration must be derived from a different class:
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
@Configuration
public class MyClientConfig extends ReactiveElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() <.>
.connectedTo("localhost:9200")
.build();
}
}
----
<.> for a detailed description of the builder methods see <<elasticsearch.clients.configuration>>
====
The following beans can then be injected in other Spring components:
====
[source,java]
----
@Autowired
ReactiveElasticsearchOperations operations; <.>
@Autowired
ReactiveElasticsearchClient elasticsearchClient; <.>
@Autowired
RestClient restClient; <.>
----
the following can be injected:
<.> an implementation of `ReactiveElasticsearchOperations`
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
This is a reactive implementation based on the Elasticsearch client implementation.
<.> the low level `RestClient` from the Elasticsearch libraries
====
Basically one should just use the `ReactiveElasticsearchOperations` to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.resthighlevelclient]]
== High Level REST Client (deprecated)
[CAUTION]
====
The Elasticsearch Java RestHighLevelClient is deprecated, but still can be configured like shown (make sure to read
<<elasticsearch-migration-guide-4.4-5.0.old-client>> as well).
It should only be used to access an Elasticsearch cluster running version 7, even with the compatibility headers set
there are cases where the `RestHighLevelClient` cannot read the responses sent from a version 8 cluster.
====
.RestHighLevelClient
====
[source,java]
----
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@@ -36,15 +144,6 @@ public class RestClientConfig extends AbstractElasticsearchConfiguration {
RestHighLevelClient highLevelClient;
RestClient lowLevelClient = highLevelClient.lowLevelClient(); <3>
// ...
IndexRequest request = new IndexRequest("spring-data")
.id(randomID())
.source(singletonMap("feature", "high-level-rest-client"))
.setRefreshPolicy(IMMEDIATE);
IndexResponse response = highLevelClient.index(request,RequestOptions.DEFAULT);
----
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
@@ -53,16 +152,25 @@ IndexResponse response = highLevelClient.index(request,RequestOptions.DEFAULT);
====
[[elasticsearch.clients.reactive]]
== Reactive Client
== Reactive Client (deprecated)
The `ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
The `org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
It uses the request/response objects provided by the Elasticsearch core project.
Calls are directly operated on the reactive stack, **not** wrapping async (thread pool bound) responses into reactive types.
.Reactive REST Client
[CAUTION]
====
This was the first reactive implementation Spring Data Elasticsearch provided, but now is deprecated in favour
of the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient`
which uses the functionality offered by the new Elasticsearch client libraries.
====
.Reactive REST Client (deprecated)
====
[source,java]
----
import org.springframework.data.elasticsearch.client.erhlc.AbstractReactiveElasticsearchConfiguration;
@Configuration
public class ReactiveRestClientConfig extends AbstractReactiveElasticsearchConfiguration {
@@ -89,8 +197,6 @@ Mono<IndexResponse> response = client.index(request ->
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
====
NOTE: The ReactiveClient response, especially for search operations, is bound to the `from` (offset) & `size` (limit) options of the request.
[[elasticsearch.clients.configuration]]
== Client Configuration
@@ -100,6 +206,11 @@ Client behaviour can be changed via the `ClientConfiguration` that allows to set
====
[source,java]
----
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import static org.springframework.data.elasticsearch.client.elc.ElasticsearchClients.*;
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("some-header", "on every request") <.>
@@ -118,12 +229,7 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
return headers;
})
.withClientConfigurer( <.>
ReactiveRestClients.WebClientConfigurationCallback.from(webClient -> {
// ...
return webClient;
}))
.withClientConfigurer( <.>
RestClients.RestClientConfigurationCallback.from(clientBuilder -> {
ElasticsearchClientConfigurationCallback.from(clientBuilder -> {
// ...
return clientBuilder;
}))
@@ -134,31 +240,76 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
<.> Define default headers, if they need to be customized
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
<.> Optionally enable SSL.
<.> Optionally enable SSL. There exist overloads of this function that can take a `SSLContext` or as an alternative the fingerprint of the certificate as it is output by Elasticsearch 8 on startup.
<.> Optionally set a proxy.
<.> Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
<.> Set the connection timeout.
Default is 10 sec.
<.> Set the socket timeout.
Default is 5 sec.
<.> Optionally set headers.
<.> Add basic authentication.
<.> A `Supplier<Header>` function can be specified which is called every time before a request is sent to Elasticsearch - here, as an example, the current time is written in a header.
<.> for reactive setup a function configuring the `WebClient`
<.> for non-reactive setup a function configuring the REST client
<.> A `Supplier<HttpHeaders>` function can be specified which is called every time before a request is sent to Elasticsearch - here, as an example, the current time is written in a header.
<.> a function to configure the created client (see <<elasticsearch.clients.configuration.callbacks>>), can be added
multiple times.
====
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens.
If this is used in the reactive setup, the supplier function *must not* block!
[[elasticsearch.clients.configuration.callbacks]]
=== Client configuration callbacks
The `ClientConfiguration` class offers the most common parameters to configure the client. In the case this is not
enough, the user can add callback functions by using the `withClientConfigurer(ClientConfigurationCallback<?>)` method.
The following callbacks are provided:
[[elasticsearch.clients.configuration.callbacks.rest]]
==== Configuration of the low level Elasticsearch `RestClient`:
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
`RestClient`:
====
[source,java]
----
ClientConfiguration.builder()
.withClientConfigurer(ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
// configure the Elasticsearch RestClient
return restClientBuilder;
}))
.build();
----
====
[[elasticsearch.clients.configurationcallbacks.httpasync]]
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `RestClient`:
This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpCLient that is
used by the `RestClient`.
====
[source,java]
----
ClientConfiguration.builder()
.withClientConfigurer(ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
// configure the HttpAsyncClient
return httpAsyncClientBuilder;
}))
.build();
----
====
[[elasticsearch.clients.configuration.headers]]
=== Elasticsearch 7 compatibility headers
When using Spring Data Elasticsearch 4 - which uses the Elasticsearch 7 client libraries - and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
When using the deprecated `RestHighLevelClient` and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html[see Elasticsearch
documentation].
For the imperative client this must be done by setting the default headers, for the reactive code this must be done using a header supplier:
CAUTION: Even when these headers are set, there are cases where the response returned from the cluster cannot be
parsed with the client. This is not an error in Spring Data Elasticsearch.
====
[source,java]
----
@@ -182,12 +333,13 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
[[elasticsearch.clients.logging]]
== Client Logging
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level needs to be turned on as outlined in the snippet below.
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level
needs to be turned on as outlined in the snippet below. This can be enabled in the Elasticsearch client by setting
the level of the `tracer` package to "trace" (see
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-usage-logging.html)
.Enable transport layer logging
[source,xml]
----
<logger name="org.springframework.data.elasticsearch.client.WIRE" level="trace"/>
<logger name="tracer" level="trace"/>
----
NOTE: The above applies to both the `RestHighLevelClient` and `ReactiveElasticsearchClient` when obtained via `RestClients` respectively `ReactiveRestClients`.
@@ -3,6 +3,7 @@
Spring Data Elasticsearch supports the https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html[Join data type] for creating the corresponding index mappings and for storing the relevant information.
[[elasticsearch.jointype.setting-up]]
== Setting up the data
For an entity to be used in a parent child join relationship, it must have a property of type `JoinField` which must be annotated.
@@ -160,6 +161,7 @@ Spring Data Elasticsearch will build the following mapping for this class:
----
====
[[elasticsearch.jointype.storing]]
== Storing data
Given a repository for this class the following code inserts a question, two answers, a comment and a vote:
@@ -209,9 +211,10 @@ void init() {
<5> a vote for the first answer, this needs to have the routing set to the weather document, see <<elasticsearch.routing>>.
====
[[elasticsearch.jointype.retrieving]]
== Retrieving data
Currently native search queries must be used to query the data, so there is no support from standard repository methods. <<repositories.custom-implementations>> can be used instead.
Currently native queries must be used to query the data, so there is no support from standard repository methods. <<repositories.custom-implementations>> can be used instead.
The following code shows as an example how to retrieve all entries that have a _vote_ (which must be _answers_, because only answers can have a vote) using an `ElasticsearchOperations` instance:
@@ -219,11 +222,17 @@ The following code shows as an example how to retrieve all entries that have a _
[source,java]
----
SearchHits<Statement> hasVotes() {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(hasChildQuery("vote", matchAllQuery(), ScoreMode.None))
.build();
return operations.search(query, Statement.class);
Query query = NativeQuery.builder()
.withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query.of(qb -> qb
.hasChild(hc -> hc
.queryName("vote")
.query(matchAllQueryAsQuery())
.scoreMode(ChildScoreMode.None)
)))
.build();
return operations.search(query, Statement.class);
}
----
====
@@ -95,6 +95,7 @@ default boolean createIndex(String indexName) {
[[elasticsearch-migration-guide-3.2-4.0.deprecations]]
== Deprecations
[[elasticsearch-migration-guide-3.2-4.0.deprecations.methods-classes]]
=== Methods and classes
Many functions and classes have been deprecated. These functions still work, but the Javadocs show with what they should be replaced.
@@ -115,6 +116,7 @@ Many functions and classes have been deprecated. These functions still work, but
<T> T queryForObject(GetQuery query, Class<T> clazz);
----
[[elasticsearch-migration-guide-3.2-4.0.deprecations.elasticsearch]]
=== Elasticsearch deprecations
Since version 7 the Elasticsearch `TransportClient` is deprecated, it will be removed with Elasticsearch version 8. Spring Data Elasticsearch deprecates the `ElasticsearchTemplate` class which uses the `TransportClient` in version 4.0.
@@ -9,7 +9,7 @@ This section describes breaking changes from version 4.0.x to 4.1.x and how remo
.Definition of the id property
It is possible to define a property of en entity as the id property by naming it either `id` or `document`.
This behaviour is now deprecated and will produce a warning.
PLease us the `@Id` annotation to mark a property as being the id property.
Please use the `@Id` annotation to mark a property as being the id property.
.Index mappings
In the `ReactiveElasticsearchClient.Indices` interface the `updateMapping` methods are deprecated in favour of the `putMapping` methods.
@@ -32,6 +32,7 @@ They had been deprecated in Spring Data Elasticsearch 4.0 and their values weren
[[elasticsearch-migration-guide-4.0-4.1.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-4.0-4.1.breaking-changes.returntypes-1]]
=== Return types of ReactiveElasticsearchClient.Indices methods
The methods in the `ReactiveElasticsearchClient.Indices` were not used up to now.
@@ -40,7 +41,8 @@ With the introduction of the `ReactiveIndexOperations` it became necessary to ch
* the `createIndex` variants now return a `Mono<Boolean>` instead of a `Mono<Void>` to signal successful index creation.
* the `updateMapping` variants now return a `Mono<Boolean>` instead of a `Mono<Void>` to signal successful mappings storage.
=== Return types of DocumentOperartions.bulkIndex methods
[[elasticsearch-migration-guide-4.0-4.1.breaking-changes.returntypes-2]]
=== Return types of DocumentOperations.bulkIndex methods
These methods were returing a `List<String>` containing the ids of the new indexed records.
These methods were returning a `List<String>` containing the ids of the new indexed records.
Now they return a `List<IndexedObjectInformation>`; these objects contain the id and information about optimistic locking (seq_no and primary_term)
@@ -6,6 +6,7 @@ This section describes breaking changes from version 4.1.x to 4.2.x and how remo
[[elasticsearch-migration-guide-4.1-4.2.deprecations]]
== Deprecations
[[elasticsearch-migration-guide-4.1-4.2.deprecations.document]]
=== @Document parameters
The parameters of the `@Document` annotation that are relevant for the index settings (`useServerConfiguration`, `shards`. `replicas`, `refreshIntervall` and `indexStoretype`) have been moved to the `@Setting` annotation. Use in `@Document` is still possible but deprecated.
@@ -14,7 +15,7 @@ The parameters of the `@Document` annotation that are relevant for the index se
== Removals
The `@Score` annotation that was used to set the score return value in an entity was deprecated in version 4.0 and has been removed.
Scroe values are returned in the `SearchHit` instances that encapsulate the returned entities.
Score values are returned in the `SearchHit` instances that encapsulate the returned entities.
The `org.springframework.data.elasticsearch.ElasticsearchException` class has been removed.
The remaining usages have been replaced with `org.springframework.data.mapping.MappingException` and `org.springframework.dao.InvalidDataAccessApiUsageException`.
@@ -28,8 +29,10 @@ The deprecated `find` methods from `ReactiveSearchOperations` and `ReactiveDocum
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.refresh-policy]]
=== RefreshPolicy
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.refresh-policy.enum]]
==== Enum package changed
It was possible in 4.1 to configure the refresh policy for the `ReactiveElasticsearchTemplate` by overriding the method `AbstractReactiveElasticsearchConfiguration.refreshPolicy()` in a custom configuration class.
@@ -38,6 +41,7 @@ The return value of this method was an instance of the class `org.elasticsearch.
Now the configuration must return `org.springframework.data.elasticsearch.core.RefreshPolicy`.
This enum has the same values and triggers the same behaviour as before, so only the `import` statement has to be adjusted.
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.refresh-policy.behaviour]]
==== Refresh behaviour
`ElasticsearchOperations` and `ReactiveElasticsearchOperations` now explicitly use the `RefreshPolicy` set on the template for write requests if not null.
@@ -47,17 +51,21 @@ The provided implementations for `ElasticsearchRepository` and `ReactiveElastics
This is the same behaviour as in previous versions.
If a refresh policy is set, then it will be used by the repositories as well.
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.refresh-policy.configuration]]
==== Refresh configuration
When configuring Spring Data Elasticsearch like described in <<elasticsearch.clients>> by using `ElasticsearchConfigurationSupport`, `AbstractElasticsearchConfiguration` or `AbstractReactiveElasticsearchConfiguration` the refresh policy will be initialized to `null`.
Previously the reactive code initialized this to `IMMEDIATE`, now reactive and non-reactive code show the same behaviour.
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.method-return-types]]
=== Method return types
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.method-return-types.delete]]
==== delete methods that take a Query
The reactive methods previously returned a `Mono<Long>` with the number of deleted documents, the non reactive versions were void. They now return a `Mono<ByQueryResponse>` which contains much more detailed information about the deleted documents and errors that might have occurred.
[[elasticsearch-migration-guide-4.1-4.2.breaking-changes.method-return-types.multiget]]
==== multiget methods
The implementations of _multiget_ previousl only returned the found entities in a `List<T>` for non-reactive implementations and in a `Flux<T>` for reactive implementations. If the request contained ids that were not found, the information that these are missing was not available. The user needed to compare the returned ids to the requested ones to find
@@ -21,6 +21,7 @@ Check the sections on <<elasticsearch-migration-guide-4.2-4.3.deprecations>> and
[[elasticsearch-migration-guide-4.2-4.3.deprecations]]
== Deprecations
[[elasticsearch-migration-guide-4.2-4.3.deprecations.suggest]]
=== suggest methods
In `SearchOperations`, and so in `ElasticsearchOperations` as well, the `suggest` methods taking a `org.elasticsearch.search.suggest.SuggestBuilder` as argument and returning a `org.elasticsearch.action.search.SearchResponse` have been deprecated.
@@ -32,6 +33,7 @@ Here as well the old methods are deprecated.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.1]]
=== Removal of `org.elasticsearch` classes from the API.
* In the `org.springframework.data.elasticsearch.annotations.CompletionContext` annotation the property `type()` has changed from `org.elasticsearch.search.suggest.completion.context.ContextMapping.Type` to `org.springframework.data.elasticsearch.annotations.CompletionContext.ContextMappingType`, the available enum values are the same.
@@ -46,6 +48,7 @@ The same change has been done to the `ReactiveSearchOperations.aggregate()` func
Programs using the aggregations need to be changed to cast the returned value to the appropriate class to further proces it.
* methods that might have thrown a `org.elasticsearch.ElasticsearchStatusException` now will throw `org.springframework.data.elasticsearch.RestStatusException` instead.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.2]]
=== Handling of field and sourceFilter properties of Query
Up to version 4.2 the `fields` property of a `Query` was interpreted and added to the include list of the `sourceFilter`.
@@ -53,11 +56,13 @@ This was not correct, as these are different things for Elasticsearch.
This has been corrected.
As a consequence code might not work anymore that relies on using `fields` to specify which fields should be returned from the document's `_source' and should be changed to use the `sourceFilter`.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.3]]
=== search_type default value
The default value for the `search_type` in Elasticsearch is `query_then_fetch`.
This now is also set as default value in the `Query` implementations, it was previously set to `dfs_query_then_fetch`.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.4]]
=== BulkOptions changes
Some properties of the `org.springframework.data.elasticsearch.core.query.BulkOptions` class have changed their type:
@@ -65,14 +70,17 @@ Some properties of the `org.springframework.data.elasticsearch.core.query.BulkOp
* the type of the `timeout` property has been changed to `java.time.Duration`.
* the type of the`refreshPolicy` property has been changed to `org.springframework.data.elasticsearch.core.RefreshPolicy`.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.5]]
=== IndicesOptions change
Spring Data Elasticsearch now uses `org.springframework.data.elasticsearch.core.query.IndicesOptions` instead of `org.elasticsearch.action.support.IndicesOptions`.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.6]]
=== Completion classes
The classes from the package `org.springframework.data.elasticsearch.core.completion` have been moved to `org.springframework.data.elasticsearch.core.suggest`.
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes.7]]
=== Other renamings
The `org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter` interface has been renamed to `org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter`.
@@ -6,6 +6,7 @@ This section describes breaking changes from version 4.3.x to 4.4.x and how remo
[[elasticsearch-migration-guide-4.3-4.4.deprecations]]
== Deprecations
[[elasticsearch-migration-guide-4.3-4.4.deprecations.reactive-operations]]
=== org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations
The method `<T> Publisher<T> execute(ClientCallback<Publisher<T>> callback)` has been deprecated.
@@ -14,6 +15,7 @@ As there now are multiple implementations using different client libraries the `
[[elasticsearch-migration-guide-4.3-4.4.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-4.3-4.4.breaking-changes.1]]
=== Removal of deprecated classes
==== `org.springframework.data.elasticsearch.core.ElasticsearchTemplate` has been removed
@@ -23,11 +25,21 @@ This means that the `org.springframework.data.elasticsearch.core.ElasticsearchTe
This was the implementation of the `ElasticsearchOperations` interface that was using the `TransportClient`.
Connections to Elasticsearch must be made using either the imperative `ElasticsearchRestTemplate` or the reactive `ReactiveElasticsearchTemplate`.
[[elasticsearch-migration-guide-4.3-4.4.breaking-changes.2]]
=== Package changes
In 4.3 two classes (`ElasticsearchAggregations` and `ElasticsearchAggregation`) had been moved to the `org.springframework.data.elasticsearch.core.clients.elasticsearch7` package in preparation for the integration of the new Elasticsearch client.
The were moved back to the `org.springframework.data.elasticsearch.core` package as we keep the classes use the old Elasticsearch client where they were.
[[elasticsearch-migration-guide-4.3-4.4.breaking-changes.3]]
=== Behaviour change
The `ReactiveElasticsearchTemplate`, when created directly or by Spring Boot configuration had a default refresh policy of IMMEDIATE.
This could cause performance issues on heavy indexing and was different than the default behaviour of Elasticsearch.
This has been changed to that now the default refresh policy is NONE.
When the
`ReactiveElasticsearchTemplate` was provided by using the configuration like described in <<elasticsearch.clients.reactive>> the default refresh policy already was set to NONE.
[[elasticsearch-migration-guide-4.3-4.4.new-clients]]
== New Elasticsearch client
@@ -38,6 +50,7 @@ Spring Data Elasticsearch 4.4 still uses the old client as the default client fo
* There are still some bugs in the Elasticsearch client which need to be resolved
* The implementation using the new client in Spring Data Elasticsearch is not yet complete, due to limited resources working on that - remember Spring Data Elasticsearch is a community driven project that lives from public contributions.
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to]]
=== How to use the new client
CAUTION: The implementation using the new client is not complete, some operations will throw a `java.lang.UnsupportedOperationException` or might throw NPE (for example when the Elasticsearch cannot parse a response from the server, this still happens sometimes) +
@@ -45,6 +58,7 @@ Use the new client to test the implementations but do not use it in productive c
In order to try and use the new client the following steps are necessary:
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to.not]]
==== Make sure not to configure the existing default client
If using Spring Boot, exclude Spring Data Elasticsearch from the autoconfiguration
@@ -61,8 +75,9 @@ public class SpringdataElasticTestApplication {
====
Remove Spring Data Elasticsearch related properties from your application configuration.
If Spring Data Elasticsearch was configured using a programmatic configuration (see <<elastisearch.clients>>), remove these beans from the Spring application context.
If Spring Data Elasticsearch was configured using a programmatic configuration (see <<elasticsearch.clients>>), remove these beans from the Spring application context.
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to.dependencies]]
==== Add dependencies
The dependencies for the new Elasticsearch client are still optional in Spring Data Elasticsearch so they need to be added explicitly:
@@ -74,7 +89,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.1</version>
<version>7.17.3</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -85,7 +100,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>7.17.1</version>
<version>7.17.3</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -108,8 +123,10 @@ When using Spring Boot, it is necessary to set the following property in the _po
----
====
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to.configuration]]
==== New configuration classes
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to.configuration.imperative]]
===== Imperative style
In order configure Spring Data Elasticsearch to use the new client, it is necessary to create a configuration bean that derives from `org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration`:
@@ -137,6 +154,7 @@ With this configuration, the following beans will be available in the Spring app
* an `ElasticsearchClient` bean, this is the new client that uses the `RestClient`
* an `ElasticsearchOperations` bean, available with the bean names _elasticsearchOperations_ and _elasticsearchTemplate_, this uses the `ElasticsearchClient`
[[elasticsearch-migration-guide-4.3-4.4.new-clients.how-to.configuration.reactive]]
===== Reactive style
To use the new client in a reactive environment the only difference is the class from which to derive the configuration:
@@ -0,0 +1,168 @@
[[elasticsearch-migration-guide-4.4-5.0]]
= Upgrading from 4.4.x to 5.0.x
This section describes breaking changes from version 4.4.x to 5.0.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-4.4-4.5.deprecations]]
== Deprecations
=== Custom trace level logging
Logging by setting the property `logging.level.org.springframework.data.elasticsearch.client.WIRE=trace` is
deprecated now, the Elasticsearch `RestClient` provides a better solution that can be activated by setting the
logging level of the `tracer` package to "trace".
[[elasticsearch-migration-guide-4.4-4.5.deprecations.package]]
=== `org.springframework.data.elasticsearch.client.erhlc` package
See <<elasticsearch-migration-guide-4.4-5.0.breaking-changes-packages>>, all classes in this package have been deprecated, as the default client implementations to use are the ones based on the new Java Client from Elasticsearch, see <<elasticsearch-migration-guide-4.4-5.0.new-clients>>
[[elasticsearch-migration-guide-4.4-4.5.deprecations.code]]
=== Removal of deprecated code
`DateFormat.none` and `DateFormat.custom` had been deprecated since version 4.2 and have been removed.
The properties of `@Document` that were deprecated since 4.2 have been removed.
Use the `@Settings` annotation for these.
`@DynamicMapping` and `@DynamicMappingValue` have been removed.
Use `@Document.dynamic` or `@Field.dynamic` instead.
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes.deprecated-calls]]
=== Removal of deprecated calls
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes.deprecated-calls.1]]
==== suggest calls in operations interfaces have been removed
Both `SearchOperations` and `ReactiveSearchOperations` had deprecated calls that were using Elasticsearch classes as parameters.
These now have been removed and so the dependency on Elasticsearch classes in these APIs has been cleaned.
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes-packages]]
=== Package changes
All the classes that are using or depend on the deprecated Elasticsearch `RestHighLevelClient` have been moved to the package `org.springframework.data.elasticsearch.client.erhlc`.
By this change we now have a clear separation of code using the old deprecated Elasticsearch libraries, code using the new Elasticsearch client and code that is independent of the client implementation.
Also the reactive implementation that was provided up to now has been moved here, as this implementation contains code that was copied and adapted from Elasticsearch libraries.
If you are using `ElasticsearchRestTemplate` directly and not the `ElasticsearchOperations` interface you'll need to adjust your imports as well.
When working with the `NativeSearchQuery` class, you'll need to switch to the `NativeQuery` class, which can take a
`Query` instance comign from the new Elasticsearch client libraries.
You'll find plenty of examples in the test code.
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes-records]]
=== Conversion to Java 17 records
The following classes have been converted to `Record`, you might need to adjust the use of getter methods from
`getProp()` to `prop()`:
* `org.springframework.data.elasticsearch.core.AbstractReactiveElasticsearchTemplate.IndexResponseMetaData`
* `org.springframework.data.elasticsearch.core.ActiveShardCount`
* `org.springframework.data.elasticsearch.support.Version`
* `org.springframework.data.elasticsearch.support.ScoreDoc`
* `org.springframework.data.elasticsearch.core.query.ScriptData`
* `org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm`
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes-http-headers]]
=== New HttpHeaders class
Until version 4.4 the client configuration used the `HttpHeaders` class from the `org.springframework:spring-web`
project.
This introduces a dependency on that artifact.
Users that do not use spring-web then face an error as this class cannot be found.
In version 5.0 we introduce our own `HttpHeaders` to configure the clients.
So if you are using headers in the client configuration, you need to replace `org.springframework.http.HttpHeaders`
with `org.springframework.data.elasticsearch.support.HttpHeaders`.
Hint: You can pass a `org.springframework.http
.HttpHeaders` to the `addAll()` method of `org.springframework.data.elasticsearch.support.HttpHeaders`.
[[elasticsearch-migration-guide-4.4-5.0.new-clients]]
== New Elasticsearch client
Spring Data Elasticsearch now uses the new `ElasticsearchClient` and has deprecated the use of the previous `RestHighLevelClient`.
[[elasticsearch-migration-guide-4.4-5.0.new-clients.imperative]]
=== Imperative style configuration
To configure Spring Data Elasticsearch to use the new client, it is necessary to create a configuration bean that derives from `org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration`:
====
[source,java]
----
@Configuration
public class NewRestClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
----
====
The configuration is done in the same way as with the old client, but it is not necessary anymore to create more than the configuration bean.
With this configuration, the following beans will be available in the Spring application context:
* a `RestClient` bean, that is the configured low level `RestClient` that is used by the Elasticsearch client
* an `ElasticsearchClient` bean, this is the new client that uses the `RestClient`
* an `ElasticsearchOperations` bean, available with the bean names _elasticsearchOperations_ and _elasticsearchTemplate_, this uses the `ElasticsearchClient`
[[elasticsearch-migration-guide-4.4-5.0.new-clients.reactive]]
=== Reactive style configuration
To use the new client in a reactive environment the only difference is the class from which to derive the configuration:
====
[source,java]
----
@Configuration
public class NewRestClientConfig extends ReactiveElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
----
====
With this configuration, the following beans will be available in the Spring application context:
* a `RestClient` bean, that is the configured low level `RestClient` that is used by the Elasticsearch client
* an `ReactiveElasticsearchClient` bean, this is the new reactive client that uses the `RestClient`
* an `ReactiveElasticsearchOperations` bean, available with the bean names _reactiveElasticsearchOperations_ and _reactiveElasticsearchTemplate_, this uses the `ReactiveElasticsearchClient`
[[elasticsearch-migration-guide-4.4-5.0.old-client]]
=== Still want to use the old client?
The old deprecated `RestHighLevelClient` can still be used, but you will need to add the dependency explicitly to your application as Spring Data Elasticsearch does not pull it in automatically anymore:
====
[source,xml]
----
<!-- include the RHLC, specify version explicitly -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.5</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
----
====
Make sure to specify the version 7.17.6 explicitly, otherwise maven will resolve to 8.5.0, and this does not exist.
@@ -0,0 +1,26 @@
[[elasticsearch-migration-guide-5.0-5.1]]
= Upgrading from 5.0.x to 5.1.x
This section describes breaking changes from version 5.0.x to 5.1.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-5.0-5.1.breaking-changes]]
== Breaking Changes
In the `org.springframework.data.elasticsearch.core.index.AliasData` class, which is used for alias information returned from Elasticsearch, the property `filter` (of type `Document`) is replaced by `filterQuery` which is of type
`org.springframework.data.elasticsearch.core.query.Query`.
`org.springframework.data.elasticsearch.annotations.Similarity` was an enum class until 5.1. This enum was used in the `@Field` annotation to specify a similarity value.
But besides the values defined by the enum, it is possible to have similarities with custom names in Elasticsearch.
Therefore, the annotation property was changed from the type of the enum to a simple `String`.
The previous enum values like `Similarity.Default` do still exist as String constants, so existing code will compile unmodified.
Adaptions are necessary when this enum was used at other places than as a property of the `@Field` annotation.
[[elasticsearch-migration-guide-5.0-5.1.deprecations]]
== Deprecations
=== template functions
The functions in the `IndexOperations` and `ReactiverIndexOperations` to manage index templates that were introduced in Spring Data Elasticsearch 4.1
have been deprecated. They were using the old Elasticsearch API that was deprecated in Elasticsearch version 7.8.
Please use the new functions that are based on the compsable index template API instead.
@@ -17,7 +17,6 @@ The following arguments are available:
* `refreshIntervall`, defaults to _"1s"_
* `indexStoreType`, defaults to _"fs"_
It is as well possible to define https://www.elastic.co/guide/en/elasticsearch/reference/7.11/index-modules-index-sorting.html[index sorting] (check the linked Elasticsearch documentation for the possible field types and values):
====
@@ -87,12 +86,19 @@ private ElasticsearchOperations operations;
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(boolFilter().must(termFilter("id", documentId)))
.build();
Query query = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFilter( q -> q
.bool(b -> b
.must(m -> m
.term(t -> t
.field("id")
.value(documentId))
)))
.build();
Page<SampleEntity> sampleEntities = operations.searchForPage(searchQuery, SampleEntity.class, index);
SearchHits<SampleEntity> sampleEntities = operations.search(query, SampleEntity.class, index);
----
====
@@ -107,13 +113,15 @@ This is internally used by Spring Data Elasticsearch to provide the implementati
----
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
Query searchQuery = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
SearchHitsIterator<SampleEntity> stream = elasticsearchTemplate.searchForStream(searchQuery, SampleEntity.class, index);
SearchHitsIterator<SampleEntity> stream = elasticsearchOperations.searchForStream(searchQuery, SampleEntity.class,
index);
List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {
@@ -124,23 +132,26 @@ stream.close();
----
====
There are no methods in the `SearchOperations` API to access the scroll id, if it should be necessary to access this, the following methods of the `ElasticsearchRestTemplate` can be used:
There are no methods in the `SearchOperations` API to access the scroll id, if it should be necessary to access this, the following methods of the `AbstractElasticsearchTemplate` can be used (this is the base implementation for the different `ElasticsearchOperations` implementations):
====
[source,java]
----
@Autowired ElasticsearchRestTemplate template;
@Autowired ElasticsearchOperations operations;
AbstractElasticsearchTemplate template = (AbstractElasticsearchTemplate)operations;
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
Query query = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
SearchScrollHits<SampleEntity> scroll = template.searchScrollStart(1000, searchQuery, SampleEntity.class, index);
SearchScrollHits<SampleEntity> scroll = template.searchScrollStart(1000, query, SampleEntity.class, index);
String scrollId = scroll.getScrollId();
List<SampleEntity> sampleEntities = new ArrayList<>();
@@ -190,6 +201,7 @@ Sort.by(new GeoDistanceOrder("location", new GeoPoint(48.137154, 11.5761247)))
From version 7.12 on Elasticsearch has added the feature of runtime fields (https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime.html).
Spring Data Elasticsearch supports this in two ways:
[[elasticsearch.misc.runtime-fields.index-mappings]]
=== Runtime field definitions in the index mappings
The first way to define runtime fields is by adding the definitions to the index mappings (see https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-mapping-fields.html).
@@ -224,6 +236,7 @@ public class RuntimeFieldEntity {
----
====
[[elasticsearch.misc.runtime-fields.query]]
=== Runtime fields definitions set on a Query
The second way to define runtime fields is by adding the definitions to a search query (see https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-search-request.html).
@@ -261,3 +274,148 @@ SearchHits<SomethingToBuy> searchHits = operations.search(query, SomethingToBuy.
====
This works with every implementation of the `Query` interface.
[[elasticsearch.misc.point-in-time]]
== Point In Time (PIT) API
`ElasticsearchOperations` supports the point in time API of Elasticsearch (see https://www.elastic.co/guide/en/elasticsearch/reference/8.3/point-in-time-api.html).
The following code snippet shows how to use this feature with a fictional `Person` class:
====
[source,java]
----
ElasticsearchOperations operations; // autowired
Duration tenSeconds = Duration.ofSeconds(10);
String pit = operations.openPointInTime(IndexCoordinates.of("person"), tenSeconds); <.>
// create query for the pit
Query query1 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Smith"))
.withPointInTime(new Query.PointInTime(pit, tenSeconds)) <.>
.build();
SearchHits<Person> searchHits1 = operations.search(query1, Person.class);
// do something with the data
// create 2nd query for the pit, use the id returned in the previous result
Query query2 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Miller"))
.withPointInTime(
new Query.PointInTime(searchHits1.getPointInTimeId(), tenSeconds)) <.>
.build();
SearchHits<Person> searchHits2 = operations.search(query2, Person.class);
// do something with the data
operations.closePointInTime(searchHits2.getPointInTimeId()); <.>
----
<.> create a point in time for an index (can be multiple names) and a keep-alive duration and retrieve its id
<.> pass that id into the query to search together with the next keep-alive value
<.> for the next query, use the id returned from the previous search
<.> when done, close the point in time using the last returned id
====
[[elasticsearch.misc.searchtemplates]]
== Search Template support
Use of the search template API is supported.
To use this, it first is necessary to create a stored script.
The `ElasticsearchOperations` interface extends `ScriptOperations` which provides the necessary functions.
The example used here assumes that we have `Person` entity with a property named `firstName`.
A search template script can be saved like this:
====
[source,java]
----
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.script.Script;
operations.putScript( <.>
Script.builder()
.withId("person-firstname") <.>
.withLanguage("mustache") <.>
.withSource(""" <.>
{
"query": {
"bool": {
"must": [
{
"match": {
"firstName": "{{firstName}}" <.>
}
}
]
}
},
"from": "{{from}}", <.>
"size": "{{size}}" <.>
}
""")
.build()
);
----
<.> Use the `putScript()` method to store a search template script
<.> The name / id of the script
<.> Scripts that are used in search templates must be in the _mustache_ language.
<.> The script source
<.> The search parameter in the script
<.> Paging request offset
<.> Paging request size
====
To use a search template in a search query, Spring Data Elasticsearch provides the `SearchTemplateQuery`, an implementation of the `org.springframework.data.elasticsearch.core.query.Query` interface.
In the following code, we will add a call using a search template query to a custom repository implementation (see
<<repositories.custom-implementations>>) as
an example how this can be integrated into a repository call.
We first define the custom repository fragment interface:
====
[source,java]
----
interface PersonCustomRepository {
SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable);
}
----
====
The implementation of this repository fragment looks like this:
====
[source,java]
----
public class PersonCustomRepositoryImpl implements PersonCustomRepository {
private final ElasticsearchOperations operations;
public PersonCustomRepositoryImpl(ElasticsearchOperations operations) {
this.operations = operations;
}
@Override
public SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable) {
var query = SearchTemplateQuery.builder() <.>
.withId("person-firstname") <.>
.withParams(
Map.of( <.>
"firstName", firstName,
"from", pageable.getOffset(),
"size", pageable.getPageSize()
)
)
.build();
SearchHits<Person> searchHits = operations.search(query, Person.class); <.>
return SearchHitSupport.searchPageFor(searchHits, pageable);
}
}
----
<.> Create a `SearchTemplateQuery`
<.> Provide the id of the search template
<.> The parameters are passed in a `Map<String,Object>`
<.> Do the search in the same way as with the other query types.
====
@@ -1,11 +1,25 @@
[[new-features]]
= What's new
[[new-features.5-1-0]]
== New in Spring Data Elasticsearch 5.1
* Upgrade to Elasticsearch 8.7.0
* Allow specification of the TLS certificate when connecting to an Elasticsearch 8 cluster
[[new-features.5-0-0]]
== New in Spring Data Elasticsearch 5.0
* Upgrade to Java 17 baseline
* Upgrade to Spring Framework 6
* Upgrade to Elasticsearch 8.5.0
* Use the new Elasticsearch client library
[[new-features.4-4-0]]
== New in Spring Data Elasticsearch 4.4
* Introduction of new imperative and reactive clients using the classes from the new Elasticsearch Java client
* Upgrade to Elasticsearch 7.17.1.
* Upgrade to Elasticsearch 7.17.3.
[[new-features.4-3-0]]
== New in Spring Data Elasticsearch 4.3
@@ -2,19 +2,8 @@
= Elasticsearch Object Mapping
Spring Data Elasticsearch Object Mapping is the process that maps a Java object - the domain entity - into the JSON representation that is stored in Elasticsearch and back.
Earlier versions of Spring Data Elasticsearch used a Jackson based conversion, Spring Data Elasticsearch 3.2.x introduced the <<elasticsearch.mapping.meta-model>>.
As of version 4.0 only the Meta Object Mapping is used, the Jackson based mapper is not available anymore and the `MappingElasticsearchConverter` is used.
The main reasons for the removal of the Jackson based mapper are:
* Custom mappings of fields needed to be done with annotations like `@JsonFormat` or `@JsonInclude`.
This often caused problems when the same object was used in different JSON based datastores or sent over a JSON based API.
* Custom field types and formats also need to be stored into the Elasticsearch index mappings.
The Jackson based annotations did not fully provide all the information that is necessary to represent the types of Elasticsearch.
* Fields must be mapped not only when converting from and to entities, but also in query argument, returned data and on other places.
Using the `MappingElasticsearchConverter` now covers all these cases.
The class that is internally used for this mapping is the
`MappingElasticsearchConverter`.
[[elasticsearch.mapping.meta-model]]
== Meta Model Object Mapping
@@ -31,17 +20,16 @@ The metadata is taken from the entity's properties which can be annotated.
The following annotations are available:
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database.
The most important attributes are:
The most important attributes are (check the API documentation for the complete list of attributes):
** `indexName`: the name of the index to store this entity in.
This can contain a SpEL template expression like `"log-#{T(java.time.LocalDate).now().toString()}"`
** `createIndex`: flag whether to create an index on repository bootstrapping.
Default value is _true_.
See <<elasticsearch.repositories.autocreation>>
** `versionType`: Configuration of version management.
Default value is _EXTERNAL_.
* `@Id`: Applied at the field level to mark the field used for identity purpose.
* `@Transient`: By default all fields are mapped to the document when it is stored or retrieved, this annotation excludes the field.
* `@Transient`, `@ReadOnlyProperty`, `@WriteOnlyProperty`: see the following section <<elasticsearch.mapping.meta-model.annotations.read-write>> for detailed information.
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database.
Constructor arguments are mapped by name to the key values in the retrieved Document.
* `@Field`: Applied at the field level and defines properties of the field, most of the attributes map to the respective https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html[Elasticsearch Mapping] definitions (the following list is not complete, check the annotation Javadoc for a complete reference):
@@ -50,8 +38,8 @@ Constructor arguments are mapped by name to the key values in the retrieved Docu
See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types].
If the field type is not specified, it defaults to `FieldType.Auto`.
This means, that no mapping entry is written for the property and that Elasticsearch will add a mapping entry dynamically when the first data for this property is stored (check the Elasticsearch documentation for dynamic mapping rules).
** `format`: One or more built-in date formats, see the next section <<elasticsearch.mapping.meta-model.date-formats>>.
** `pattern`: One or more custom date formats, see the next section <<elasticsearch.mapping.meta-model.date-formats>>.
** `format`: One or more built-in date formats, see the next section <<elasticsearch.mapping.meta-model.annotations.date-formats>>.
** `pattern`: One or more custom date formats, see the next section <<elasticsearch.mapping.meta-model.annotations.date-formats>>.
** `store`: Flag whether the original field value should be store in Elasticsearch, default value is _false_.
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom analyzers and normalizer.
* `@GeoPoint`: Marks a field as _geo_point_ datatype.
@@ -61,7 +49,20 @@ In difference to a registered Spring `Converter` this only converts the annotate
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
[[elasticsearch.mapping.meta-model.date-formats]]
[[elasticsearch.mapping.meta-model.annotations.read-write]]
==== Controlling which properties are written to and read from Elasticsearch
This section details the annotations that define if the value of a property is written to or read from Elasticsearch.
`@Transient`: A property annotated with this annotation will not be written to the mapping, it's value will not be sent to Elasticsearch and when documents are returned from Elasticsearch, this property will not be set in the resulting entity.
`@ReadOnlyProperty`: A property with this annotaiton will not have its value written to Elasticsearch, but when returning data, the proeprty will be filled with the value returned in the document from Elasticsearch.
One use case for this are runtime fields defined in the index mapping.
`@WriteOnlyProperty`: A property with this annotaiton will have its value stored in Elasticsearch but will not be set with any value when reading document.
This can be used for example for synthesized fields which should go into the Elasticsearch index but are not used elsewhere.
[[elasticsearch.mapping.meta-model.annotations.date-formats]]
==== Date format mapping
Properties that derive from `TemporalAccessor` or are of type `java.util.Date` must either have a `@Field` annotation of type `FieldType.Date` or a custom converter must be registered for this type.
@@ -103,6 +104,9 @@ The following table shows the different attributes and the mapping created from
NOTE: If you are using a custom date format, you need to use _uuuu_ for the year instead of _yyyy_.
This is due to a https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats[change in Elasticsearch 7].
Check the code of the `org.springframework.data.elasticsearch.annotations.DateFormat` enum for a complete list of predefined values and their patterns.
[[elasticsearch.mapping.meta-model.annotations.range]]
==== Range types
When a field is annotated with a type of one of _Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range,_ or _Ip_Range_ the field must be an instance of a class that will be mapped to an Elasticsearch range, for example:
@@ -148,6 +152,7 @@ class SomePersonData {
Supported classes for the type `<T>` are `Integer`, `Long`, `Float`, `Double`, `Date` and classes that implement the
`TemporalAccessor` interface.
[[elasticsearch.mapping.meta-model.annotations.mapped-names]]
==== Mapped field names
Without further configuration, Spring Data Elasticsearch will use the property name of an object as field name in Elasticsearch.
@@ -157,9 +162,45 @@ It is also possible to define a `FieldNamingStrategy` in the configuration of th
If for example a `SnakeCaseFieldNamingStrategy` is configured, the property _sampleProperty_ of the object would be mapped to _sample_property_ in Elasticsearch.
A `FieldNamingStrategy` applies to all entities; it can be overwritten by setting a specific name with `@Field` on a property.
[[elasticsearch.mapping.meta-model.annotations.non-field-backed-properties]]
==== Non-field-backed properties
Normally the properties used in an entity are fields of the entity class.
There might be cases, when a property value is calculated in the entity and should be stored in Elasticsearch.
In this case, the getter method (`getProperty()`) can be annotated with the `@Field` annotation, in addition to that the method must be annotated with `@AccessType(AccessType.Type
.PROPERTY)`.
The third annotation that is needed in such a case is `@WriteOnlyProperty`, as such a value is only written to Elasticsearch.
A full example:
====
[source,java]
----
@Field(type = Keyword)
@WriteOnlyProperty
@AccessType(AccessType.Type.PROPERTY)
public String getProperty() {
return "some value that is calculated here";
}
----
====
[[elasticsearch.mapping.meta-model.annotations.misc]]
==== Other property annotations
===== @IndexedIndexName
This annotation can be set on a String property of an entity.
This property will not be written to the mapping, it will not be stored in Elasticsearch and its value will not be read from an Elasticsearch document.
After an entity is persisted, for example with a call to `ElasticsearchOperations.save(T entity)`, the entity
returned from that call will contain the name of the index that an entity was saved to in that property.
This is useful when the index name is dynamically set by a bean, or when writing to a write alias.
Putting some value into such a property does not set the index into which an entity is stored!
[[elasticsearch.mapping.meta-model.rules]]
=== Mapping Rules
[[elasticsearch.mapping.meta-model.rules.typehints]]
==== Type Hints
Mapping uses _type hints_ embedded in the document sent to the server to allow generic type mapping.
@@ -170,7 +211,6 @@ Those type hints are represented as `_class` attributes within the document and
[source,java]
----
public class Person { <1>
@Id String id;
String firstname;
String lastname;
@@ -227,7 +267,7 @@ In this case, writing the type hint will produce an error, as the field cannot b
Type hints can be disabled for the whole application by overriding the method `writeTypeHints()` in a configuration class derived from `AbstractElasticsearchConfiguration` (see <<elasticsearch.clients>>).
As an alternativ they can be disabled for a single index with the `@Document` annotation:
As an alternative they can be disabled for a single index with the `@Document` annotation:
====
[source,java]
@@ -240,6 +280,7 @@ WARNING: We strongly advise against disabling Type Hints.
Only do this if you are forced to.
Disabling type hints can lead to documents not being retrieved correctly from Elasticsearch in case of polymorphic data or document retrieval may fail completely.
[[elasticsearch.mapping.meta-model.rules.geospatial]]
==== Geospatial Types
Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
@@ -249,7 +290,6 @@ Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
[source,java]
----
public class Address {
String city, street;
Point location;
}
@@ -265,6 +305,7 @@ public class Address {
----
====
[[elasticsearch.mapping.meta-model.rules.geojson]]
==== GeoJson Types
Spring Data Elasticsearch supports the GeoJson types by providing an interface `GeoJson` and implementations for the different geometries.
@@ -305,6 +346,7 @@ The following GeoJson types are implemented:
* `GeoJsonMultiPolygon`
* `GeoJsonGeometryCollection`
[[elasticsearch.mapping.meta-model.rules.collections]]
==== Collections
For values inside Collections apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
@@ -332,6 +374,7 @@ public class Person {
----
====
[[elasticsearch.mapping.meta-model.rules.maps]]
==== Maps
For values inside Maps apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
@@ -376,12 +419,15 @@ Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, prev
[source,java]
----
@Configuration
public class Config extends AbstractElasticsearchConfiguration {
public class Config extends ElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
}
@NonNull
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
@Bean
@Override
@@ -392,7 +438,7 @@ public class Config extends AbstractElasticsearchConfiguration {
@WritingConverter <2>
static class AddressToMap implements Converter<Address, Map<String, Object>> {
@Override
public Map<String, Object> convert(Address source) {
@@ -30,36 +30,11 @@ There is support for automatic creation of indices and writing the mappings when
====
[[elasticsearch.operations.resttemplate]]
== ElasticsearchRestTemplate
The `ElasticsearchRestTemplate` is an implementation of the `ElasticsearchOperations` interface using the <<elasticsearch.clients.rest>>.
.ElasticsearchRestTemplate configuration
====
[source,java]
----
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() { <1>
return RestClients.create(ClientConfiguration.localhost()).rest();
}
// no special bean creation needed <2>
}
----
<1> Setting up the <<elasticsearch.clients.rest>>.
<2> The base class `AbstractElasticsearchConfiguration` already provides the `elasticsearchTemplate` bean.
====
[[elasticsearch.operations.usage]]
== Usage examples
As both `ElasticsearchTemplate` and `ElasticsearchRestTemplate` implement the `ElasticsearchOperations` interface, the code to use them is not different.
The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller.
The decision, if this is using the `TransportClient` or the `RestClient` is made by providing the corresponding Bean with one of the configurations shown above.
The example assumes that `Person` is a class that is annotated with `@Document`, `@Id` etc (see <<elasticsearch.mapping.meta-model.annotations>>).
.ElasticsearchOperations usage
====
@@ -71,34 +46,29 @@ public class TestController {
private ElasticsearchOperations elasticsearchOperations;
public TestController(ElasticsearchOperations elasticsearchOperations) { <1>
public TestController(ElasticsearchOperations elasticsearchOperations) { <.>
this.elasticsearchOperations = elasticsearchOperations;
}
@PostMapping("/person")
public String save(@RequestBody Person person) { <2>
IndexQuery indexQuery = new IndexQueryBuilder()
.withId(person.getId().toString())
.withObject(person)
.build();
String documentId = elasticsearchOperations.index(indexQuery);
return documentId;
public String save(@RequestBody Person person) { <.>
Person savedEntity = elasticsearchOperations.save(person);
return savedEntity.getId();
}
@GetMapping("/person/{id}")
public Person findById(@PathVariable("id") Long id) { <3>
Person person = elasticsearchOperations
.queryForObject(GetQuery.getById(id.toString()), Person.class);
public Person findById(@PathVariable("id") Long id) { <.>
Person person = elasticsearchOperations.get(id.toString(), Person.class);
return person;
}
}
----
<1> Let Spring inject the provided `ElasticsearchOperations` bean in the constructor.
<2> Store some entity in the Elasticsearch cluster.
<3> Retrieve the entity with a query by id.
<.> Let Spring inject the provided `ElasticsearchOperations` bean in the constructor.
<.> Store some entity in the Elasticsearch cluster.
The id is read from the returned entity, as it might have been null in the `person` object and been created by Elasticsearch.
<.> Retrieve the entity with a get by id.
====
To see the full possibilities of `ElasticsearchOperations` please refer to the API documentation.
@@ -150,7 +120,7 @@ An Iterator returned by the streaming functions of the `SearchOperations` interf
[[elasticsearch.operations.queries]]
== Queries
Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchOperations` interface take a `Query` parameter that defines the query to execute for searching. `Query` is an interface and Spring Data Elasticsearch provides three implementations: `CriteriaQuery`, `StringQuery` and `NativeSearchQuery`.
Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchOperations` interface take a `Query` parameter that defines the query to execute for searching. `Query` is an interface and Spring Data Elasticsearch provides three implementations: `CriteriaQuery`, `StringQuery` and `NativeQuery`.
[[elasticsearch.operations.criteriaquery]]
=== CriteriaQuery
@@ -177,7 +147,7 @@ Conditions for the same field can be chained, they will be combined with a logic
====
[source,java]
----
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0L);
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0);
Query query = new CriteriaQuery(criteria);
----
====
@@ -230,7 +200,7 @@ The following code shows a query that searches for persons having the first name
[source,java]
----
Query query = new SearchQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
Query query = new StringQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
SearchHits<Person> searchHits = operations.search(query, Person.class);
----
@@ -238,22 +208,35 @@ SearchHits<Person> searchHits = operations.search(query, Person.class);
Using `StringQuery` may be appropriate if you already have an Elasticsearch query to use.
[[elasticsearch.operations.nativesearchquery]]
=== NativeSearchQuery
[[elasticsearch.operations.nativequery]]
=== NativeQuery
`NativeSearchQuery` is the class to use when you have a complex query, or a query that cannot be expressed by using the `Criteria` API, for example when building queries and using aggregates.
It allows to use all the different `QueryBuilder` implementations from the Elasticsearch library therefore named "native".
`NativeQuery` is the class to use when you have a complex query, or a query that cannot be expressed by using the `Criteria` API, for example when building queries and using aggregates.
It allows to use all the different `co.elastic.clients.elasticsearch._types.query_dsl.Query` implementations from the Elasticsearch library therefore named "native".
The following code shows how to search for persons with a given firstname and for the found documents have a terms aggregation that counts the number of occurences of the lastnames for these persons:
====
[source,java]
----
Query query = new NativeSearchQueryBuilder()
.addAggregation(terms("lastnames").field("lastname").size(10)) //
.withQuery(QueryBuilders.matchQuery("firstname", firstName))
.build();
Query query = NativeQuery.builder()
.withAggregation("lastNames", Aggregation.of(a -> a
.terms(ta -> ta.field("last-name").size(10))))
.withQuery(q -> q
.match(m -> m
.field("firstName")
.query(firstName)
)
)
.withPageable(pageable)
.build();
SearchHits<Person> searchHits = operations.search(query, Person.class);
----
====
[[elasticsearch.operations.searchtemplateScOp§query]]
=== SearchTemplateQuery
This is a special implementation of the `Query` interface to be used in combination with a stored search template.
See <<elasticsearch.misc.searchtemplates>> for further information.
@@ -40,6 +40,7 @@ include::reactive-elasticsearch-repositories.adoc[leveloffset=+1]
[[elasticsearch.repositories.annotations]]
== Annotations for repository methods
[[elasticsearch.repositories.annotations.highlight]]
=== @Highlight
The `@Highlight` annotation on a repository method defines for which fields of the returned entity highlighting should be included. To search for some text in a `Book` 's name or summary and have the found data highlighted, the following repository method can be used:
@@ -53,7 +54,7 @@ interface BookRepository extends Repository<Book, String> {
@HighlightField(name = "name"),
@HighlightField(name = "summary")
})
List<SearchHit<Book>> findByNameOrSummary(String text, String summary);
SearchHits<Book> findByNameOrSummary(String text, String summary);
}
----
====
@@ -62,6 +63,31 @@ It is possible to define multiple fields to be highlighted like above, and both
In the search results the highlight data can be retrieved from the `SearchHit` class.
[[elasticsearch.repositories.annotations.sourcefilters]]
=== @SourceFilters
Sometimes the user does not need to have all the properties of an entity returned from a search but only a subset.
Elasticsearch provides source filtering to reduce the amount of data that is transferred across the network to the
application.
When working with `Query` implementations and the `ElasticsearchOperations` this is easily possible by setting a
source filter on the query.
When using repository methods there is the `@SourceFilters` annotation:
====
[source,java]
----
interface BookRepository extends Repository<Book, String> {
@SourceFilters(includes = "name")
SearchHits<Book> findByName(String text);
}
----
====
In this example, all the properties of the returned `Book` objects would be `null` except the name.
[[elasticsearch.annotation]]
== Annotation based configuration
@@ -6,6 +6,7 @@
The Elasticsearch module supports all basic query building feature as string queries, native search queries, criteria based queries or have it being derived from the method name.
[[elasticsearch.query-methods.finders.declared]]
=== Declared queries
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names.
@@ -298,6 +299,7 @@ A list of supported keywords for Elasticsearch is shown below.
NOTE: Methods names to build Geo-shape queries taking `GeoJson` parameters are not supported.
Use `ElasticsearchOperations` with `CriteriaQuery` in a custom repository implementation if you need to have such a function in a repository.
[[elasticsearch.query-methods.return-types]]
== Method return types
Repository methods can be defined to have the following return types for returning multiple Elements:
@@ -359,4 +361,3 @@ would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/qu
}
----
====
@@ -1,4 +1,3 @@
[[elasticsearch.routing]]
= Routing values
@@ -8,6 +7,7 @@ For this Elasticsearch offers the possibility to define a routing, which is the
Spring Data Elasticsearch supports routing definitions on storing and retrieving data in the following ways:
[[elasticsearch.routing.join-types]]
== Routing on join-types
When using join-types (see <<elasticsearch.jointype>>), Spring Data Elasticsearch will automatically use the `parent` property of the entity's `JoinField` property as the value for the routing.
@@ -15,6 +15,7 @@ When using join-types (see <<elasticsearch.jointype>>), Spring Data Elasticsearc
This is correct for all the use-cases where the parent-child relationship has just one level.
If it is deeper, like a child-parent-grandparent relationship - like in the above example from _vote_ -> _answer_ -> _question_ - then the routing needs to explicitly specified by using the techniques described in the next section (the _vote_ needs the _question.id_ as routing value).
[[elasticsearch.routing.custom]]
== Custom routing values
To define a custom routing for an entity, Spring Data Elasticsearch provides a `@Routing` annotation (reusing the `Statement` class from above):
@@ -103,4 +104,3 @@ operations.withRouting(RoutingResolver.just(routing)).delete(id);
----
<.> `RoutingResolver.just(s)` returns a resolver that will just return the given String.
====
@@ -12,4 +12,7 @@ include::elasticsearch-migration-guide-4.1-4.2.adoc[]
include::elasticsearch-migration-guide-4.2-4.3.adoc[]
include::elasticsearch-migration-guide-4.3-4.4.adoc[]
include::elasticsearch-migration-guide-4.4-5.0.adoc[]
:leveloffset: -1
@@ -5,80 +5,22 @@
The `ReactiveElasticsearchTemplate` is the default implementation of `ReactiveElasticsearchOperations`.
[[elasticsearch.reactive.template]]
== Reactive Elasticsearch Template
[[elasticsearch.reactive.operations]]
== Reactive Elasticsearch Operations
To get started the `ReactiveElasticsearchTemplate` needs to know about the actual client to work with.
Please see <<elasticsearch.clients.reactive>> for details on the client.
To get started the `ReactiveElasticsearchOperations` needs to know about the actual client to work with.
Please see <<elasticsearch.clients.reactiverestclient>> for details on the client and how to configure it.
[[elasticsearch.reactive.template.configuration]]
=== Reactive Template Configuration
[[elasticsearch.reactive.operations.usage]]
=== Reactive Operations Usage
The easiest way of setting up the `ReactiveElasticsearchTemplate` is via `AbstractReactiveElasticsearchConfiguration` providing
dedicated configuration method hooks for `base package`, the `initial entity set` etc.
.The AbstractReactiveElasticsearchConfiguration
====
[source,java]
----
@Configuration
public class Config extends AbstractReactiveElasticsearchConfiguration {
@Bean <1>
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
// ...
}
}
----
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
====
NOTE: If applicable set default `HttpHeaders` via the `ClientConfiguration` of the `ReactiveElasticsearchClient`. See <<elasticsearch.clients.configuration>>.
TIP: If needed the `ReactiveElasticsearchTemplate` can be configured with default `RefreshPolicy` and `IndicesOptions` that get applied to the related requests by overriding the defaults of `refreshPolicy()` and `indicesOptions()`.
However one might want to be more in control over the actual components and use a more verbose approach.
.Configure the ReactiveElasticsearchTemplate
====
[source,java]
----
@Configuration
public class Config {
@Bean <1>
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
// ...
}
@Bean <2>
public ElasticsearchConverter elasticsearchConverter() {
return new MappingElasticsearchConverter(elasticsearchMappingContext());
}
@Bean <3>
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
return new SimpleElasticsearchMappingContext();
}
@Bean <4>
public ReactiveElasticsearchOperations reactiveElasticsearchOperations() {
return new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(), elasticsearchConverter());
}
}
----
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
<2> Set up the `ElasticsearchConverter` used for domain type mapping utilizing metadata provided by the mapping context.
<3> The Elasticsearch specific mapping context for domain type metadata.
<4> The actual template based on the client and conversion infrastructure.
====
[[elasticsearch.reactive.template.usage]]
=== Reactive Template Usage
`ReactiveElasticsearchTemplate` lets you save, find and delete your domain objects and map those objects to documents stored in Elasticsearch.
`ReactiveElasticsearchOperations` lets you save, find and delete your domain objects and map those objects to documents
stored
in Elasticsearch.
Consider the following:
.Use the ReactiveElasticsearchTemplate
.Use the ReactiveElasticsearchOperations
====
[source,java]
----
@@ -94,15 +36,20 @@ public class Person {
[source,java]
----
template.save(new Person("Bruce Banner", 42)) <1>
ReactiveElasticsearchOperations operations;
// ...
operations.save(new Person("Bruce Banner", 42)) <.>
.doOnNext(System.out::println)
.flatMap(person -> template.findById(person.id, Person.class)) <2>
.flatMap(person -> operations.get(person.id, Person.class)) <.>
.doOnNext(System.out::println)
.flatMap(person -> template.delete(person)) <3>
.flatMap(person -> operations.delete(person)) <.>
.doOnNext(System.out::println)
.flatMap(id -> template.count(Person.class)) <4>
.flatMap(id -> operations.count(Person.class)) <.>
.doOnNext(System.out::println)
.subscribe(); <5>
.subscribe(); <.>
----
The above outputs the following sequence on the console.
@@ -114,11 +61,10 @@ The above outputs the following sequence on the console.
> QjWCWWcBXiLAnp77ksfR
> 0
----
<1> Insert a new `Person` document into the _marvel_ index under type _characters_. The `id` is generated on server side and set into the instance returned.
<2> Lookup the `Person` with matching `id` in the _marvel_ index under type _characters_.
<3> Delete the `Person` with matching `id`, extracted from the given instance, in the _marvel_ index under type _characters_.
<4> Count the total number of documents in the _marvel_ index under type _characters_.
<5> Don't forget to _subscribe()_.
<.> Insert a new `Person` document into the _marvel_ index . The `id` is generated on server
side and set into the instance returned.
<.> Lookup the `Person` with matching `id` in the _marvel_ index.
<.> Delete the `Person` with matching `id`, extracted from the given instance, in the _marvel_ index.
<.> Count the total number of documents in the _marvel_ index.
<.> Don't forget to _subscribe()_.
====
@@ -79,7 +79,7 @@ interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Str
parameters.
<9> Count all entities with matching `firstname`.
<10> Check if at least one entity with matching `firstname` exists.
<11> Delete all entites with matching `firstname`.
<11> Delete all entities with matching `firstname`.
====
[[elasticsearch.reactive.repositories.configuration]]
@@ -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.
@@ -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,9 +15,9 @@
*/
package org.springframework.data.elasticsearch;
import java.util.List;
import org.springframework.lang.Nullable;
import javax.annotation.Nullable;
import java.util.List;
/**
* Object describing an Elasticsearch error
@@ -26,7 +26,8 @@ import javax.annotation.Nullable;
* @since 4.4
*/
public class ElasticsearchErrorCause {
@Nullable private final String type;
@Nullable
private final String type;
private final String reason;
@@ -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.
@@ -25,6 +25,14 @@ public class NoSuchIndexException extends NonTransientDataAccessResourceExceptio
private final String index;
/**
* @since 4.4
*/
public NoSuchIndexException(String index) {
super(String.format("Index %s not found.", index));
this.index = index;
}
public NoSuchIndexException(String index, Throwable cause) {
super(String.format("Index %s not found.", index), cause);
this.index = index;
@@ -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.
@@ -0,0 +1,29 @@
/*
* 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.elasticsearch;
import org.springframework.dao.NonTransientDataAccessResourceException;
/**
* @author Peter-Josef Meisch
* @since 5.1
*/
public class ResourceNotFoundException extends NonTransientDataAccessResourceException {
public ResourceNotFoundException(String msg) {
super(msg);
}
}
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -16,9 +16,11 @@
package org.springframework.data.elasticsearch.annotations;
/**
* Values based on reference doc - https://www.elastic.co/guide/reference/mapping/date-format/. The patterns are taken
* from this documentation and slightly adapted so that a Java {@link java.time.format.DateTimeFormatter} produces the
* same values as the Elasticsearch formatter.
* Values based on <a href="https://www.elastic.co/guide/reference/mapping/date-format/">Elasticsearch reference
* documentation</a>. The patterns are taken from this documentation and slightly adapted so that a Java
* {@link java.time.format.DateTimeFormatter} produces the same values as the Elasticsearch formatter. Use
* <code>format = {}</code> to disable built-in date formats in the {@link Field} annotation. If you want to use only a
* custom date format pattern, you must set the <code>format</code> property to empty <code>{}</code>.
*
* @author Jakub Vavrik
* @author Tim te Beek
@@ -26,19 +28,6 @@ package org.springframework.data.elasticsearch.annotations;
* @author Sascha Woo
*/
public enum DateFormat {
/**
* @deprecated since 4.2, will be removed in a future version. Use <code>format = {}</code> to disable built-in date
* formats in the @Field annotation.
*/
@Deprecated
none(""), //
/**
* @deprecated since 4.2, will be removed in a future version.It is no longer required for using a custom date format
* pattern. If you want to use only a custom date format pattern, you must set the <code>format</code>
* property to empty <code>{}</code>.
*/
@Deprecated
custom(""), //
basic_date("uuuuMMdd"), //
basic_date_time("uuuuMMdd'T'HHmmss.SSSXXX"), //
basic_date_time_no_millis("uuuuMMdd'T'HHmmssXXX"), //
@@ -60,7 +49,7 @@ public enum DateFormat {
date_hour_minute_second_millis("uuuu-MM-dd'T'HH:mm:ss.SSS"), //
date_optional_time("uuuu-MM-dd['T'HH:mm:ss.SSSXXX]"), //
date_time("uuuu-MM-dd'T'HH:mm:ss.SSSXXX"), //
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in it's implementation
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in its implementation
epoch_millis("epoch_millis"), //
epoch_second("epoch_second"), //
hour("HH"), //
@@ -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.
@@ -53,49 +53,6 @@ public @interface Document {
*/
String indexName();
/**
* Use server-side settings when creating the index.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
boolean useServerConfiguration() default false;
/**
* Number of shards for the index {@link #indexName()}. Used for index creation. <br/>
* With version 4.0, the default value is changed from 5 to 1 to reflect the change in the default settings of
* Elasticsearch which changed to 1 as well in Elasticsearch 7.0.
* ComposableAnnotationsUnitTest.documentAnnotationShouldBeComposable:60
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
short shards() default 1;
/**
* Number of replicas for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
short replicas() default 1;
/**
* Refresh interval for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
String refreshInterval() default "1s";
/**
* Index storage type for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
String indexStoreType() default "fs";
/**
* Configuration whether to create an index on repository bootstrapping.
*/
@@ -120,6 +77,22 @@ public @interface Document {
*/
Dynamic dynamic() default Dynamic.INHERIT;
/**
* Specifies if the id property should also be stored in the Elasticsearch document source. Default value is
* {@literal true}
*
* @since 5.1
*/
boolean storeIdInSource() default true;
/**
* Specifies if the version property should also be stored in the Elasticsearch document source. Default value is
* true.
*
* @since 5.1
*/
boolean storeVersionInSource() default true;
/**
* @since 4.3
*/
@@ -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.
@@ -46,7 +46,7 @@ public enum Dynamic {
/**
* Inherit the dynamic setting from their parent object or from the mapping type.
*/
INHERIT("nherit");
INHERIT("inherit");
private final String mappedName;
@@ -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.
@@ -39,7 +39,7 @@ import org.springframework.core.annotation.AliasFor;
* @author Sascha Woo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Documented
@Inherited
public @interface Field {
@@ -53,9 +53,8 @@ public @interface Field {
String value() default "";
/**
* The <em>name</em> to be used to store the field inside the document.
* <p>
* √5 If not set, the name of the annotated property is used.
* The <em>name</em> to be used to store the field inside the document. If not set, the name of the annotated property
* is used.
*
* @since 3.2
*/
@@ -141,7 +140,7 @@ public @interface Field {
/**
* @since 4.0
*/
Similarity similarity() default Similarity.Default;
String similarity() default Similarity.Default;
/**
* @since 4.0
@@ -211,4 +210,12 @@ public @interface Field {
* @since 4.3
*/
boolean excludeFromSource() default false;
/**
* when this field is a {{@link String}}, a {{@link java.util.Collection}} or a {{@link java.util.Map}} that is empty
* this property controlls whether the empty value is sent to Elasticsearch.
*
* @since 5.1
*/
boolean storeEmptyValue() default true;
}
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -0,0 +1,42 @@
/*
* 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.elasticsearch.annotations;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark a String property of an entity to be filled with the name of the index where the entity was
* stored after it is indexed into Elasticsearch. This can be used when the name of the index is dynamically created
* or when a document was indexed into a write alias.
*
* This can not be used to specify the index where an entity should be written to.
*
* @author Peter-Josef Meisch
* @since 5.1
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Documented
@Field(type = FieldType.Auto) // prevents the property being written to the index mapping
public @interface IndexedIndexName {
}
@@ -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.
@@ -109,7 +109,7 @@ public @interface InnerField {
/**
* @since 4.0
*/
Similarity similarity() default Similarity.Default;
String similarity() default Similarity.Default;
/**
* @since 4.0
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -15,8 +15,14 @@
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.annotation.QueryAnnotation;
import java.lang.annotation.*;
/**
* Query
@@ -34,16 +40,17 @@ import java.lang.annotation.*;
public @interface Query {
/**
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0. Alias for query.
*/
@AliasFor("query")
String value() default "";
/**
* Named Query Named looked up by repository.
*
* @deprecated since 4.2, not implemented and used anywhere
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0. Alias for value
* @since 5.0
*/
String name() default "";
@AliasFor("value")
String query() default "";
/**
* Returns whether the query defined should be executed as count projection.
@@ -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.
@@ -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.
@@ -19,18 +19,9 @@ package org.springframework.data.elasticsearch.annotations;
* @author Peter-Josef Meisch
* @since 4.0
*/
public enum Similarity {
Default("default"), BM25("BM25"), classic("classic"), Boolean("boolean");
// need to use a custom name because 'boolean' can't be used as enum name
private final String toStringName;
Similarity(String name) {
this.toStringName = name;
}
@Override
public String toString() {
return toStringName;
}
public final class Similarity {
public final static String Default = "default";
public final static String BM25 = "BM25";
public final static String classic = "classic";
public final static String Boolean = "boolean";
}
@@ -0,0 +1,73 @@
/*
* 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.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation can be placed on repository methods to define the properties that should be requested from
* Elasticsearch when the method is run.
*
* @author Alexander Torres
* @author Peter-Josef Meisch
* @since 5.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
public @interface SourceFilters {
/**
* Properties to be requested from Elasticsearch to be included in the response. These can be passed in as literals
* like
*
* <pre>
* {@code @SourceFilters(includes = {"property1", "property2"})}
* </pre>
*
* or as a parameterized value
*
* <pre>
* {@code @SourceFilters(includes = "?0")}
* </pre>
*
* when the list of properties is passed as a function parameter.
*/
String[] includes() default "";
/**
* Properties to be requested from Elasticsearch to be excluded in the response. These can be passed in as literals
* like
*
* <pre>
* {@code @SourceFilters(excludes = {"property1", "property2"})}
* </pre>
*
* or as a parameterized value
*
* <pre>
* {@code @SourceFilters(excludes = "?0")}
* </pre>
*
* when the list of properties is passed as a function parameter.
*/
String[] excludes() default "";
}
@@ -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.
@@ -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.
@@ -1,5 +1,5 @@
/*
* Copyright 2019-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.
@@ -22,19 +22,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to set the dynamic mapping mode
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic.html">elasticsearch doc</a>}
* Annotation to mark a property that will be written to Elasticsearch, but not set when reading from Elasticsearch.
* This is needed for synthesized fields that may be used for search but that are not available in the entity.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.0
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
* @since 5.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD })
@Target({ ElementType.METHOD, ElementType.FIELD })
@Documented
@Deprecated
public @interface DynamicMapping {
DynamicMappingValue value() default DynamicMappingValue.True;
public @interface WriteOnlyProperty {
}
@@ -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.
@@ -0,0 +1,35 @@
/*
* 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.elasticsearch.aot;
import java.util.function.Predicate;
import org.springframework.data.util.ReactiveWrappers;
/**
* @author Peter-Josef Meisch
* @since 5.1
*/
public class ElasticsearchAotPredicates {
public static final Predicate<ReactiveWrappers.ReactiveLibrary> IS_REACTIVE_LIBARARY_AVAILABLE = (
lib) -> ReactiveWrappers.isAvailable(lib);
public static boolean isReactorPresent() {
return IS_REACTIVE_LIBARARY_AVAILABLE.test(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR);
}
}
@@ -0,0 +1,75 @@
/*
* 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.elasticsearch.aot;
import static org.springframework.data.elasticsearch.aot.ElasticsearchAotPredicates.*;
import java.util.Arrays;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.data.elasticsearch.client.elc.EntityAsMap;
import org.springframework.data.elasticsearch.core.event.AfterConvertCallback;
import org.springframework.data.elasticsearch.core.event.AfterLoadCallback;
import org.springframework.data.elasticsearch.core.event.AfterSaveCallback;
import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback;
import org.springframework.data.elasticsearch.core.event.BeforeConvertCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterConvertCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterLoadCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
* @since 5.1
*/
public class ElasticsearchRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerTypes( //
Arrays.asList( //
TypeReference.of(AfterConvertCallback.class), //
TypeReference.of(AfterLoadCallback.class), //
TypeReference.of(AfterSaveCallback.class), //
TypeReference.of(BeforeConvertCallback.class), //
TypeReference.of(EntityAsMap.class) //
), //
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
if (isReactorPresent()) {
hints.reflection().registerTypes( //
Arrays.asList( //
TypeReference.of(ReactiveAfterConvertCallback.class), //
TypeReference.of(ReactiveAfterLoadCallback.class), //
TypeReference.of(ReactiveAfterSaveCallback.class), //
TypeReference.of(ReactiveBeforeConvertCallback.class) //
), //
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
}
// properties needed to log the different versions
hints.resources().registerPattern("versions.properties");
hints.resources().registerPattern("co/elastic/clients/version.properties");
}
}
@@ -1,3 +1,3 @@
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.aot;
@@ -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.
@@ -20,17 +20,13 @@ import java.net.SocketAddress;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Configuration interface exposing common client configuration properties for Elasticsearch clients.
@@ -54,7 +50,6 @@ public interface ClientConfiguration {
/**
* Creates a new {@link ClientConfiguration} instance configured to localhost.
* <p/>
*
* <pre class="code">
* // "localhost:9200"
@@ -69,9 +64,8 @@ public interface ClientConfiguration {
}
/**
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@code hostAndPort}.
* <p/>
* For example given the endpoint http://localhost:9200
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@code hostAndPort}. For
* example given the endpoint http://localhost:9200
*
* <pre class="code">
* ClientConfiguration configuration = ClientConfiguration.create("localhost:9200");
@@ -84,9 +78,8 @@ public interface ClientConfiguration {
}
/**
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@link InetSocketAddress}.
* <p/>
* For example given the endpoint http://localhost:9200
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@link InetSocketAddress}. For
* example given the endpoint http://localhost:9200
*
* <pre class="code">
* ClientConfiguration configuration = ClientConfiguration
@@ -127,6 +120,12 @@ public interface ClientConfiguration {
*/
Optional<SSLContext> getSslContext();
/**
* @return the optional SHA-256 fingerprint of the self-signed http_ca.crt certificate output by Elasticsearch at
* startup time.
*/
Optional<String> getCaFingerprint();
/**
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
*
@@ -168,19 +167,6 @@ public interface ClientConfiguration {
*/
Optional<String> getProxy();
/**
* @return the function for configuring a WebClient.
*/
Function<WebClient, WebClient> getWebClientConfigurer();
/**
* @return the Rest Client configuration callback.
* @since 4.2
* @deprecated since 4.3 use {@link #getClientConfigurers()}
*/
@Deprecated
HttpClientConfigCallback getHttpClientConfigurer();
/**
* @return the client configuration callbacks
* @since 4.3
@@ -268,6 +254,15 @@ public interface ClientConfiguration {
* @return the {@link TerminalClientConfigurationBuilder}.
*/
TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier);
/**
* Connect via https using a SSLContext that is build from the given certificate fingerprint.
*
* @param caFingerprint the SHA-256 fingerprint of the self-signed http_ca.crt certificate output by Elasticsearch
* at startup time.
* @return the {@link TerminalClientConfigurationBuilder}.
*/
TerminalClientConfigurationBuilder usingSsl(String caFingerprint);
}
/**
@@ -349,29 +344,6 @@ public interface ClientConfiguration {
*/
TerminalClientConfigurationBuilder withProxy(String proxy);
/**
* set customization hook in case of a reactive configuration
*
* @param webClientConfigurer function to configure the WebClient
* @return the {@link TerminalClientConfigurationBuilder}.
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
* {@link ReactiveRestClients.WebClientConfigurationCallback}
*/
@Deprecated
TerminalClientConfigurationBuilder withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
/**
* Register a {HttpClientConfigCallback} to configure the non-reactive REST client.
*
* @param httpClientConfigurer configuration callback, must not be null.
* @return the {@link TerminalClientConfigurationBuilder}.
* @since 4.2
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
* {@link RestClients.RestClientConfigurationCallback}
*/
@Deprecated
TerminalClientConfigurationBuilder withHttpClientConfigurer(HttpClientConfigCallback httpClientConfigurer);
/**
* Register a {@link ClientConfigurationCallback} to configure the client.
*
@@ -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.
@@ -20,9 +20,7 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -31,11 +29,9 @@ import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint;
import org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Default builder implementation for {@link ClientConfiguration}.
@@ -51,20 +47,20 @@ class ClientConfigurationBuilder
implements ClientConfigurationBuilderWithRequiredEndpoint, MaybeSecureClientConfigurationBuilder {
private final List<InetSocketAddress> hosts = new ArrayList<>();
private HttpHeaders headers = HttpHeaders.EMPTY;
private HttpHeaders headers = new HttpHeaders();
private boolean useSsl;
private @Nullable SSLContext sslContext;
private @Nullable HostnameVerifier hostnameVerifier;
@Nullable private SSLContext sslContext;
@Nullable private String caFingerprint;
@Nullable private HostnameVerifier hostnameVerifier;
private Duration connectTimeout = Duration.ofSeconds(10);
private Duration soTimeout = Duration.ofSeconds(5);
private @Nullable String username;
private @Nullable String password;
private @Nullable String pathPrefix;
private @Nullable String proxy;
private Function<WebClient, WebClient> webClientConfigurer = Function.identity();
private Supplier<HttpHeaders> headersSupplier = () -> HttpHeaders.EMPTY;
@Deprecated private HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
private List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
@Nullable private String username;
@Nullable private String password;
@Nullable private String pathPrefix;
@Nullable private String proxy;
private Supplier<HttpHeaders> headersSupplier = HttpHeaders::new;
@Deprecated private final HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
private final List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
/*
* (non-Javadoc)
@@ -75,7 +71,7 @@ class ClientConfigurationBuilder
Assert.notEmpty(hostAndPorts, "At least one host is required");
this.hosts.addAll(Arrays.stream(hostAndPorts).map(ClientConfigurationBuilder::parse).collect(Collectors.toList()));
this.hosts.addAll(Arrays.stream(hostAndPorts).map(ClientConfigurationBuilder::parse).toList());
return this;
}
@@ -141,10 +137,20 @@ class ClientConfigurationBuilder
return this;
}
@Override
public TerminalClientConfigurationBuilder usingSsl(String caFingerprint) {
Assert.notNull(caFingerprint, "caFingerprint must not be null");
this.useSsl = true;
this.caFingerprint = caFingerprint;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders)
*/
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders)
*/
@Override
public TerminalClientConfigurationBuilder withDefaultHeaders(HttpHeaders defaultHeaders) {
@@ -201,28 +207,6 @@ class ClientConfigurationBuilder
return this;
}
@Override
public TerminalClientConfigurationBuilder withWebClientConfigurer(
Function<WebClient, WebClient> webClientConfigurer) {
Assert.notNull(webClientConfigurer, "webClientConfigurer must not be null");
this.webClientConfigurer = webClientConfigurer;
this.clientConfigurers.add(ReactiveRestClients.WebClientConfigurationCallback.from(webClientConfigurer));
return this;
}
@Override
public TerminalClientConfigurationBuilder withHttpClientConfigurer(HttpClientConfigCallback httpClientConfigurer) {
Assert.notNull(httpClientConfigurer, "httpClientConfigurer must not be null");
this.httpClientConfigurer = httpClientConfigurer;
this.clientConfigurers
.add(RestClients.RestClientConfigurationCallback.from(httpClientConfigurer::customizeHttpClient));
return this;
}
@Override
public TerminalClientConfigurationBuilder withClientConfigurer(
ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer) {
@@ -250,14 +234,15 @@ class ClientConfigurationBuilder
public ClientConfiguration build() {
if (username != null && password != null) {
if (HttpHeaders.EMPTY.equals(headers)) {
headers = new HttpHeaders();
}
headers.setBasicAuth(username, password);
}
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix,
hostnameVerifier, proxy, webClientConfigurer, httpClientConfigurer, clientConfigurers, headersSupplier);
if (sslContext != null && caFingerprint != null) {
throw new IllegalArgumentException("Either SSLContext or caFingerprint must be set, but not both");
}
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, caFingerprint, soTimeout, connectTimeout,
pathPrefix, hostnameVerifier, proxy, httpClientConfigurer, clientConfigurers, headersSupplier);
}
private static InetSocketAddress parse(String hostAndPort) {
@@ -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.
@@ -19,7 +19,6 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -30,8 +29,11 @@ import org.springframework.util.ObjectUtils;
* @author Mark Paluch
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Oliver Drotbohm
* @since 3.2
* @deprecated since 5.0, Elasticsearch's RestClient has a trace level logging available.
*/
@Deprecated
public abstract class ClientLogger {
private static final Log WIRE_LOGGER = LogFactory.getLog("org.springframework.data.elasticsearch.client.WIRE");
@@ -125,10 +127,10 @@ public abstract class ClientLogger {
* @param logId the correlation id, see {@link #newLogId()}.
* @param statusCode the HTTP status code.
*/
public static void logRawResponse(String logId, @Nullable HttpStatus statusCode) {
public static void logRawResponse(String logId, @Nullable Integer statusCode) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received raw response: %s", logId, statusCode));
WIRE_LOGGER.trace(String.format("[%s] Received raw response: %d", logId, statusCode));
}
}
@@ -139,10 +141,10 @@ public abstract class ClientLogger {
* @param statusCode the HTTP status code.
* @param headers a String containing the headers
*/
public static void logRawResponse(String logId, @Nullable HttpStatus statusCode, String headers) {
public static void logRawResponse(String logId, @Nullable Integer statusCode, String headers) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%n%s", logId, statusCode, headers));
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%n%s", logId, statusCode, headers));
}
}
@@ -153,10 +155,10 @@ public abstract class ClientLogger {
* @param statusCode the HTTP status code.
* @param body body content.
*/
public static void logResponse(String logId, HttpStatus statusCode, String body) {
public static void logResponse(String logId, Integer statusCode, String body) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%nResponse body: %s", logId, statusCode, body));
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%nResponse body: %s", logId, statusCode, body));
}
}
@@ -169,10 +171,10 @@ public abstract class ClientLogger {
* @param body body content.
* @since 4.4
*/
public static void logResponse(String logId, @Nullable HttpStatus statusCode, String headers, String body) {
public static void logResponse(String logId, @Nullable Integer statusCode, String headers, String body) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%nHeaders: %s%nResponse body: %s", logId, statusCode,
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%nHeaders: %s%nResponse body: %s", logId, statusCode,
headers, body));
}
}
@@ -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,20 +17,16 @@ package org.springframework.data.elasticsearch.client;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Default {@link ClientConfiguration} implementation.
@@ -46,33 +42,33 @@ class DefaultClientConfiguration implements ClientConfiguration {
private final List<InetSocketAddress> hosts;
private final HttpHeaders headers;
private final boolean useSsl;
private final @Nullable SSLContext sslContext;
@Nullable private final SSLContext sslContext;
@Nullable private final String caFingerprint;
private final Duration soTimeout;
private final Duration connectTimeout;
private final @Nullable String pathPrefix;
private final @Nullable HostnameVerifier hostnameVerifier;
private final @Nullable String proxy;
private final Function<WebClient, WebClient> webClientConfigurer;
@Nullable private final String pathPrefix;
@Nullable private final HostnameVerifier hostnameVerifier;
@Nullable private final String proxy;
private final HttpClientConfigCallback httpClientConfigurer;
private final Supplier<HttpHeaders> headersSupplier;
private final List<ClientConfigurationCallback<?>> clientConfigurers;
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix,
@Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
Function<WebClient, WebClient> webClientConfigurer, HttpClientConfigCallback httpClientConfigurer,
List<ClientConfigurationCallback<?>> clientConfigurers, Supplier<HttpHeaders> headersSupplier) {
@Nullable SSLContext sslContext, @Nullable String caFingerprint, Duration soTimeout, Duration connectTimeout,
@Nullable String pathPrefix, @Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
HttpClientConfigCallback httpClientConfigurer, List<ClientConfigurationCallback<?>> clientConfigurers,
Supplier<HttpHeaders> headersSupplier) {
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
this.headers = new HttpHeaders(headers);
this.hosts = List.copyOf(hosts);
this.headers = headers;
this.useSsl = useSsl;
this.sslContext = sslContext;
this.caFingerprint = caFingerprint;
this.soTimeout = soTimeout;
this.connectTimeout = connectTimeout;
this.pathPrefix = pathPrefix;
this.hostnameVerifier = hostnameVerifier;
this.proxy = proxy;
this.webClientConfigurer = webClientConfigurer;
this.httpClientConfigurer = httpClientConfigurer;
this.clientConfigurers = clientConfigurers;
this.headersSupplier = headersSupplier;
@@ -98,6 +94,11 @@ class DefaultClientConfiguration implements ClientConfiguration {
return Optional.ofNullable(this.sslContext);
}
@Override
public Optional<String> getCaFingerprint() {
return Optional.ofNullable(this.caFingerprint);
}
@Override
public Optional<HostnameVerifier> getHostNameVerifier() {
return Optional.ofNullable(this.hostnameVerifier);
@@ -124,18 +125,6 @@ class DefaultClientConfiguration implements ClientConfiguration {
return Optional.ofNullable(proxy);
}
@Override
public Function<WebClient, WebClient> getWebClientConfigurer() {
return webClientConfigurer;
}
@Deprecated
@Override
public HttpClientConfigCallback getHttpClientConfigurer() {
return httpClientConfigurer;
}
@SuppressWarnings("unchecked")
@Override
public <T> List<ClientConfigurationCallback<?>> getClientConfigurers() {
return clientConfigurers;
@@ -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.
@@ -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.
@@ -26,7 +26,7 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @since 3.2
*/
class InetSocketAddressParser {
public class InetSocketAddressParser {
/**
* Parse a host and port string into a {@link InetSocketAddress}.
@@ -36,7 +36,7 @@ class InetSocketAddressParser {
* @return a {@link InetSocketAddress} that is unresolved to avoid DNS lookups.
* @see InetSocketAddress#createUnresolved(String, int)
*/
static InetSocketAddress parse(String hostPortString, int defaultPort) {
public static InetSocketAddress parse(String hostPortString, int defaultPort) {
Assert.notNull(hostPortString, "HostPortString must not be null");
String host;
@@ -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.
@@ -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.
@@ -0,0 +1,25 @@
/*
* 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.elasticsearch.client;
/**
* @author Peter-Josef Meisch
*/
public class UnsupportedClientOperationException extends RuntimeException {
public UnsupportedClientOperationException(Class<?> clientClass, String operation) {
super("Client %1$s does not support the operation %2$s".formatted(clientClass, operation));
}
}
@@ -0,0 +1,44 @@
/*
* 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.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
/**
* Class to combine an Elasticsearch {@link co.elastic.clients.elasticsearch._types.aggregations.Aggregate} with its
* name. Necessary as the Elasticsearch Aggregate does not know its name.
*
* @author Peter-Josef Meisch
* @since 4.4
*/
public class Aggregation {
private final String name;
private final Aggregate aggregate;
public Aggregation(String name, Aggregate aggregate) {
this.name = name;
this.aggregate = aggregate;
}
public String getName() {
return name;
}
public Aggregate getAggregate() {
return aggregate;
}
}
@@ -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.
@@ -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.
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient;
import co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Transport;
import java.io.IOException;
@@ -32,7 +33,7 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 4.4
*/
public abstract class ChildTemplate<CLIENT extends ApiClient> {
public abstract class ChildTemplate<T extends Transport, CLIENT extends ApiClient<T, CLIENT>> {
protected final CLIENT client;
protected final RequestConverter requestConverter;
@@ -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.
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.transport.ElasticsearchTransport;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
@@ -29,7 +30,8 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverte
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ClusterTemplate extends ChildTemplate<ElasticsearchClusterClient> implements ClusterOperations {
public class ClusterTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchClusterClient>
implements ClusterOperations {
public ClusterTemplate(ElasticsearchClusterClient client, ElasticsearchConverter elasticsearchConverter) {
super(client, elasticsearchConverter);
@@ -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.
@@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.elasticsearch.core.convert.GeoConverters;
@@ -67,9 +68,13 @@ class CriteriaFilterProcessor {
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
if (chainedCriteria.isOr()) {
// todo #1973
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
queriesForEntries(chainedCriteria).forEach(boolQueryBuilder::should);
filterQueries.add(new Query(boolQueryBuilder.build()));
} else if (chainedCriteria.isNegating()) {
// todo #1973
Collection<? extends Query> negatingFilters = buildNegatingFilter(criteria.getField().getName(),
criteria.getFilterCriteriaEntries());
filterQueries.addAll(negatingFilters);
} else {
filterQueries.addAll(queriesForEntries(chainedCriteria));
}
@@ -85,11 +90,28 @@ class CriteriaFilterProcessor {
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
filterQueries.forEach(boolQueryBuilder::must);
BoolQuery boolQuery = boolQueryBuilder.build();
return Optional.of(boolQuery._toQuery());
return Optional.of(new Query(boolQuery));
}
}
}
private static Collection<? extends Query> buildNegatingFilter(String fieldName,
Set<Criteria.CriteriaEntry> filterCriteriaEntries) {
List<Query> negationFilters = new ArrayList<>();
filterCriteriaEntries.forEach(criteriaEntry -> {
Optional<Query> query = queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName);
if (query.isPresent()) {
BoolQuery negatingFilter = QueryBuilders.bool().mustNot(query.get()).build();
negationFilters.add(new Query(negatingFilter));
}
});
return negationFilters;
}
private static Collection<? extends Query> queriesForEntries(Criteria criteria) {
Assert.notNull(criteria.getField(), "criteria must have a field");
@@ -108,37 +130,37 @@ class CriteriaFilterProcessor {
ObjectBuilder<? extends QueryVariant> queryBuilder = null;
switch (key) {
case WITHIN:
case WITHIN -> {
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
queryBuilder = withinQuery(fieldName, (Object[]) value);
break;
case BBOX:
}
case BBOX -> {
Assert.isTrue(value instanceof Object[],
"Value of a boundedBy filter should be an array of one or two values.");
queryBuilder = boundingBoxQuery(fieldName, (Object[]) value);
break;
case GEO_INTERSECTS:
}
case GEO_INTERSECTS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_INTERSECTS filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "intersects");
break;
case GEO_IS_DISJOINT:
}
case GEO_IS_DISJOINT -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_IS_DISJOINT filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "disjoint");
break;
case GEO_WITHIN:
}
case GEO_WITHIN -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_WITHIN filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "within");
break;
case GEO_CONTAINS:
}
case GEO_CONTAINS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_CONTAINS filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "contains");
break;
}
}
return Optional.ofNullable(queryBuilder != null ? queryBuilder.build()._toQuery() : null);
}
private static ObjectBuilder<GeoDistanceQuery> withinQuery(String fieldName, Object[] values) {
private static ObjectBuilder<GeoDistanceQuery> withinQuery(String fieldName, Object... values) {
Assert.noNullElements(values, "Geo distance filter takes 2 not null elements array as parameter.");
Assert.isTrue(values.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
@@ -154,8 +176,7 @@ class CriteriaFilterProcessor {
.distance(dist) //
.distanceType(GeoDistanceType.Plane) //
.location(location -> {
if (values[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) values[0];
if (values[0]instanceof GeoPoint loc) {
location.latlon(latlon -> latlon.lat(loc.getLat()).lon(loc.getLon()));
} else if (values[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) values[0]);
@@ -173,7 +194,7 @@ class CriteriaFilterProcessor {
});
}
private static ObjectBuilder<GeoBoundingBoxQuery> boundingBoxQuery(String fieldName, Object[] values) {
private static ObjectBuilder<GeoBoundingBoxQuery> boundingBoxQuery(String fieldName, Object... values) {
Assert.noNullElements(values, "Geo boundedBy filter takes a not null element array as parameter.");
@@ -218,13 +239,12 @@ class CriteriaFilterProcessor {
)))));
}
private static void twoParameterBBox(GeoBoundingBoxQuery.Builder queryBuilder, Object[] values) {
private static void twoParameterBBox(GeoBoundingBoxQuery.Builder queryBuilder, Object... values) {
Assert.isTrue(allElementsAreOfType(values, GeoPoint.class) || allElementsAreOfType(values, String.class),
" both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
if (values[0] instanceof GeoPoint) {
GeoPoint topLeft = (GeoPoint) values[0];
if (values[0]instanceof GeoPoint topLeft) {
GeoPoint bottomRight = (GeoPoint) values[1];
queryBuilder.boundingBox(bb -> bb //
.tlbr(tlbr -> tlbr //
@@ -307,12 +327,8 @@ class CriteriaFilterProcessor {
StringBuilder sb = new StringBuilder();
sb.append((int) distance.getValue());
switch ((Metrics) distance.getMetric()) {
case KILOMETERS:
sb.append("km");
break;
case MILES:
sb.append("mi");
break;
case KILOMETERS -> sb.append("km");
case MILES -> sb.append("mi");
}
return sb.toString();
@@ -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.
@@ -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.
@@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
import static org.springframework.data.elasticsearch.client.elc.Queries.*;
import static org.springframework.util.StringUtils.*;
import co.elastic.clients.elasticsearch._types.FieldValue;
@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.Field;
@@ -40,6 +39,7 @@ import org.springframework.util.Assert;
* query.
*
* @author Peter-Josef Meisch
* @author Ezequiel Antúnez Camacho
* @since 4.4
*/
class CriteriaQueryProcessor {
@@ -184,7 +184,7 @@ class CriteriaQueryProcessor {
Criteria.OperationKey key = entry.getKey();
Object value = key.hasValue() ? entry.getValue() : null;
String searchText = value != null ? QueryParserUtil.escape(value.toString()) : "UNKNOWN_VALUE";
String searchText = value != null ? escape(value.toString()) : "UNKNOWN_VALUE";
Query.Builder queryBuilder = new Query.Builder();
switch (key) {
@@ -289,8 +289,7 @@ class CriteriaQueryProcessor {
break;
case IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
queryBuilder.bool(bb -> bb //
.must(mb -> mb //
@@ -311,8 +310,7 @@ class CriteriaQueryProcessor {
}
break;
case NOT_IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
queryBuilder.bool(bb -> bb //
.mustNot(mnb -> mnb //
@@ -332,6 +330,13 @@ class CriteriaQueryProcessor {
throw new CriteriaQueryException("value for " + fieldName + " is not an Iterable");
}
break;
case REGEXP:
queryBuilder //
.regexp(rb -> rb //
.field(fieldName) //
.value(value.toString()) //
.boost(boost)); //
break;
default:
throw new CriteriaQueryException("Could not build query for " + entry);
}
@@ -358,11 +363,32 @@ class CriteriaQueryProcessor {
sb.append(' ');
}
sb.append('"');
sb.append(QueryParserUtil.escape(item.toString()));
sb.append(escape(item.toString()));
sb.append('"');
}
}
return sb.toString();
}
/**
* Returns a String where those characters that TextParser expects to be escaped are escaped by a preceding
* <code>\</code>. Copied from Apachae 2 licensed org.apache.lucene.queryparser.flexible.standard.QueryParserUtil
* class
*/
public static String escape(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// These characters are part of the query syntax and must be escaped
if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '['
|| c == ']' || c == '\"' || c == '{' || c == '}' || c == '~' || c == '*' || c == '?' || c == '|' || c == '&'
|| c == '/') {
sb.append('\\');
}
sb.append(c);
}
return sb.toString();
}
}
@@ -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.
@@ -19,6 +19,7 @@ import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.elasticsearch.core.MgetResponse;
import co.elastic.clients.elasticsearch.core.explain.ExplanationDetail;
import co.elastic.clients.elasticsearch.core.get.GetResult;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.NestedIdentity;
import co.elastic.clients.json.JsonData;
@@ -28,6 +29,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
@@ -71,24 +73,17 @@ final class DocumentAdapters {
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
hit.innerHits().forEach((name, innerHitsResult) -> {
// noinspection ReturnOfNull
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null,
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, null,
searchDocument -> null, jsonpMapper));
});
NestedMetaData nestedMetaData = from(hit.nested());
// todo #1973 explanation
Explanation explanation = from(hit.explanation());
// todo #1973 matchedQueries
List<String> matchedQueries = null;
// todo #1973 documentFields
Map<String, List<Object>> documentFields = Collections.emptyMap();
List<String> matchedQueries = hit.matchedQueries();
Document document;
Object source = hit.source();
if (source == null) {
// Elasticsearch provides raw JsonData, so we build the fields into a JSON string
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{");
final boolean[] firstField = { true };
hit.fields().forEach((key, jsonData) -> {
@@ -100,12 +95,29 @@ final class DocumentAdapters {
firstField[0] = false;
});
sb.append('}');
document = Document.parse(sb.toString());
return new EntityAsMap().fromJson(sb.toString());
};
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
Map<String, List<Object>> documentFields = new LinkedHashMap<>();
hitFieldsAsMap.forEach((key, value) -> {
if (value instanceof List) {
// noinspection unchecked
documentFields.put(key, (List<Object>) value);
} else {
documentFields.put(key, Collections.singletonList(value));
}
});
Document document;
Object source = hit.source();
if (source == null) {
document = Document.from(hitFieldsAsMap);
} else {
if (source instanceof EntityAsMap) {
document = Document.from((EntityAsMap) source);
} else if (source instanceof JsonData) {
JsonData jsonData = (JsonData) source;
} else if (source instanceof JsonData jsonData) {
document = Document.from(jsonData.to(EntityAsMap.class));
} else {
@@ -126,8 +138,23 @@ final class DocumentAdapters {
document.setPrimaryTerm(hit.primaryTerm() != null && hit.primaryTerm() > 0 ? hit.primaryTerm() : 0);
float score = hit.score() != null ? hit.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, hit.sort().toArray(new String[0]), documentFields,
highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
return new SearchDocumentAdapter(document, score, hit.sort().stream().map(TypeUtils::toString).toArray(),
documentFields, highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
}
public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {
Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create();
document.setIndex(completionSuggestOption.index());
if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id());
}
float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[] {}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
}
@Nullable
@@ -0,0 +1,37 @@
/*
* 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.elasticsearch.client.elc;
import org.springframework.data.elasticsearch.core.AggregationContainer;
/**
* {@link AggregationContainer} for a {@link Aggregation} that holds Elasticsearch data.
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
private final Aggregation aggregation;
public ElasticsearchAggregation(Aggregation aggregation) {
this.aggregation = aggregation;
}
@Override
public Aggregation aggregation() {
return aggregation;
}
}
@@ -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.
@@ -17,26 +17,62 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* AggregationsContainer implementation for the Elasticsearch aggregations.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4
*/
public class ElasticsearchAggregations implements AggregationsContainer<Map<String, Aggregate>> {
public class ElasticsearchAggregations implements AggregationsContainer<List<ElasticsearchAggregation>> {
private final Map<String, Aggregate> aggregations;
private final List<ElasticsearchAggregation> aggregations;
private final Map<String, ElasticsearchAggregation> aggregationsAsMap;
public ElasticsearchAggregations(Map<String, Aggregate> aggregations) {
this.aggregations = aggregations;
Assert.notNull(aggregations, "aggregations must not be null");
aggregationsAsMap = new HashMap<>();
aggregations.forEach((name, aggregate) -> aggregationsAsMap //
.put(name, new ElasticsearchAggregation(new Aggregation(name, aggregate))));
this.aggregations = new ArrayList<>(aggregationsAsMap.values());
}
@Override
public Map<String, Aggregate> aggregations() {
public List<ElasticsearchAggregation> aggregations() {
return aggregations;
}
/**
* @return the {@link ElasticsearchAggregation}s keyed by aggregation name.
*/
public Map<String, ElasticsearchAggregation> aggregationsAsMap() {
return aggregationsAsMap;
}
/**
* Returns the aggregation that is associated with the specified name.
*
* @param name the name of the aggregation
* @return the aggregation or {@literal null} if not found
*/
@Nullable
public ElasticsearchAggregation get(String name) {
Assert.notNull(name, "name must not be null");
return aggregationsAsMap.get(name);
}
}
@@ -0,0 +1,47 @@
/*
* 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.elasticsearch.client.elc;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* @author Peter-Josef Meisch
* @since 5.0
*/
public class ElasticsearchClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ElasticsearchClientFactoryBean.class);
setConfigurations(element, builder);
return getSourcedBeanDefinition(builder, element, parserContext);
}
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
builder.addPropertyValue("hosts", element.getAttribute("hosts"));
}
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
ParserContext context) {
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(context.extractSource(source));
return definition;
}
}
@@ -0,0 +1,97 @@
/*
* 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.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* ElasticsearchClientFactoryBean
*
* @author Peter-Josef Meisch
* @since 5.0
*/
public class ElasticsearchClientFactoryBean
implements FactoryBean<ElasticsearchClient>, InitializingBean, DisposableBean {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchClientFactoryBean.class);
private @Nullable AutoCloseableElasticsearchClient client;
private String hosts = "http://localhost:9200";
static final String COMMA = ",";
@Override
public void destroy() {
try {
LOGGER.info("Closing elasticSearch client");
if (client != null) {
client.close();
}
} catch (final Exception e) {
LOGGER.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public void afterPropertiesSet() throws Exception {
buildClient();
}
@Override
public ElasticsearchClient getObject() {
if (client == null) {
throw new FactoryBeanNotInitializedException();
}
return client;
}
@Override
public Class<?> getObjectType() {
return ElasticsearchClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
protected void buildClient() throws Exception {
Assert.hasText(hosts, "[Assertion Failed] At least one host must be set.");
var clientConfiguration = ClientConfiguration.builder().connectedTo(hosts).build();
client = (AutoCloseableElasticsearchClient) ElasticsearchClients.createImperative(clientConfiguration);
}
public void setHosts(String hosts) {
this.hosts = hosts;
}
public String getHosts() {
return this.hosts;
}
}
@@ -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.
@@ -19,6 +19,8 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.TransportUtils;
import co.elastic.clients.transport.Version;
import co.elastic.clients.transport.rest_client.RestClientOptions;
import co.elastic.clients.transport.rest_client.RestClientTransport;
@@ -28,32 +30,27 @@ import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Utility class to create the different Elasticsearch clients
@@ -195,6 +192,10 @@ public final class ElasticsearchClients {
}
builder.setHttpClientConfigCallback(clientBuilder -> {
if (clientConfiguration.getCaFingerprint().isPresent()) {
clientBuilder
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
}
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
@@ -226,14 +227,20 @@ public final class ElasticsearchClients {
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof ElasticsearchClientConfigurationCallback) {
ElasticsearchClientConfigurationCallback restClientConfigurationCallback = (ElasticsearchClientConfigurationCallback) clientConfigurer;
if (clientConfigurer instanceof ElasticsearchHttpClientConfigurationCallback restClientConfigurationCallback) {
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
}
}
return clientBuilder;
});
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurationCallback instanceof ElasticsearchRestClientConfigurationCallback configurationCallback) {
builder = configurationCallback.configure(builder);
}
}
return builder;
}
@@ -242,12 +249,27 @@ public final class ElasticsearchClients {
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
: new RestClientOptions(RequestOptions.DEFAULT).toBuilder();
ContentType jsonContentType = Version.VERSION == null ? ContentType.APPLICATION_JSON
: ContentType.create("application/vnd.elasticsearch+json",
new BasicNameValuePair("compatible-with", String.valueOf(Version.VERSION.major())));
Consumer<String> setHeaderIfNotPresent = header -> {
if (transportOptionsBuilder.build().headers().stream() //
.noneMatch((h) -> h.getKey().equalsIgnoreCase(header))) {
// need to add the compatibility header, this is only done automatically when not passing in custom options.
// code copied from RestClientTransport as it is not available outside the package
transportOptionsBuilder.addHeader(header, jsonContentType.toString());
}
};
setHeaderIfNotPresent.accept("Content-Type");
setHeaderIfNotPresent.accept("Accept");
TransportOptions transportOptionsWithHeader = transportOptionsBuilder
.addHeader(X_SPRING_DATA_ELASTICSEARCH_CLIENT, clientType).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper(),
transportOptionsWithHeader);
return transport;
return new RestClientTransport(restClient, new JacksonJsonpMapper(), transportOptionsWithHeader);
}
private static List<String> formattedHosts(List<InetSocketAddress> hosts, boolean useSsl) {
@@ -267,7 +289,9 @@ public final class ElasticsearchClients {
*
* @see ClientLogger
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
private static class HttpLoggingInterceptor implements HttpResponseInterceptor, HttpRequestInterceptor {
@Override
@@ -285,9 +309,9 @@ public final class ElasticsearchClients {
+ ((header.getName().equals("Authorization")) ? ": *****" : ": " + header.getValue()))
.collect(Collectors.joining(", ", "[", "]"));
if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) {
if (request instanceof HttpEntityEnclosingRequest entityRequest
&& ((HttpEntityEnclosingRequest) request).getEntity() != null) {
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
entity.writeTo(buffer);
@@ -314,9 +338,9 @@ public final class ElasticsearchClients {
+ ((header.getName().equals("Authorization")) ? ": *****" : ": " + header.getValue()))
.collect(Collectors.joining(", ", "[", "]"));
// no way of logging the body, in this callback, it is not read yset, later there is no callback possibility in
// no way of logging the body, in this callback, it is not read yet, later there is no callback possibility in
// RestClient or RestClientTransport
ClientLogger.logRawResponse(logId, HttpStatus.resolve(response.getStatusLine().getStatusCode()), headers);
ClientLogger.logRawResponse(logId, response.getStatusLine().getStatusCode(), headers);
}
}
@@ -337,7 +361,7 @@ public final class ElasticsearchClients {
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && httpHeaders != HttpHeaders.EMPTY) {
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
@@ -345,37 +369,37 @@ public final class ElasticsearchClients {
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the RestClient with a {@link HttpAsyncClientBuilder}
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
*
* @since 4.4
*/
public interface ElasticsearchClientConfigurationCallback
public interface ElasticsearchHttpClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static ElasticsearchClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> clientBuilderCallback) {
static ElasticsearchHttpClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
Assert.notNull(clientBuilderCallback, "clientBuilderCallback must not be null");
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
// noinspection NullableProblems
return clientBuilderCallback::apply;
return httpClientBuilderCallback::apply;
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the ReactiveElasticsearchClient with a {@link WebClient}
* the RestClient client with a {@link RestClientBuilder}
*
* @since 4.4
* @since 5.0
*/
public interface WebClientConfigurationCallback extends ClientConfiguration.ClientConfigurationCallback<WebClient> {
public interface ElasticsearchRestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
static WebClientConfigurationCallback from(Function<WebClient, WebClient> webClientCallback) {
static ElasticsearchRestClientConfigurationCallback from(
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
Assert.notNull(webClientCallback, "webClientCallback must not be null");
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
// noinspection NullableProblems
return webClientCallback::apply;
return restClientBuilderCallback::apply;
}
}
}
@@ -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.
@@ -42,7 +42,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
*
* @return configuration, must not be {@literal null}
*/
@Bean
@Bean(name="elasticsearchClientConfiguration")
public abstract ClientConfiguration clientConfiguration();
/**
@@ -52,7 +52,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
* @return RestClient
*/
@Bean
public RestClient restClient(ClientConfiguration clientConfiguration) {
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
@@ -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.
@@ -20,12 +20,17 @@ import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpMapper;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.elasticsearch.client.ResponseException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.ResourceNotFoundException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
/**
@@ -67,14 +72,33 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
}
// todo #1973 index unavailable?
if (ex instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) ex;
if (ex instanceof ElasticsearchException elasticsearchException) {
ErrorResponse response = elasticsearchException.response();
var errorType = response.error().type();
var errorReason = response.error().reason() != null ? response.error().reason() : "undefined reason";
if (response.status() == 404) {
if ("index_not_found_exception".equals(errorType)) {
// noinspection RegExpRedundantEscape
Pattern pattern = Pattern.compile(".*no such index \\[(.*)\\]");
String index = "";
Matcher matcher = pattern.matcher(errorReason);
if (matcher.matches()) {
index = matcher.group(1);
}
return new NoSuchIndexException(index);
}
return new ResourceNotFoundException(errorReason);
}
String body = JsonUtils.toJson(response, jsonpMapper);
if (errorType != null && errorType.contains("validation_exception")) {
return new DataIntegrityViolationException(errorReason);
}
return new UncategorizedElasticsearchException(ex.getMessage(), response.status(), body, ex);
}
@@ -86,20 +110,19 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return null;
}
private boolean isSeqNoConflict(Exception exception) {
// todo #1973 check if this works
private boolean isSeqNoConflict(Throwable exception) {
Integer status = null;
String message = null;
if (exception instanceof RestStatusException) {
RestStatusException statusException = (RestStatusException) exception;
status = statusException.getStatus();
message = statusException.getMessage();
if (exception instanceof ResponseException responseException) {
status = responseException.getResponse().getStatusLine().getStatusCode();
message = responseException.getMessage();
} else if (exception.getCause() != null) {
return isSeqNoConflict(exception.getCause());
}
if (status != null && message != null) {
return status == 409 && message.contains("type=version_conflict_engine_exception")
return status == 409 && message.contains("type\":\"version_conflict_engine_exception")
&& message.contains("version conflict, required seqNo");
}
@@ -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.
@@ -15,14 +15,19 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -30,6 +35,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
@@ -40,6 +47,7 @@ import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
@@ -47,10 +55,12 @@ import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -59,10 +69,13 @@ import org.springframework.util.Assert;
* Elasticsearch client.
*
* @author Peter-Josef Meisch
* @author Hamid Rahimi
* @since 4.4
*/
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
private final ElasticsearchClient client;
private final RequestConverter requestConverter;
private final ResponseConverter responseConverter;
@@ -70,6 +83,17 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private final ElasticsearchExceptionTranslator exceptionTranslator;
// region _initialization
public ElasticsearchTemplate(ElasticsearchClient client) {
Assert.notNull(client, "client must not be null");
this.client = client;
this.jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper);
exceptionTranslator = new ElasticsearchExceptionTranslator(jsonpMapper);
}
public ElasticsearchTemplate(ElasticsearchClient client, ElasticsearchConverter elasticsearchConverter) {
super(elasticsearchConverter);
@@ -91,16 +115,20 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// region child templates
@Override
public IndexOperations indexOps(Class<?> clazz) {
return new IndicesTemplate(client.indices(), elasticsearchConverter, clazz);
return new IndicesTemplate(client.indices(), getClusterTemplate(), elasticsearchConverter, clazz);
}
@Override
public IndexOperations indexOps(IndexCoordinates index) {
return new IndicesTemplate(client.indices(), elasticsearchConverter, index);
return new IndicesTemplate(client.indices(), getClusterTemplate(), elasticsearchConverter, index);
}
@Override
public ClusterOperations cluster() {
return getClusterTemplate();
}
private ClusterTemplate getClusterTemplate() {
return new ClusterTemplate(client.cluster(), elasticsearchConverter);
}
// endregion
@@ -137,7 +165,12 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(queries, "queries must not be null");
Assert.notNull(bulkOptions, "bulkOptions must not be null");
Assert.notNull(index, "index must not be null");
doBulkOperation(queries, bulkOptions, index);
}
@Override
@@ -145,8 +178,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, clazz, index,
getRefreshPolicy());
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index, getRefreshPolicy());
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
@@ -155,12 +188,25 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
co.elastic.clients.elasticsearch.core.UpdateResponse<Document> response = execute(
client -> client.update(request, Document.class));
return UpdateResponse.of(result(response.result()));
}
@Override
public ByQueryResponse updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(updateQuery, "updateQuery must not be null");
Assert.notNull(index, "index must not be null");
UpdateByQueryRequest request = requestConverter.documentUpdateByQueryRequest(updateQuery, index,
getRefreshPolicy());
UpdateByQueryResponse byQueryResponse = execute(client -> client.updateByQuery(request));
return responseConverter.byQueryResponse(byQueryResponse);
}
@Override
@@ -176,8 +222,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Object queryObject = query.getObject();
if (queryObject != null) {
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.id(),
indexResponse.seqNo(), indexResponse.primaryTerm(), indexResponse.version())));
query.setObject(updateIndexedObject(queryObject, new IndexedObjectInformation(indexResponse.id(),
indexResponse.index(), indexResponse.seqNo(), indexResponse.primaryTerm(), indexResponse.version())));
}
return indexResponse.id();
@@ -226,7 +272,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
client -> client.reindex(reindexRequestES));
if (reindexResponse.task() == null) {
// todo #1973 check behaviour and create issue in ES if necessary
throw new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request");
}
@@ -247,19 +292,18 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// endregion
@Override
protected String getClusterVersion() {
public String getClusterVersion() {
return execute(client -> client.info().version().number());
}
@Override
protected String getVendor() {
public String getVendor() {
return "Elasticsearch";
}
@Override
protected String getRuntimeLibraryVersion() {
return Version.VERSION.toString();
public String getRuntimeLibraryVersion() {
return Version.VERSION != null ? Version.VERSION.toString() : "0.0.0.?";
}
// region search operations
@@ -269,7 +313,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
Assert.notNull(index, "index must not be null");
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, true, false);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
true);
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
@@ -280,11 +325,22 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(clazz, "clazz must not be null");
Assert.notNull(index, "index must not be null");
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
if (query instanceof SearchTemplateQuery searchTemplateQuery) {
return doSearch(searchTemplateQuery, clazz, index);
} else {
return doSearch(query, clazz, index);
}
}
protected <T> SearchHits<T> doSearch(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false);
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
// noinspection DuplicatedCode
ReadDocumentCallback<T> readDocumentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponse.EntityCreator<T> entityCreator = getEntityCreator(readDocumentCallback);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
@@ -292,6 +348,18 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return callback.doWith(SearchDocumentResponseBuilder.from(searchResponse, entityCreator, jsonpMapper));
}
protected <T> SearchHits<T> doSearch(SearchTemplateQuery query, Class<T> clazz, IndexCoordinates index) {
var searchTemplateRequest = requestConverter.searchTemplate(query, routingResolver.getRouting(), index);
var searchTemplateResponse = execute(client -> client.searchTemplate(searchTemplateRequest, EntityAsMap.class));
// noinspection DuplicatedCode
ReadDocumentCallback<T> readDocumentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponse.EntityCreator<T> entityCreator = getEntityCreator(readDocumentCallback);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponseBuilder.from(searchTemplateResponse, entityCreator, jsonpMapper));
}
@Override
protected <T> SearchHits<T> doSearch(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
@@ -306,20 +374,21 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected <T> SearchScrollHits<T> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
public <T> SearchScrollHits<T> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(query.getPageable(), "pageable of query must not be null.");
SearchRequest request = requestConverter.searchRequest(query, clazz, index, false, scrollTimeInMillis);
SearchRequest request = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index, false,
scrollTimeInMillis);
SearchResponse<EntityAsMap> response = execute(client -> client.search(request, EntityAsMap.class));
return getSearchScrollHits(clazz, index, response);
}
@Override
protected <T> SearchScrollHits<T> searchScrollContinue(String scrollId, long scrollTimeInMillis, Class<T> clazz,
public <T> SearchScrollHits<T> searchScrollContinue(String scrollId, long scrollTimeInMillis, Class<T> clazz,
IndexCoordinates index) {
Assert.notNull(scrollId, "scrollId must not be null");
@@ -331,8 +400,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return getSearchScrollHits(clazz, index, response);
}
private <T, R extends SearchResponse<EntityAsMap>> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz,
IndexCoordinates index, R response) {
private <T> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz, IndexCoordinates index,
ResponseBody<EntityAsMap> response) {
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
@@ -342,7 +411,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected void searchScrollClear(List<String> scrollIds) {
public void searchScrollClear(List<String> scrollIds) {
Assert.notNull(scrollIds, "scrollIds must not be null");
@@ -404,23 +473,128 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return doMultiSearch(multiSearchQueryParameters);
}
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
List<IndexCoordinates> indexes) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.notNull(indexes, "indexes must not be null");
Assert.isTrue(queries.size() == classes.size() && queries.size() == indexes.size(),
"queries, classes and indexes must have the same size");
List<MultiSearchQueryParameter> multiSearchQueryParameters = new ArrayList<>(queries.size());
Iterator<Class<?>> it = classes.iterator();
Iterator<IndexCoordinates> indexesIt = indexes.iterator();
for (Query query : queries) {
Class<?> clazz = it.next();
IndexCoordinates index = indexesIt.next();
multiSearchQueryParameters.add(new MultiSearchQueryParameter(query, clazz, index));
}
return doMultiSearch(multiSearchQueryParameters);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<SearchHits<?>> doMultiSearch(List<MultiSearchQueryParameter> multiSearchQueryParameters) {
throw new UnsupportedOperationException("not implemented");
MsearchRequest request = requestConverter.searchMsearchRequest(multiSearchQueryParameters,
routingResolver.getRouting());
MsearchResponse<EntityAsMap> msearchResponse = execute(client -> client.msearch(request, EntityAsMap.class));
List<MultiSearchResponseItem<EntityAsMap>> responseItems = msearchResponse.responses();
Assert.isTrue(multiSearchQueryParameters.size() == responseItems.size(),
"number of response items does not match number of requests");
List<SearchHits<?>> searchHitsList = new ArrayList<>(multiSearchQueryParameters.size());
Iterator<MultiSearchQueryParameter> queryIterator = multiSearchQueryParameters.iterator();
Iterator<MultiSearchResponseItem<EntityAsMap>> responseIterator = responseItems.iterator();
while (queryIterator.hasNext()) {
MultiSearchQueryParameter queryParameter = queryIterator.next();
MultiSearchResponseItem<EntityAsMap> responseItem = responseIterator.next();
if (responseItem.isResult()) {
Class clazz = queryParameter.clazz;
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz,
queryParameter.index);
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(clazz,
queryParameter.index);
SearchHits<?> searchHits = callback.doWith(
SearchDocumentResponseBuilder.from(responseItem.result(), getEntityCreator(documentCallback), jsonpMapper));
searchHitsList.add(searchHits);
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER
.warn(String.format("multisearch responsecontains failure: {}", responseItem.failure().error().reason()));
}
}
}
return searchHitsList;
}
/**
* value class combining the information needed for a single query in a multisearch request.
*/
private static class MultiSearchQueryParameter {
final Query query;
final Class<?> clazz;
final IndexCoordinates index;
record MultiSearchQueryParameter(Query query, Class<?> clazz, IndexCoordinates index) {
}
public MultiSearchQueryParameter(Query query, Class<?> clazz, IndexCoordinates index) {
this.query = query;
this.clazz = clazz;
this.index = index;
}
@Override
public String openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable) {
Assert.notNull(index, "index must not be null");
Assert.notNull(keepAlive, "keepAlive must not be null");
Assert.notNull(ignoreUnavailable, "ignoreUnavailable must not be null");
var request = requestConverter.searchOpenPointInTimeRequest(index, keepAlive, ignoreUnavailable);
return execute(client -> client.openPointInTime(request)).id();
}
@Override
public Boolean closePointInTime(String pit) {
Assert.notNull(pit, "pit must not be null");
ClosePointInTimeRequest request = requestConverter.searchClosePointInTime(pit);
var response = execute(client -> client.closePointInTime(request));
return response.succeeded();
}
// endregion
// region script methods
@Override
public boolean putScript(Script script) {
Assert.notNull(script, "script must not be null");
var request = requestConverter.scriptPut(script);
return execute(client -> client.putScript(request)).acknowledged();
}
@Nullable
@Override
public Script getScript(String name) {
Assert.notNull(name, "name must not be null");
var request = requestConverter.scriptGet(name);
return responseConverter.scriptResponse(execute(client -> client.getScript(request)));
}
public boolean deleteScript(String name) {
Assert.notNull(name, "name must not be null");
DeleteScriptRequest request = requestConverter.scriptDelete(name);
return execute(client -> client.deleteScript(request)).acknowledged();
}
// endregion
@@ -486,8 +660,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
failedDocuments);
}
return bulkResponse.items().stream()
.map(item -> IndexedObjectInformation.of(item.id(), item.seqNo(), item.primaryTerm(), item.version()))
return bulkResponse.items().stream().map(
item -> new IndexedObjectInformation(item.id(), item.index(), item.seqNo(), item.primaryTerm(), item.version()))
.collect(Collectors.toList());
}
@@ -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.
@@ -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.
@@ -87,10 +87,6 @@ class HighlightQueryBuilder {
builder.boundaryScannerLocale(parameters.getBoundaryScannerLocale());
}
if (parameters.getForceSource()) { // default is false
// todo #1973 parameter missing in new client
}
if (StringUtils.hasLength(parameters.getFragmenter())) {
builder.fragmenter(highlighterFragmenter(parameters.getFragmenter()));
}
@@ -111,10 +107,6 @@ class HighlightQueryBuilder {
builder.order(highlighterOrder(parameters.getOrder()));
}
if (parameters.getPhraseLimit() > -1) {
// todo #1973 parameter missing in new client
}
if (parameters.getPreTags().length > 0) {
builder.preTags(Arrays.asList(parameters.getPreTags()));
}
@@ -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.
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.util.StringUtils.*;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import java.util.List;
@@ -36,15 +37,15 @@ import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.ResourceUtil;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.index.DeleteIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.DeleteTemplateRequest;
import org.springframework.data.elasticsearch.core.index.ExistsIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.ExistsTemplateRequest;
import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.data.elasticsearch.core.index.TemplateData;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
@@ -56,34 +57,42 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 4.4
*/
public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> implements IndexOperations {
public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchIndicesClient>
implements IndexOperations {
private static final Logger LOGGER = LoggerFactory.getLogger(IndicesTemplate.class);
// we need a cluster client as well because ES has put some methods from the indices API into the cluster client
// (component templates)
private final ClusterTemplate clusterTemplate;
protected final ElasticsearchConverter elasticsearchConverter;
@Nullable protected final Class<?> boundClass;
@Nullable protected final IndexCoordinates boundIndex;
public IndicesTemplate(ElasticsearchIndicesClient client, ElasticsearchConverter elasticsearchConverter,
Class<?> boundClass) {
public IndicesTemplate(ElasticsearchIndicesClient client, ClusterTemplate clusterTemplate,
ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
super(client, elasticsearchConverter);
Assert.notNull(clusterTemplate, "cluster must not be null");
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null");
Assert.notNull(boundClass, "boundClass may not be null");
this.clusterTemplate = clusterTemplate;
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = boundClass;
this.boundIndex = null;
}
public IndicesTemplate(ElasticsearchIndicesClient client, ElasticsearchConverter elasticsearchConverter,
IndexCoordinates boundIndex) {
public IndicesTemplate(ElasticsearchIndicesClient client, ClusterTemplate clusterTemplate,
ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
super(client, elasticsearchConverter);
Assert.notNull(clusterTemplate, "cluster must not be null");
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null");
Assert.notNull(boundIndex, "boundIndex must not be null");
this.clusterTemplate = clusterTemplate;
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = null;
this.boundIndex = boundIndex;
@@ -187,7 +196,6 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> i
Assert.notNull(clazz, "clazz must not be null");
// load mapping specified in Mapping annotation if present
// noinspection DuplicatedCode
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
if (mappingAnnotation != null) {
@@ -277,12 +285,22 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> i
@Override
public Map<String, Set<AliasData>> getAliases(String... aliasNames) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(aliasNames, "aliasNames must not be null");
GetAliasRequest getAliasRequest = requestConverter.indicesGetAliasRequest(aliasNames, null);
var getAliasResponse = execute(client -> client.getAlias(getAliasRequest));
return responseConverter.indicesGetAliasData(getAliasResponse);
}
@Override
public Map<String, Set<AliasData>> getAliasesForIndex(String... indexNames) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(indexNames, "indexNames must not be null");
GetAliasRequest getAliasRequest = requestConverter.indicesGetAliasRequest(null, indexNames);
var getAliasResponse = execute(client -> client.getAlias(getAliasRequest));
return responseConverter.indicesGetAliasData(getAliasResponse);
}
@Override
@@ -327,6 +345,87 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> i
return execute(client -> client.deleteTemplate(deleteTemplateRequestES)).acknowledged();
}
@Override
public boolean putIndexTemplate(PutIndexTemplateRequest putIndexTemplateRequest) {
co.elastic.clients.elasticsearch.indices.PutIndexTemplateRequest putIndexTemplateRequestES = requestConverter
.indicesPutIndexTemplateRequest(putIndexTemplateRequest);
return execute(client -> client.putIndexTemplate(putIndexTemplateRequestES)).acknowledged();
}
@Override
public boolean existsIndexTemplate(ExistsIndexTemplateRequest existsIndexTemplateRequest) {
Assert.notNull(existsIndexTemplateRequest, "existsIndexTemplateRequest must not be null");
co.elastic.clients.elasticsearch.indices.ExistsIndexTemplateRequest existsTemplateRequestES = requestConverter
.indicesExistsIndexTemplateRequest(existsIndexTemplateRequest);
return execute(client -> client.existsIndexTemplate(existsTemplateRequestES)).value();
}
@Override
public List<TemplateResponse> getIndexTemplate(GetIndexTemplateRequest getIndexTemplateRequest) {
Assert.notNull(getIndexTemplateRequest, "getIndexTemplateRequest must not be null");
co.elastic.clients.elasticsearch.indices.GetIndexTemplateRequest getIndexTemplateRequestES = requestConverter
.indicesGetIndexTemplateRequest(getIndexTemplateRequest);
var getIndexTemplateResponse = execute(client -> client.getIndexTemplate(getIndexTemplateRequestES));
return responseConverter.getIndexTemplates(getIndexTemplateResponse);
}
@Override
public boolean deleteIndexTemplate(DeleteIndexTemplateRequest deleteIndexTemplateRequest) {
Assert.notNull(deleteIndexTemplateRequest, "deleteIndexTemplateRequest must not be null");
co.elastic.clients.elasticsearch.indices.DeleteIndexTemplateRequest deleteIndexTemplateRequestES = requestConverter
.indicesDeleteIndexTemplateRequest(deleteIndexTemplateRequest);
return execute(client -> client.deleteIndexTemplate(deleteIndexTemplateRequestES)).acknowledged();
}
@Override
public boolean putComponentTemplate(PutComponentTemplateRequest putComponentTemplateRequest) {
Assert.notNull(putComponentTemplateRequest, "putComponentTemplateRequest must not be null");
co.elastic.clients.elasticsearch.cluster.PutComponentTemplateRequest putComponentTemplateRequestES = requestConverter
.clusterPutComponentTemplateRequest(putComponentTemplateRequest);
// the new Elasticsearch client has this call in the cluster index
return clusterTemplate.execute(client -> client.putComponentTemplate(putComponentTemplateRequestES)).acknowledged();
}
@Override
public boolean existsComponentTemplate(ExistsComponentTemplateRequest existsComponentTemplateRequest) {
Assert.notNull(existsComponentTemplateRequest, "existsComponentTemplateRequest must not be null");
co.elastic.clients.elasticsearch.cluster.ExistsComponentTemplateRequest existsComponentTemplateRequestES = requestConverter
.clusterExistsComponentTemplateRequest(existsComponentTemplateRequest);
return clusterTemplate.execute(client -> client.existsComponentTemplate(existsComponentTemplateRequestES)).value();
}
@Override
public List<TemplateResponse> getComponentTemplate(GetComponentTemplateRequest getComponentTemplateRequest) {
co.elastic.clients.elasticsearch.cluster.GetComponentTemplateRequest getComponentTemplateRequestES = requestConverter
.clusterGetComponentTemplateRequest(getComponentTemplateRequest);
var response = clusterTemplate.execute(client -> client.getComponentTemplate(getComponentTemplateRequestES));
return responseConverter.clusterGetComponentTemplates(response);
}
@Override
public boolean deleteComponentTemplate(DeleteComponentTemplateRequest deleteComponentTemplateRequest) {
Assert.notNull(deleteComponentTemplateRequest, "deleteComponentTemplateRequest must not be null");
co.elastic.clients.elasticsearch.cluster.DeleteComponentTemplateRequest deleteComponentTemplateRequestES = requestConverter
.clusterDeleteComponentTemplateRequest(deleteComponentTemplateRequest);
return clusterTemplate.execute(client -> client.deleteComponentTemplate(deleteComponentTemplateRequestES))
.acknowledged();
}
@Override
public List<IndexInformation> getInformation(IndexCoordinates indexCoordinates) {
@@ -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.
@@ -20,9 +20,11 @@ import jakarta.json.stream.JsonGenerator;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
@@ -41,13 +43,28 @@ final class JsonUtils {
JsonGenerator generator = mapper.jsonProvider().createGenerator(baos);
mapper.serialize(object, generator);
generator.close();
String jsonMapping = "{}";
String json = "{}";
try {
jsonMapping = baos.toString("UTF-8");
json = baos.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
LOGGER.warn("could not read json", e);
}
return jsonMapping;
return json;
}
@Nullable
public static String queryToJson(@Nullable co.elastic.clients.elasticsearch._types.query_dsl.Query query, JsonpMapper mapper) {
if (query == null) {
return null;
}
var baos = new ByteArrayOutputStream();
var generator = mapper.jsonProvider().createGenerator(baos);
query.serialize(generator, mapper);
generator.close();
return baos.toString(StandardCharsets.UTF_8);
}
}
@@ -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.
@@ -15,14 +15,21 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.KnnQuery;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.json.JsonData;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -31,18 +38,39 @@ import org.springframework.util.Assert;
* Elasticsearch Client library.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4
*/
public class NativeQuery extends BaseQuery {
@Nullable private final Query query;
@Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
@Nullable private Query filter;
// note: the new client does not have pipeline aggs, these are just set up as normal aggs
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse;
private List<SortOptions> sortOptions = Collections.emptyList();
private Map<String, JsonData> searchExtensions = Collections.emptyMap();
@Nullable private KnnQuery knnQuery;
public NativeQuery(NativeQueryBuilder builder) {
super(builder);
this.query = builder.getQuery();
this.filter = builder.getFilter();
this.aggregations.putAll(builder.getAggregations());
this.suggester = builder.getSuggester();
this.fieldCollapse = builder.getFieldCollapse();
this.sortOptions = builder.getSortOptions();
this.searchExtensions = builder.getSearchExtensions();
if (builder.getSpringDataQuery() != null) {
Assert.isTrue(!NativeQuery.class.isAssignableFrom(builder.getSpringDataQuery().getClass()),
"Cannot add an NativeQuery in a NativeQuery");
}
this.springDataQuery = builder.getSpringDataQuery();
this.knnQuery = builder.getKnnQuery();
}
public NativeQuery(@Nullable Query query) {
@@ -58,20 +86,9 @@ public class NativeQuery extends BaseQuery {
return query;
}
public void addAggregation(String name, Aggregation aggregation) {
Assert.notNull(name, "name must not be null");
Assert.notNull(aggregation, "aggregation must not be null");
aggregations.put(name, aggregation);
}
public void setAggregations(Map<String, Aggregation> aggregations) {
Assert.notNull(aggregations, "aggregations must not be null");
this.aggregations.clear();
this.aggregations.putAll(aggregations);
@Nullable
public Query getFilter() {
return filter;
}
public Map<String, Aggregation> getAggregations() {
@@ -83,8 +100,37 @@ public class NativeQuery extends BaseQuery {
return suggester;
}
public void setSuggester(@Nullable Suggester suggester) {
this.suggester = suggester;
@Nullable
public FieldCollapse getFieldCollapse() {
return fieldCollapse;
}
public List<SortOptions> getSortOptions() {
return sortOptions;
}
public Map<String, JsonData> getSearchExtensions() {
return searchExtensions;
}
/**
* @see NativeQueryBuilder#withQuery(org.springframework.data.elasticsearch.core.query.Query).
* @since 5.1
*/
public void setSpringDataQuery(@Nullable org.springframework.data.elasticsearch.core.query.Query springDataQuery) {
this.springDataQuery = springDataQuery;
}
/**
* @since 5.1
*/
@Nullable
public KnnQuery getKnnQuery() {
return knnQuery;
}
@Nullable
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
return springDataQuery;
}
}

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