1
0
mirror of synced 2026-07-07 18:50:00 +00:00

Compare commits

...

205 Commits

Author SHA1 Message Date
Oliver Gierke ff1f7676b2 DATAES-268 - Release version 2.0.3 (Hopper SR3). 2016-09-20 10:54:51 +02:00
Oliver Gierke 10a11ad22f DATAES-268 - Prepare 2.0.3 (Hopper SR3). 2016-09-20 10:54:17 +02:00
Oliver Gierke 0978c47fb9 DATAES-268 - Updated changelog. 2016-09-20 10:54:14 +02:00
Oliver Gierke acc81f89fb DATAES-281 - Polishing.
Added unit test for change in DefaultResultMapperTests. Tweaked and simplified integration tests.

Make use of IdentifierAccessor in ElasticsearcTemplate.getPersistentEntityId(…). Added missing generics in DefaultResultMapper.

Removed unused imports, fixed copyright ranges, authors.

Original pull request: #156.
2016-08-20 10:07:59 +02:00
Young Gu 4fb2b33066 DATAES-281 - Setting identifier values now uses PersistentPropertyAccessor API.
DefaultResultMapper and ElasticsearchTemplate now use the PersistentPropertyAccessor API to set identifier values on objects instead of using reflection.

Original pull request: #156.
2016-08-20 10:04:09 +02:00
Oliver Gierke 20d8103a19 DATAES-250 - Updated changelog. 2016-07-28 08:50:39 +02:00
Artur Konczak c799e8d1c8 DATAES-262 - added profile to build project with ES 2.3.3 2016-06-17 09:31:49 +01:00
Oliver Gierke e4e6b09161 DATAES-251 - After release cleanups. 2016-06-15 14:17:54 +02:00
Oliver Gierke 511da0fadf DATAES-251 - Prepare next development iteration. 2016-06-15 14:17:52 +02:00
Oliver Gierke 6090f4a5db DATAES-251 - Release version 2.0.2 (Hopper SR2). 2016-06-15 13:45:53 +02:00
Oliver Gierke c964ce5dac DATAES-251 - Prepare 2.0.2 (Hopper SR2). 2016-06-15 13:44:54 +02:00
Oliver Gierke 2f50462857 DATAES-251 - Updated changelog. 2016-06-15 13:44:49 +02:00
Oliver Gierke eab54cb546 DATAES-249 - After release cleanups. 2016-04-06 22:51:45 +02:00
Oliver Gierke 6bdb45a0f8 DATAES-249 - Prepare next development iteration. 2016-04-06 22:51:43 +02:00
Oliver Gierke 793e6ab843 DATAES-249 - Release version 2.0.1 (Hopper SR1). 2016-04-06 22:32:15 +02:00
Oliver Gierke 182684e56c DATAES-249 - Prepare 2.0.1 (Hopper SR1). 2016-04-06 22:31:41 +02:00
Oliver Gierke 33e1611186 DATAES-249 - Updated changelog. 2016-04-06 22:31:38 +02:00
Oliver Gierke 55ccfe5c0b DATAES-245 - Prepare next development iteration. 2016-04-06 16:38:19 +02:00
Oliver Gierke a362361ace DATAES-245 - Release version 2.0 GA (Hopper). 2016-04-06 16:35:59 +02:00
Oliver Gierke d9e66aa532 DATAES-245 - Prepare 2.0 GA (Hopper). 2016-04-06 16:34:47 +02:00
Oliver Gierke 5094482b6c DATAES-245 - Updated changelog. 2016-04-06 16:34:42 +02:00
Artur Konczak b6a8f2f832 DATAES-248 - updated jira link to point to correct project on jira 2016-04-05 10:42:17 +01:00
Mark Paluch c856680523 DATAES-248 - Add pull request template.
Original pull request: #143.
2016-04-05 10:23:05 +01:00
Artur Konczak ef0831fa33 DATAES-105 - support for IP6 2016-04-04 10:31:32 +01:00
Mohsin Husen b2f0300856 DATAES-240 - After release cleanups. 2016-03-18 10:44:11 +00:00
Oliver Gierke 246da57993 DATAES-240 - After release cleanups. 2016-03-18 11:16:08 +01:00
Oliver Gierke 72ca7477fc DATAES-240 - Prepare next development iteration. 2016-03-18 11:15:51 +01:00
Oliver Gierke 3a41435155 DATAES-240 - Release version 2.0 RC1 (Hopper). 2016-03-18 11:15:00 +01:00
Oliver Gierke cbb3a7afc4 DATAES-240 - Prepare 2.0 RC1 (Hopper). 2016-03-18 11:06:58 +01:00
Oliver Gierke 800b10b2b1 DATAES-240 - Updated changelog. 2016-03-18 11:06:55 +01:00
Mark Paluch 023214a344 DATAES-234 - Use Set to store qualifiers in CDI extension.
Qualifiers are now stored in their original set instead of using the toString representation.
2016-03-17 14:52:39 +00:00
Mohsin Husen 477e8e2a07 DATAES-241 - Remove commons-collections dependency 2016-03-16 15:36:01 +00:00
Mohsin Husen 374acbdd63 DATAES-124 - ElasticSearchTemplate should expose client 2016-03-15 11:34:41 +00:00
Markus Ackermann 8bb4ac3653 DATAES-238 - Fix ElasticsearchTemplate.prepareUpdate(): preserve routing
* need to specify Locale.ENGLISH in String.format()
  to make tests succeed on non-english locales

* that's because the String.format involves floating
  point numbers which are formatted differently in
  some locales

* ElasticsearchTemplate.prepareUpdate() properly copies routing
  from given UpdateRequest

* add unit tests to ElasticsearchTemplateParentChildTests
  for update of a child document
2016-03-15 11:16:57 +00:00
Mohsin Husen 907571e550 DATAES-211 - clean up
update readme
2016-03-11 12:04:14 +00:00
Mohsin Husen f98d888151 DATAES-240 - Bump version to 2.0
update readme
2016-03-09 09:55:12 +00:00
Mohsin Husen a8d915f830 DATAES-241 - Remove commons-collections dependency and use CollectionUtils from Spring Utils 2016-03-08 15:24:51 +00:00
Mohsin Husen f493db7774 DATAES-240 - Bump version to 2.0
There are breaking changes that demands a major version upgrade of spring-data-elasticsearch
2016-03-07 14:43:01 +00:00
Mohsin Husen c4b4a8c45d DATAES-188 - Source filtering feature Implementation 2016-03-03 14:53:18 +00:00
Joseph Moore 1cf18a4a50 DATAES-237 - path-configuration fails to load configs from inside jars 2016-02-26 15:18:12 +00:00
Mohsin Husen 6535caff8a DATAES-236 - fix test for build failure 2016-02-26 15:00:36 +00:00
Mohsin Husen 2aa7ed1c23 DATAES-236 - Clear the search contexts associated with specified scroll id 2016-02-26 14:54:51 +00:00
Mohsin Husen 0f6004ae94 DATAES-211 - update read me for elasticsearch version information. 2016-02-25 09:44:57 +00:00
Mohsin Husen e9576adb1a DATAES-211 - build fix, ignore test 2016-02-24 13:42:07 +00:00
Mohsin Husen ca7a7fb382 DATAES-211 - updated readme for elasticsearch version information 2016-02-24 13:14:03 +00:00
Mohsin Husen c5c9956bc0 DATAES-211 - clean up
remove waitForOperation from refresh()
enhance GeoPoint and Point feature
code clean up
2016-02-24 12:45:37 +00:00
Mohsin Husen 4930415302 DATAES-211 - Fix tests for CDI (jar hell) 2016-02-24 12:45:36 +00:00
Mohsin Husen 42fc41b2eb DATAES-211 - clean up
remove facet feature as it is removed in elasticsearch 2.x
2016-02-24 12:45:36 +00:00
Mohsin Husen 4e0cbb8966 DATAES-211
changes in AliasRequest
 fix tests due to changes in mapping, geo point, alias etc
2016-02-24 12:45:36 +00:00
Mohsin Husen 95194961dc DATAES-211 - move groovy scripting into test folder 2016-02-24 12:45:36 +00:00
Mohsin Husen 6a2b5fd945 DATAES-211 - entity should have suggest mapping 2016-02-24 12:45:35 +00:00
Mohsin Husen 4efec54e1d DATAES-211 - incorporate changes in GeoDistance 2016-02-24 12:45:35 +00:00
Mohsin Husen c7313b52eb DATAES-211 - index analyzer is removed. use analyzer for search and index analyzer or use search analyzer. 2016-02-24 12:45:35 +00:00
Mohsin Husen a05a597123 DATAES-211 - index analyzer is removed. use analyzer for search and index analyzer or use search analyzer. 2016-02-24 12:45:35 +00:00
Mohsin Husen 61880671a4 DATAES-211 - enhance multi field support as multifield type is removed 2016-02-24 12:45:35 +00:00
Mohsin Husen 09455006ab DATAES-211 - enable groovy for scripting 2016-02-24 12:45:35 +00:00
Mohsin Husen 924467ec94 DATAES-211 -clean up 2016-02-24 12:45:34 +00:00
Artur Konczak 8963123ffe DATAES-211 - updated to ES-2.2, test fixes round 1 2016-02-24 12:45:34 +00:00
Oliver Gierke a853ca359a DATAES-231 - After release cleanups. 2016-02-12 15:43:57 +01:00
Oliver Gierke 76d8d5fe3e DATAES-231 - Prepare next development iteration. 2016-02-12 15:43:39 +01:00
Oliver Gierke de821410f2 DATAES-231 - Release version 1.4 M1 (Hopper). 2016-02-12 15:42:47 +01:00
Oliver Gierke ba8bd886d4 DATAES-231 - Prepare 1.4 M1 (Hopper). 2016-02-12 15:36:20 +01:00
Oliver Gierke fd4b74313f DATAES-231 - Updated changelog. 2016-02-12 15:36:16 +01:00
Oliver Gierke fad5162b26 DATAES-232 - Added code of conduct.
Added CONTRIBUTING file along the way.
2016-02-02 10:59:29 +01:00
Oliver Gierke d5cdba06b3 DATA_ES-231 - Declare Artifactory Maven plugin to be able to distribute build artifacts. 2016-01-28 15:01:50 +01:00
slowikps 4296760eb0 DATAES-230 - Remove unnecessary type check in AbstractElasticsearchRepository 2016-01-26 15:38:51 +00:00
Ted Liang 403a85b094 DATAES-209 - Dynamic mapping using @Mapping Annotation at field level
fix test

fix comment in test
2016-01-26 15:22:16 +00:00
Mohsin Husen 428cac3642 DATAES-216 - cleanup 2016-01-26 15:04:00 +00:00
Thiago Locatelli ba1fb9fc6f DATAES-216 - Adding support to 'indices_boost' when searching against multiple indices
Applying spring-data code format

Merge branch 'master' of https://github.com/thiagolocatelli/spring-data-elasticsearch into thiagolocatelli-master
2016-01-26 14:57:00 +00:00
pkraeutli 9c91723833 DATAES-224 retain newlines when reading mapping/setting files
Fixed test-settings yaml
2016-01-26 14:42:34 +00:00
Artur Konczak 7dbab75cf5 DATAES-50 - Enabled support for scripts for node client, added missing groovy lib 2015-12-31 02:09:11 +00:00
Ryan Murfitt d6603f8edf DATAES-50 - Added scripted field functionality 2015-12-31 02:08:38 +00:00
Kevin Leturc 727cb5eb09 DATAES-141 - Update ElasticsearchTemplate constructors to init properly the default implementations. 2015-12-28 00:26:20 +00:00
Oliver Gierke aa44da63f9 DATAES-223 - Updated changelog. 2015-12-18 11:26:11 +01:00
Kevin Leturc 23e1e9f46a DATAES-217 Propose scan method with a class in order to retrieve index and type from Document 2015-12-17 00:47:01 +00:00
Mason Chan 751302d6f0 DATAES-143 added support for turning off auto index creation based on Document annotation. 2015-12-16 01:53:49 +00:00
Oliver Gierke 485f0e7252 DATAES-221 - Adapted to API changes in Spring Data Commons.
Related ticket: DATACMNS-89.
2015-12-14 21:13:33 +01:00
dianandonov aa9c47516c DATAES-208 - Allow node configuration to honor the server settings in @Document 2015-12-12 02:18:24 +00:00
Artur Konczak 21ff7e50cf DATAES-206 - added GEO_POINT support for Point from Spring Data Commons 2015-12-08 12:03:25 +00:00
Oliver Gierke 909caa732b DATAES-212 - Updated changelog. 2015-11-16 08:51:36 +01:00
Nicolas François 5b850c61e2 DATAES-210 - Typo in error message 2015-11-04 16:41:44 +00:00
Mohsin Husen 9981ad0dba DATAES-94 - upgrade to latest elasticsearch 1.7.3 driver 2015-11-04 16:11:57 +00:00
Oliver Gierke bcc763b97b DATAES-202 - Updated changelog. 2015-10-14 13:57:59 +02:00
Mohsin Husen 131b172ce2 DATAES-94 - upgrade to latest elasticsearch 1.7.2 driver 2015-09-30 17:04:44 +01:00
Kevin Leturc 7d645ac5e1 DATAES-200 Set persistent entity index and type in query when no indices/types are set. 2015-09-23 13:23:00 +02:00
Artur Konczak 4350234299 DATAES-179 - added support for Attachment field type 2015-09-19 00:16:59 +01:00
Artur Konczak 5a648bcb7a DATAES-172 - added test to cover geohash prefix 2015-09-18 16:16:46 +01:00
dianandonov 6a06bbfbb9 DATAES-172 - Added support for "geohash_prefix" and "geohash_precision" on GeoPointField annotation 2015-09-17 23:10:51 +01:00
Mohsin Husen c2f13c3820 DATAES-194 - Tests should clean up "data" directory. 2015-09-17 12:02:51 +01:00
Artur Konczak 49708d38c0 DATAES-171 - added support for missing query keywords 2015-09-17 09:11:57 +01:00
Mohsin Husen d9212630b9 DATAES-193 - After release cleanups 2015-09-02 11:53:44 +01:00
Oliver Gierke 9682e7ee28 DATAES-193 - After release cleanups. 2015-09-01 12:11:03 +02:00
Spring Buildmaster a6d4109d0b DATAES-193 - Prepare next development iteration. 2015-09-01 01:53:56 -07:00
Spring Buildmaster 6a6c44401d DATAES-193 - Release version 1.3.0.RELEASE (Gosling GA). 2015-09-01 01:53:54 -07:00
Oliver Gierke 7f4ee37be3 DATAES-193 - Prepare 1.3.0.RELEASE (Gosling GA). 2015-09-01 10:13:36 +02:00
Oliver Gierke bc8b5b8bd8 DATAES-193 - Updated changelog. 2015-09-01 10:13:33 +02:00
Artur Konczak 6cdc765b24 DATAES-137 - set missing MappingContext in ElasticsearchRepositoryFactoryBean 2015-08-29 01:49:18 +01:00
Oliver Gierke f9e0b3a4f9 DATAES-186 - After release cleanups. 2015-08-04 14:09:22 +02:00
Spring Buildmaster 52fec26a1d DATAES-186 - Prepare next development iteration. 2015-08-04 04:29:19 -07:00
Spring Buildmaster eadf51ec27 DATAES-186 - Release version 1.3.0.RC1 (Gosling RC1). 2015-08-04 04:29:17 -07:00
Oliver Gierke af3121a165 DATAES-186 - Prepare 1.3.0.RC1 (Gosling RC1). 2015-08-04 11:48:44 +02:00
Oliver Gierke 9b28ca805b DATAES-186 - Updated changelog. 2015-08-04 11:48:34 +02:00
Oliver Gierke 9d8bc61401 DATAES-184 - Updated changelog. 2015-07-28 16:50:21 +02:00
Oliver Gierke ae1a873705 DATASOLR-250 - Switched to PersistentEntityInformation to avoid reference to BeanWrapper.
MappingElasticsearchEntityInformation now extends PersistentEntityInformation over AbstractInformation to be able to use the default implementations of getId(…) and getIdType().

Related ticket: DATACMNS-738.
2015-07-22 21:24:11 +02:00
Oliver Gierke 9524b2cc8d DATAES-173 - Updated changelog. 2015-07-01 10:22:17 +02:00
Oliver Gierke d83a6541aa DATAES-174 - Updated changelog. 2015-07-01 08:01:11 +02:00
Oliver Gierke fdc62dd7d6 DATAES-175 - Updated changelog. 2015-06-30 14:21:36 +02:00
Kevin Leturc 416daef2f9 DATAES-165 - Add Java 8 Steram support in custom repositories. 2015-06-21 01:39:50 +01:00
Kevin Leturc 576495932e DATAES-167 - Add scan method for CriteriaQuery. 2015-06-21 01:11:39 +01:00
Matija Obreza c9ffc4417a DATAES-127 - Include total, other and missing count in TermResult
Include totalCount, otherCount and missingCount from TermsFacet in TermResult.
2015-06-21 00:13:41 +01:00
Artur Konczak e80ddd249f DATAES-103 - fix for schema parsing errors with Spring 4.0.x 2015-06-20 23:03:03 +01:00
Oliver Gierke 9a8c04c16f DATAES-168 - After release cleanups. 2015-06-02 11:40:22 +02:00
Spring Buildmaster d117f6f515 DATAES-168 - Prepare next development iteration. 2015-06-02 01:39:58 -07:00
Spring Buildmaster 3e1099740b DATAES-168 - Release version 1.3.0.M1 (Gosling M1). 2015-06-02 01:39:54 -07:00
Oliver Gierke 763e61c862 DATAES-168 - Prepare 1.3.0.M1 (Gosling M1). 2015-06-02 08:25:10 +02:00
Oliver Gierke ea02e99263 DATAES-168 - Updated changelog. 2015-06-02 08:24:59 +02:00
Artur Konczak a3828c6161 Additional tests to cover synonyms 2015-05-22 13:20:06 +02:00
Mohsin Husen 4a90020dc1 DATAES-157 - support deleteBy operation 2015-05-15 13:21:38 +01:00
Mohsin Husen 01a67eef05 DATAES-164 - CriteriaQuery equals method has to use AND operator instead of OR 2015-05-15 12:23:23 +01:00
Mohsin Husen 0825f15ec3 DATAES-94 - upgrade to latest elasticsearch 1.5.2 driver 2015-05-12 15:54:49 +01:00
XAVIER VANDEMEULEBROUCKE 7aaf7e1782 DATAES-158 - DATAES-159 2015-05-11 00:32:42 +01:00
Kevin Leturc 455df72923 DATAES-155 setPersistentEntityId in mapResutls(MultiGetResponse, Class). 2015-05-11 00:19:33 +01:00
Artur Konczak cee493a6c7 DATAES-162 - Adapt to changes in Spring Data Commons.
Tweaked method signatures in ElasticsearchRepositoryFactory after some signature changes in Spring Data Commons. Use newly introduced getTragetRepositoryViaReflection(…) to obtain the repository instance via the super class.

Added repositoryBaseClass() attribute to @EnableMongoRepositories.

Related tickets: DATACMNS-542
2015-05-09 00:45:34 +01:00
Mohsin Husen fa996716ab DATAES-154 - After release cleanups. 2015-03-24 10:23:33 +00:00
Oliver Gierke f789521210 DATAES-154 - After release cleanups. 2015-03-23 13:58:01 +01:00
Spring Buildmaster 57d6c17212 DATAES-154 - Prepare next development iteration. 2015-03-23 05:00:31 -07:00
Spring Buildmaster d6d4cced28 DATAES-154 - Release version 1.2.0.RELEASE. 2015-03-23 05:00:28 -07:00
Oliver Gierke 77e6e5e045 DATAES-154 - Prepare 1.2.0.RELEASE (Fowler GA). 2015-03-23 12:27:18 +01:00
Oliver Gierke b5a4bf0ad0 DATAES-154 - Updated changelog. 2015-03-23 12:04:07 +01:00
Christoph Strobl a1993c2b7e DATAES-154 - Polish reference docs before release.
Updated copyright years. Fall back to locally available Spring Data Commons reference docs as the remote variant doesn't seem to work currently. Included reference to the return type listing appendix from Spring Data Commons.
2015-03-23 11:21:25 +01:00
Mohsin Husen 37c710d236 DATAES-94 - upgrade to latest elasticsearch 1.4.4 driver 2015-03-22 13:46:24 +00:00
Oliver Gierke e5648eca43 DATAES-152 - After release cleanups. 2015-03-05 19:48:41 +01:00
Spring Buildmaster 66b937b058 DATAES-152 - Prepare next development iteration. 2015-03-05 07:44:28 -08:00
Spring Buildmaster 6b68d947c8 DATAES-152 - Release version 1.2.0.RC1. 2015-03-05 07:44:26 -08:00
Oliver Gierke 6b6102e175 DATAES-152 - Prepare 1.2.0.RC1 (Fowler RC1). 2015-03-05 16:27:20 +01:00
Oliver Gierke 3a9df392be DATAES-152 - Updated changelog. 2015-03-05 15:54:35 +01:00
Kevin Leturc 97bcf28a04 DATAES-135 - Add a flag in configuration to consider nested repositories 2015-03-01 20:52:05 +00:00
Kevin Leturc 956944265a DATAES-151 Update findAll(Iterable) method to do a multiGet query instead of search query. 2015-02-20 16:01:21 +01:00
Artur Konczak fad9a56036 DATAES-136 - added support for basePackage configuration for @EnableElasticsearchRepositories 2015-02-12 01:17:19 +00:00
Kevin Leturc cf3c4f7c06 DATAES-115 Add count before projection query to retrieve all results. 2015-02-01 17:10:31 +01:00
Mohsin Husen d92137247f DATAES-46 - Apply code formatting 2015-01-30 17:19:06 +00:00
Franck MARCHAND bb62148bb7 DATAES-129 : Custom Repository Method for simple geo request does not work 2015-01-30 17:07:00 +00:00
msachs 7cf7cb9168 DATAES-132 - adding possibility to configure mapping with nested and include in parent option 2015-01-30 16:02:21 +00:00
gpanthe cd9ed2cde8 DATAES-100: Allow configurable searchTimeout. 2015-01-30 13:51:32 +00:00
Oliver Gierke 14c63b3b32 DATAES-146 - Updated changelog. 2015-01-28 20:51:56 +01:00
Oliver Gierke b5b41bc739 DATAES-145 - Updated changelog. 2015-01-28 10:10:42 +01:00
Mohsin Husen 00441e743b DATAES-94 - upgrade to latest elasticsearch 1.4.2 driver 2015-01-27 17:32:51 +00:00
dmaass 0eaa4db1af DATAES-141 add new constructor to fix: Mapping Context is null in
DefaultResultMapper when using custom Entitymapper
(cherry picked from commit 2367d89)
2014-12-19 22:28:54 +00:00
avolz acecc003cb DATAES-140 add value "no" in Enum FieldIndex.
(cherry picked from commit 1b867cf)
2014-12-19 22:28:19 +00:00
Mohsin Husen 5e3a50c85c DATAES-91 - removed merge conflict file 2014-12-08 13:53:03 +00:00
Faisal Feroz 37802761ed DATAES-130 Added support for Spel Expressions in Type
- Added support for Spel Expressions in index type

- Renamed SpELEntityTest to SpELEntityTests so they run when
called from maven
2014-12-07 01:12:00 +00:00
Mewes Kochheim 1255dfa89d DATAES-91 - fixed payload feature, added corresponding unit tests to cover it, fixed code format 2014-12-06 23:34:43 +00:00
Oliver Gierke 0ad982b233 DATAES-134 - After release cleanups. 2014-12-01 13:37:34 +01:00
Spring Buildmaster 8db944bd54 DATAES-134 - Prepare next development iteration. 2014-12-01 04:03:31 -08:00
Spring Buildmaster 2ff155f4e5 DATAES-134 - Release version 1.2.0.M1. 2014-12-01 04:03:29 -08:00
Oliver Gierke f58868cef9 DATAES-134 - Prepare 1.2.0.M1 (Fowler M1). 2014-12-01 12:21:52 +01:00
Oliver Gierke 49c3853462 DATAES-134 - Updated changelog. 2014-12-01 12:21:49 +01:00
Oliver Gierke 1b307ac6dd DATAES-133 - Updated changelog. 2014-11-28 12:17:04 +01:00
Artur Konczak 7fdab4aa5c DATAES-91 - fixed tests 2014-11-13 16:23:24 +00:00
Felix Peters dfa0823c9a DATAES-91 - completion suggester support 2014-11-13 16:06:07 +00:00
Kevin Leturc 0e37c5d36f DATAES-76 - Add support for mapping generation of inherited fields 2014-10-19 12:10:21 +01:00
Mohsin Husen cd48924528 DATAES-94 - upgrade to latest elasticsearch 1.3.4 driver 2014-10-19 11:42:46 +01:00
Mohsin Husen 4fd865b658 DATAES-128 - After release cleanups. 2014-10-19 10:54:50 +01:00
Oliver Gierke 0c4e82783f DATAES-128 - After release cleanups. 2014-09-15 14:25:52 +02:00
Spring Buildmaster 5338e261a3 DATAES-128 - Prepare next development iteration. 2014-09-05 03:27:09 -07:00
Spring Buildmaster 8ee7570a97 DATAES-128 - Release version 1.1.0.RELEASE. 2014-09-05 03:27:08 -07:00
Oliver Gierke daebfd4f20 DATAES-128 - Prepare 1.1.0.RELEASE (Evans GA). 2014-09-05 09:23:54 +02:00
Oliver Gierke 7591c46874 DATAES-128 - Updated changelog. 2014-09-05 09:23:44 +02:00
Oliver Gierke f2a047aaaf DATAES-122 - Updated changelog. 2014-09-05 07:56:32 +02:00
Mark Paluch 95fc878c53 DATAES-123 - Improved detection of custom implementations for CDI repositories.
Adapted to API changes in CDI extension.

Related ticket: DATACMNS-565.
Original pull request: #74.
2014-09-01 15:30:05 +02:00
anton-martynov 89730fe7b1 DATAES-125 - The name of the isAnnotated method doesn't correspond to method body 2014-08-31 11:27:32 +01:00
Kevin Leturc 1ea9920a0d DATAES-106 - Add support for countBy projection 2014-08-31 10:50:57 +01:00
Oliver Gierke c866664acc DATAES-120 - Polished reference documentation. 2014-08-22 15:27:44 +02:00
Mohsin Husen 479a670e2f DATAES-94 - upgrade to latest elasticsearch 1.3.2 driver 2014-08-17 09:50:55 +01:00
Mohsin Husen 097c5f68ce DATAES-46 - Apply code formatting 2014-08-17 09:47:36 +01:00
Oliver Gierke a66dc08975 DATAES-119 - After release cleanups. 2014-08-13 16:29:32 +02:00
Spring Buildmaster f17bb3ac18 DATAES-119 - Prepare next development iteration. 2014-08-13 07:13:32 -07:00
Spring Buildmaster 594b4536a1 DATAES-119 - Release version 1.1.0.RC1. 2014-08-13 07:13:30 -07:00
Oliver Gierke 76952c7fc2 DATAES-119 - Prepare 1.1.0.RC1 (Evans RC1). 2014-08-13 15:05:21 +02:00
Oliver Gierke 858ae6eb03 DATAES-119 - Updated changelog. 2014-08-13 15:05:12 +02:00
Oliver Gierke b0513c2a8c DATAES-107 - Updated changelog. 2014-08-13 11:08:01 +02:00
Greg Turnquist 31797fad6e DATAES-114 - Migrate to Asciidoctor for reference documentation.
Converted existing docbook material to Asciidoctor and applied Spring styling.
2014-08-12 14:52:00 +02:00
Mark Paluch b77b8ba0f2 DATAES-113 Add support for custom implementations in CDI repositories 2014-08-10 12:05:46 +01:00
Mohsin Husen 1f99dcb911 DATAES-97 - UpdateQuery and UpdateBuilder should use UpdateRequest instead of the IndexRequest 2014-08-10 11:58:10 +01:00
xjrk58 0317dcb275 DATAES-97 - Use UpdateRequest instead of IndexRequest 2014-08-10 11:25:45 +01:00
Mohsin Husen cf54ba4e8f DATAES-46 - Apply code formatting 2014-08-09 15:14:37 +01:00
Florian Hopf 705adf25f2 DATAES-117 - Displaying proper error message for entities that don't have an Id 2014-08-09 14:57:32 +01:00
Florian Hopf a08a6fbde9 DATAES-116 - Fixed link typo 2014-08-09 14:47:48 +01:00
Artur Konczak 2bca32bb86 DATAES-93 Added test to validate SpEL for indexName 2014-08-08 09:32:35 +01:00
gpanthe 873dcca561 DATAES-93 Make fully ApplicationContextAware, allows bean resolvers. 2014-08-07 21:15:52 +01:00
gpanthe a92af0e3e5 DATAES-93 Allow Spring EL usage in index name attribute of @Document 2014-08-07 21:15:51 +01:00
Mohsin Husen fa5d1b1e49 DATAES-46 - Apply code formatting 2014-07-20 17:07:57 +01:00
Franck MARCHAND 6d61dceab9 DATAES-89 - implements missing "near" in ElasticsearchQueryCreator 2014-07-17 16:56:32 +02:00
Franck MARCHAND 5faf54b67c DATAES-89 - implements missing "within" in ElasticsearchQueryCreator 2014-07-17 16:51:42 +02:00
Oliver Gierke cb7983120e DATAES-102 - After release cleanups. 2014-07-10 20:40:44 +02:00
Spring Buildmaster fd054138b4 DATAES-102 - Prepare next development iteration. 2014-07-10 09:57:19 -07:00
Spring Buildmaster 6b998d2974 DATAES-102 - Release version 1.1.0.M1. 2014-07-10 09:57:16 -07:00
Oliver Gierke 1b292a7001 DATAES-102 - Prepare 1.1.0.M1 (Evans M1). 2014-07-10 17:14:50 +02:00
Oliver Gierke 24c12585c2 DATAES-102 - Updated changelog. 2014-07-10 17:14:38 +02:00
Oliver Gierke 2f28a9c5c6 DATAES-99 - Updated changelog. 2014-07-10 17:10:56 +02:00
Mohsin Husen 6b74c6b8da DATAES-94 - upgrade to latest 1.2.2 driver 2014-07-10 13:19:00 +01:00
Martin Sachs 5a691986a1 DATAES-94 - Bump to support ES 1.2.0 2014-07-05 10:16:20 +01:00
Rizwan Idrees ab7e870d5f DATAES-96 - Add support for aggregation in ElasticsearchTemplate 2014-06-12 17:25:00 +01:00
Oliver Gierke 369b2eb969 DATAES-90 - After release cleanups. 2014-05-20 19:50:33 +02:00
Spring Buildmaster 222c479fb5 DATAES-90 - Prepare next development iteration. 2014-05-20 09:47:45 -07:00
215 changed files with 10034 additions and 5423 deletions
+9
View File
@@ -0,0 +1,9 @@
Thank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.
Make sure that:
- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).
- [ ] There is a ticket in the bug tracker for the project in our [JIRA](https://jira.spring.io/browse/DATAES).
- [ ] 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).
- [ ] You provide your full name and an email address registered with your GitHub account. If youre a first-time submitter, make sure you have completed the [Contributors License Agreement form](https://support.springsource.com/spring_committer_signup).
+27
View File
@@ -0,0 +1,27 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io.
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
This Code of Conduct is adapted from the http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].
+3
View File
@@ -0,0 +1,3 @@
= Spring Data contribution guidelines
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc[here].
+22 -6
View File
@@ -11,12 +11,12 @@ Guide
------------
* [Reference Documentation](http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/)
* [PDF Documentation](http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/pdf/spring-data-elasticsearch-reference.pdf)
* [API Documentation](http://docs.spring.io/spring-data/elasticsearch/docs/current/api/)
* [Spring Data Project](http://project.spring.io/spring-data)
* [Spring Data Project](http://projects.spring.io/spring-data)
* [Sample Test Application](https://github.com/BioMedCentralLtd/spring-data-elasticsearch-sample-application)
* [Issues](https://jira.springsource.org/browse/DATAES)
* [Spring Data Elasticsearch Google Group](https://groups.google.com/d/forum/spring-data-elasticsearch-devs)
* For more detailed questions, use the [forum](http://forum.springsource.org/forumdisplay.php?f=80).
* [Questions](http://stackoverflow.com/questions/tagged/spring-data-elasticsearch)
Quick Start
@@ -31,7 +31,7 @@ Add the Maven dependency:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>1.0.0.RELEASE</version>
<version>1.3.4.RELEASE</version>
</dependency>
```
@@ -42,7 +42,7 @@ the appropriate dependency version.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<repository>
@@ -52,6 +52,22 @@ the appropriate dependency version.
</repository>
```
### Note:
1) Spring data elaticsearch until 1.0.0.RELEASE version is on elasticsearch 1.1.x library, which uses java 1.6 or later version.
2) From spring data elasticsearch 1.1.0.RELEASE the version of elasticsearch is 1.3.x which is supporting java 1.7 or later.
| spring data elasticsearch | elasticsearch |
|:-----------------------------------:|:-------------:|
| 2.0.0.RC1 | 2.2.0 |
| 1.4.0.M1 | 1.7.3 |
| 1.3.0.RELEASE | 1.5.2 |
| 1.2.0.RELEASE | 1.4.4 |
| 1.1.0.RELEASE | 1.3.2 |
| 1.0.0.RELEASE | 1.1.1 |
### ElasticsearchRepository
A default implementation of ElasticsearchRepository, aligning to the generic Repository Interfaces, is provided. Spring can do the Repository implementation for you depending on method names in the interface definition.
@@ -239,4 +255,4 @@ Before we accept a non-trivial patch or pull request we will need you to sign th
Code formatting for [Eclipse and Intellij](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide)
[More information about contributing to Spring Data](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.md)
[More information about contributing to Spring Data](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc)
+202 -150
View File
@@ -1,178 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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 http://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>1.0.0.RELEASE</version>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>2.0.3.RELEASE</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.8.3.RELEASE</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
<name>Spring Data Elasticsearch</name>
<description>Spring Data Implementation for Elasticsearch</description>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<name>Spring Data Elasticsearch</name>
<description>Spring Data Implementation for Elasticsearch</description>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<properties>
<dist.key>DATAES</dist.key>
<dist.key>DATAES</dist.key>
<commonscollections>3.2.1</commonscollections>
<commonslang>2.6</commonslang>
<elasticsearch>1.1.1</elasticsearch>
<springdata.commons>1.8.0.RELEASE</springdata.commons>
<commonscollections>3.2.1</commonscollections>
<commonslang>2.6</commonslang>
<elasticsearch>2.2.0</elasticsearch>
<springdata.commons>1.12.3.RELEASE</springdata.commons>
</properties>
</properties>
<dependencies>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!-- SPRING DATA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<!-- SPRING DATA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${springdata.commons}</version>
</dependency>
<!-- APACHE -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commonslang}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commonscollections}</version>
</dependency>
<!-- APACHE -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commonslang}</version>
</dependency>
<!-- JODA Time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime}</version>
</dependency>
<!-- JODA Time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime}</version>
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch}</version>
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch}</version>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson}</version>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson}</version>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans.test</groupId>
<artifactId>cditest-owb</artifactId>
<version>${webbeans}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>test</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openwebbeans.test</groupId>
<artifactId>cditest-owb</artifactId>
<version>${webbeans}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jcdi_1.0_spec</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-atinject_1.0_spec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok}</version>
<scope>test</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</dependencies>
<developers>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
<timezone>+0</timezone>
</developer>
</developers>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>http://repo.spring.io/libs-release</url>
</repository>
</repositories>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
</testResource>
<testResource>
<targetPath>${basedir}/target/test-home-dir</targetPath>
<directory>${basedir}/src/test/es-modules/${elasticsearch}</directory>
<includes>
<include>**/*</include>
</includes>
</testResource>
</testResources>
</build>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>http://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
<profiles>
<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>
<profile>
<id>es2.3.3</id>
<properties>
<elasticsearch>2.3.3</elasticsearch>
</properties>
</profile>
<ciManagement>
<system>Bamboo</system>
<url>http://build.springsource.org/browse/SPRINGDATAES</url>
</ciManagement>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.jfrog.buildinfo</groupId>
<artifactId>artifactory-maven-plugin</artifactId>
<inherited>false</inherited>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<issueManagement>
<system>JIRA</system>
<url>https://jira.springsource.org/browse/DATAES</url>
</issueManagement>
<developers>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
<timezone>+0</timezone>
</developer>
</developers>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
<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>
<ciManagement>
<system>Bamboo</system>
<url>http://build.springsource.org/browse/SPRINGDATAES</url>
</ciManagement>
<issueManagement>
<system>JIRA</system>
<url>https://jira.springsource.org/browse/DATAES</url>
</issueManagement>
</project>
-59
View File
@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<bookinfo>
<title>Spring Data Elasticsearch</title>
<authorgroup>
<author>
<firstname>BioMed Central</firstname>
<surname>Development Team</surname>
</author>
</authorgroup>
<legalnotice>
<para>
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.
</para>
</legalnotice>
<copyright>
<year>2013-2014</year>
<holder>The original author(s)</holder>
</copyright>
</bookinfo>
<toc/>
<xi:include href="preface.xml"/>
<part id="reference">
<title>Reference Documentation</title>
<xi:include href="https://raw.github.com/SpringSource/spring-data-commons/1.8.0.RELEASE/src/docbkx/repositories.xml">
<xi:fallback
href="../../../spring-data-commons/src/docbkx/repositories.xml"/>
</xi:include>
<xi:include href="reference/data-elasticsearch.xml"/>
<xi:include href="reference/elasticsearch-misc.xml"/>
</part>
<part id="appendix">
<title>Appendix</title>
<xi:include href="https://raw.github.com/SpringSource/spring-data-commons/1.8.0.RELEASE/src/docbkx/repository-namespace-reference.xml">
<xi:fallback
href="../../../spring-data-commons/src/docbkx/repository-namespace-reference.xml"/>
</xi:include>
<xi:include href="https://raw.github.com/SpringSource/spring-data-commons/1.8.0.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
<xi:fallback
href="../../../spring-data-commons/src/docbkx/repository-query-keywords-reference.xml"/>
</xi:include>
</part>
</book>
-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<preface id="preface">
<title>Preface</title>
<para>The Spring Data Elasticsearch project applies core Spring
concepts to
the
development of solutions using the Elasticsearch Search
Engine.
We have povided a "template" as a high-level abstraction for
storing,querying,sorting and faceting documents. You will notice
similarities
to the Spring data solr and
mongodb support in the Spring Framework.
</para>
<section id="project">
<title>Project Metadata</title>
<itemizedlist spacing="compact">
<listitem>
<para>
Version Control -
<ulink
url="git://github.com/BioMedCentralLtd/spring-data-elasticsearch.git">
git://github.com/BioMedCentralLtd/spring-data-elasticsearch.git
</ulink>
</para>
</listitem>
</itemizedlist>
</section>
<section id="requirements">
<title>Requirements</title>
<para>
Requires
<ulink url="http://www.elasticsearch.org/download/">Elasticsearch</ulink>
0.20.2 and above or optional dependency or not even that if you are
using Embedded Node Client
</para>
</section>
</preface>
-508
View File
@@ -1,508 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="elasticsearch.repositories">
<title>Elasticsearch Repositories</title>
<abstract>
<para>This chapter includes details of the Elasticsearch repository
implementation.
</para>
</abstract>
<section id="elasticsearch.introduction">
<title>Introduction</title>
<section id="elasticsearch.namespace">
<title>Spring Namespace</title>
<para>
The Spring Data Elasticsearch module contains a custom namespace
allowing
definition of repository beans as well as elements for
instantiating
a
<classname>ElasticsearchServer</classname>
.
</para>
<para>
Using the
<code>repositories</code>
element looks up Spring Data repositories as described in
<xref linkend="repositories.create-instances"/>
.
</para>
<example>
<title>Setting up Elasticsearch repositories using Namespace</title>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"&gt;
&lt;elasticsearch:repositories base-package="com.acme.repositories" /&gt;
&lt;/beans&gt;</programlisting>
</example>
<para>
Using the
<code>Transport Client</code>
or
<code>Node Client</code>
element registers an instance of
<code>Elasticsearch Server</code>
in the context.
<example>
<title>Transport Client using Namespace</title>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"&gt;
&lt;elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" /&gt;
&lt;/beans&gt; </programlisting>
</example>
<example>
<title>Node Client using Namespace</title>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"&gt;
&lt;elasticsearch:node-client id="client" local="true"" /&gt;
&lt;/beans&gt; </programlisting>
</example>
</para>
</section>
<section id="elasticsearch.annotation">
<title>Annotation based configuration</title>
<para>The Spring Data Elasticsearch repositories support cannot only
be
activated through an XML namespace but also using an annotation
through JavaConfig.
</para>
<example>
<title>Spring Data Elasticsearch repositories using JavaConfig
</title>
<programlisting language="java">
@Configuration
@EnableElasticsearchRepositories(basePackages =
"org/springframework/data/elasticsearch/repositories")
static class Config {
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
}
}
</programlisting>
<para>
The configuration above sets up an
<classname>Embedded Elasticsearch Server</classname>
which is used by the
<classname>ElasticsearchTemplate</classname>
. Spring Data Elasticsearch Repositories are activated using the
<interfacename>@EnableElasticsearchRepositories</interfacename>
annotation, which
essentially carries the same attributes as the XML
namespace does. If no
base package is configured, it will use the
one
the configuration class
resides in.
</para>
</example>
</section>
<section id="elasticsearch.cdi">
<title>Elasticsearch Repositores using CDI</title>
<para>The Spring Data Elasticsearch repositories can also be set up
using CDI
functionality.
</para>
<example>
<title>Spring Data Elasticsearch repositories using JavaConfig
</title>
<programlisting language="java">class ElasticsearchTemplateProducer {
@Produces
@ApplicationScoped
public ElasticsearchOperations createElasticsearchTemplate() {
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
}
}
class ProductService {
private ProductRepository repository;
public Page&lt;Product&gt; findAvailableBookByName(String name, Pageable pageable) {
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
}
@Inject
public void setRepository(ProductRepository repository) {
this.repository = repository;
}
}
</programlisting>
</example>
</section>
</section>
<section id="elasticsearch.query-methods">
<title>Query methods</title>
<section id="elasticsearch.query-methods.finders">
<title>Query lookup strategies</title>
<para>
The Elasticsearch module supports all basic query building
feature as String,Abstract,Criteria or
have
it being derived from the
method name.
</para>
<simplesect>
<title>Declared queries</title>
<para>
Deriving the query from the method name is not always sufficient
and/or may result in unreadable method names. In this case one
might make either use of
<interfacename>@Query</interfacename>
annotation (see
<xref linkend="elasticsearch.query-methods.at-query"/>
).
</para>
</simplesect>
</section>
<section id="elasticsearch.query-methods.criterions">
<title>Query creation</title>
<para>
Generally the query creation mechanism for Elasticsearch works as
described
in
<xref linkend="repositories.query-methods"/>
. Here's a short example
of what a Elasticsearch query method
translates into:
<example>
<title>Query creation from method names</title>
<programlisting language="java">public interface BookRepository extends Repository&lt;Book, String&gt;
{
List&lt;Book&gt; findByNameAndPrice(String name, Integer price);
}
</programlisting>
<para>
The method name above will be translated into the following
Elasticsearch json query
</para>
<programlisting>
{ "bool" :
{ "must" :
[
{ "field" : {"name" : "?"} },
{ "field" : {"price" : "?"} }
] } }
</programlisting>
</example>
</para>
<para>
A list of supported keywords for Elasticsearch is shown below.
<table>
<title>Supported keywords inside method names</title>
<tgroup cols="3">
<colspec colwidth="1*"/>
<colspec colwidth="2*"/>
<colspec colwidth="3*"/>
<thead>
<row>
<entry>Keyword</entry>
<entry>Sample</entry>
<entry>Elasticsearch Query String</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<code>And</code>
</entry>
<entry>
<code>findByNameAndPrice</code>
</entry>
<entry>
<code>{"bool" : {"must" : [ {"field" : {"name" : "?"}},
{"field" : {"price" : "?"}} ]}}
</code>
</entry>
</row>
<row>
<entry>
<code>Or</code>
</entry>
<entry>
<code>findByNameOrPrice</code>
</entry>
<entry>
<code>{"bool" : {"should" : [ {"field" : {"name" : "?"}},
{"field" : {"price" : "?"}} ]}}
</code>
</entry>
</row>
<row>
<entry>
<code>Is</code>
</entry>
<entry>
<code>findByName</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"name" : "?"}}}}</code>
</entry>
</row>
<row>
<entry>
<code>Not</code>
</entry>
<entry>
<code>findByNameNot</code>
</entry>
<entry>
<code>{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>Between</code>
</entry>
<entry>
<code>findByPriceBetween</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>LessThanEqual</code>
</entry>
<entry>
<code>findByPriceLessThan</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"range" : {"price" : {"from" :
null,"to" : ?,"include_lower" : true,"include_upper" :
true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>GreaterThanEqual</code>
</entry>
<entry>
<code>findByPriceGreaterThan</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : null,"include_lower" : true,"include_upper" :
true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>Before</code>
</entry>
<entry>
<code>findByPriceBefore</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"range" : {"price" : {"from" :
null,"to" : ?,"include_lower" : true,"include_upper" :
true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>After</code>
</entry>
<entry>
<code>findByPriceAfter</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : null,"include_lower" : true,"include_upper" :
true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>Like</code>
</entry>
<entry>
<code>findByNameLike</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"name" : {"query" :
"?*","analyze_wildcard" : true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>StartingWith</code>
</entry>
<entry>
<code>findByNameStartingWith</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"name" : {"query" :
"?*","analyze_wildcard" : true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>EndingWith</code>
</entry>
<entry>
<code>findByNameEndingWith</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"name" : {"query" :
"*?","analyze_wildcard" : true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>Contains/Containing</code>
</entry>
<entry>
<code>findByNameContaining</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"name" : {"query" :
"*?*","analyze_wildcard" : true}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>In</code>
</entry>
<entry>
<code>findByNameIn(Collection&lt;String&gt;names)</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"bool" : {"should" : [ {"field" :
{"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>NotIn</code>
</entry>
<entry>
<code>findByNameNotIn(Collection&lt;String&gt;names)</code>
</entry>
<entry>
<code>{"bool" : {"must_not" : {"bool" : {"should" : {"field" :
{"name" : "?"}}}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>Near</code>
</entry>
<entry>
<code>findByStoreNear</code>
</entry>
<entry>
<code>Not Supported Yet !</code>
</entry>
</row>
<row>
<entry>
<code>True</code>
</entry>
<entry>
<code>findByAvailableTrue</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"available" : true}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>False</code>
</entry>
<entry>
<code>findByAvailableFalse</code>
</entry>
<entry>
<code>{"bool" : {"must" : {"field" : {"available" : false}}}}
</code>
</entry>
</row>
<row>
<entry>
<code>OrderBy</code>
</entry>
<entry>
<code>findByAvailableTrueOrderByNameDesc</code>
</entry>
<entry>
<code>{"sort" : [{ "name" : {"order" : "desc"} }],"bool" :
{"must" : {"field" : {"available" : true}}}}
</code>
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section id="elasticsearch.query-methods.at-query">
<title>Using @Query Annotation</title>
<example>
<title>
Declare query at the method using the
<interfacename>@Query</interfacename>
annotation.
</title>
<programlisting language="java">public interface BookRepository extends ElasticsearchRepository&lt;Book,
String&gt; {
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page&lt;Book&gt; findByName(String name,Pageable pageable);
}
</programlisting>
</example>
</section>
</section>
</chapter>
@@ -1,88 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="elasticsearch.misc">
<title>Miscellaneous Elasticsearch Operation Support</title>
<abstract>
<para>
This chapter covers additional support for Elasticsearch operations
that cannot be directly accessed via the repository
interface.
It is
recommended to add those operations as custom
implementation as
described in
<xref linkend="repositories.custom-implementations"/>
.
</para>
</abstract>
<section id="elasticsearch.misc.filter">
<title>Filter Builder</title>
<para>
Filter Builder improves query speed.
</para>
<example>
<programlisting language="java">
private ElasticsearchTemplate elasticsearchTemplate;
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(boolFilter().must(termFilter("id", documentId)))
.build();
Page&lt;SampleEntity&gt; sampleEntities =
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
</programlisting>
</example>
</section>
<section id="elasticsearch.scan.and.scroll">
<title>Using Scan And Scroll For Big Result Set</title>
<para>
Elasticsearch has scan and scroll feature for getting big result set
in chunks.
<interfacename>ElasticsearchTemplate</interfacename>
has scan and scroll methods that can be used as below.
</para>
<example>
<title>
Using Scan and Scroll
</title>
<programlisting language="java">
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withIndices("test-index")
.withTypes("test-type")
.withPageable(new PageRequest(0,1))
.build();
String scrollId = elasticsearchTemplate.scan(searchQuery,1000,false);
List&lt;SampleEntity&gt; sampleEntities = new ArrayList&lt;SampleEntity&gt;();
boolean hasRecords = true;
while (hasRecords){
Page&lt;SampleEntity&gt; page = elasticsearchTemplate.scroll(scrollId, 5000L , new ResultsMapper&lt;SampleEntity&gt;()
{
@Override
public Page&lt;SampleEntity&gt; mapResults(SearchResponse response) {
List&lt;SampleEntity&gt; chunk = new ArrayList&lt;SampleEntity&gt;();
for(SearchHit searchHit : response.getHits()){
if(response.getHits().getHits().length &lt;= 0) {
return null;
}
SampleEntity user = new SampleEntity();
user.setId(searchHit.getId());
user.setMessage((String)searchHit.getSource().get("message"));
chunk.add(user);
}
return new PageImpl&lt;SampleEntity&gt;(chunk);
}
});
if(page != null) {
sampleEntities.addAll(page.getContent());
hasRecords = page.hasNextPage();
}
else{
hasRecords = false;
}
}
}
</programlisting>
</example>
</section>
</chapter>
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
= Spring Data Elasticsearch
BioMed Central Development Team
:revnumber: {version}
:revdate: {localdate}
:toc:
:toc-placement!:
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
(C) 2013-2015 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[]
:leveloffset: +1
include::{spring-data-commons-docs}/repositories.adoc[]
:leveloffset: -1
[[reference]]
= Reference Documentation
:leveloffset: +1
include::reference/data-elasticsearch.adoc[]
include::reference/elasticsearch-misc.adoc[]
:leveloffset: -1
[[appendix]]
= Appendix
:numbered!:
:leveloffset: +1
include::{spring-data-commons-docs}/repository-namespace-reference.adoc[]
include::{spring-data-commons-docs}/repository-populator-namespace-reference.adoc[]
include::{spring-data-commons-docs}/repository-query-keywords-reference.adoc[]
include::{spring-data-commons-docs}/repository-query-return-types-reference.adoc[]
:leveloffset: -1
+20
View File
@@ -0,0 +1,20 @@
= Preface
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine. We have povided a "template" as a high-level abstraction for storing,querying,sorting and faceting documents. You will notice similarities to the Spring data solr and mongodb support in the Spring Framework.
[[project]]
[preface]
== Project Metadata
* Version Control - https://github.com/spring-projects/spring-data-elasticsearch
* Bugtracker - https://jira.spring.io/browse/DATAES
* Release repository - https://repo.spring.io/libs-release
* Milestone repository - https://repo.spring.io/libs-milestone
* Snapshot repository - https://repo.spring.io/libs-snapshot
[[requirements]]
[preface]
== Requirements
Requires http://www.elasticsearch.org/download/[Elasticsearch] 0.20.2 and above or optional dependency or not even that if you are using Embedded Node Client
@@ -0,0 +1,288 @@
[[elasticsearch.repositories]]
= Elasticsearch Repositories
This chapter includes details of the Elasticsearch repository implementation.
[[elasticsearch.introduction]]
== Introduction
[[elasticsearch.namespace]]
=== Spring Namespace
The Spring Data Elasticsearch module contains a custom namespace allowing definition of repository beans as well as elements for instantiating a `ElasticsearchServer` .
Using the `repositories` element looks up Spring Data repositories as described in <<repositories.create-instances>> .
.Setting up Elasticsearch repositories using Namespace
====
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
<elasticsearch:repositories base-package="com.acme.repositories" />
</beans>
----
====
Using the `Transport Client` or `Node Client` element registers an instance of `Elasticsearch Server` in the context.
.Transport Client using Namespace
====
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
<elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" />
</beans>
----
====
.Node Client using Namespace
====
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
<elasticsearch:node-client id="client" local="true"" />
</beans>
----
====
[[elasticsearch.annotation]]
=== Annotation based configuration
The Spring Data Elasticsearch repositories support cannot only be activated through an XML namespace but also using an annotation through JavaConfig.
.Spring Data Elasticsearch repositories using JavaConfig
====
[source,java]
----
@Configuration
@EnableElasticsearchRepositories(basePackages = "org/springframework/data/elasticsearch/repositories")
static class Config {
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
}
}
----
====
The configuration above sets up an `Embedded Elasticsearch Server` which is used by the `ElasticsearchTemplate` . Spring Data Elasticsearch Repositories are activated using the `@EnableElasticsearchRepositories` annotation, which essentially carries the same attributes as the XML namespace does. If no base package is configured, it will use the one the configuration class resides in.
[[elasticsearch.cdi]]
=== Elasticsearch Repositores using CDI
The Spring Data Elasticsearch repositories can also be set up using CDI functionality.
.Spring Data Elasticsearch repositories using JavaConfig
====
[source,java]
----
class ElasticsearchTemplateProducer {
@Produces
@ApplicationScoped
public ElasticsearchOperations createElasticsearchTemplate() {
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
}
}
class ProductService {
private ProductRepository repository;
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
}
@Inject
public void setRepository(ProductRepository repository) {
this.repository = repository;
}
}
----
====
[[elasticsearch.query-methods]]
== Query methods
[[elasticsearch.query-methods.finders]]
=== Query lookup strategies
The Elasticsearch module supports all basic query building feature as String,Abstract,Criteria or have it being derived from the method name.
==== Declared queries
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. In this case one might make either use of `@Query` annotation (see <<elasticsearch.query-methods.at-query>> ).
[[elasticsearch.query-methods.criterions]]
=== Query creation
Generally the query creation mechanism for Elasticsearch works as described in <<repositories.query-methods>> . Here's a short example of what a Elasticsearch query method translates into:
.Query creation from method names
====
[source,java]
----
public interface BookRepository extends Repository<Book, String>
{
List<Book> findByNameAndPrice(String name, Integer price);
}
----
====
The method name above will be translated into the following Elasticsearch json query
[source]
----
{ "bool" :
{ "must" :
[
{ "field" : {"name" : "?"} },
{ "field" : {"price" : "?"} }
]
}
}
----
A list of supported keywords for Elasticsearch is shown below.
[cols="1,2,3", options="header"]
.Supported keywords inside method names
|===
| Keyword
| Sample
| Elasticsearch Query String| `And`
| `findByNameAndPrice`
| `{"bool" : {"must" : [ {"field" : {"name" : "?"}},
{"field" : {"price" : "?"}} ]}}`
| `Or`
| `findByNameOrPrice`
| `{"bool" : {"should" : [ {"field" : {"name" : "?"}},
{"field" : {"price" : "?"}} ]}}`
| `Is`
| `findByName`
| `{"bool" : {"must" : {"field" : {"name" : "?"}}}}`
| `Not`
| `findByNameNot`
| `{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}`
| `Between`
| `findByPriceBetween`
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}`
| `LessThanEqual`
| `findByPriceLessThan`
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
null,"to" : ?,"include_lower" : true,"include_upper" :
true}}}}}`
| `GreaterThanEqual`
| `findByPriceGreaterThan`
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : null,"include_lower" : true,"include_upper" :
true}}}}}`
| `Before`
| `findByPriceBefore`
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
null,"to" : ?,"include_lower" : true,"include_upper" :
true}}}}}`
| `After`
| `findByPriceAfter`
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
?,"to" : null,"include_lower" : true,"include_upper" :
true}}}}}`
| `Like`
| `findByNameLike`
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
"?*","analyze_wildcard" : true}}}}}`
| `StartingWith`
| `findByNameStartingWith`
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
"?*","analyze_wildcard" : true}}}}}`
| `EndingWith`
| `findByNameEndingWith`
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
"*?","analyze_wildcard" : true}}}}}`
| `Contains/Containing`
| `findByNameContaining`
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
"*?*","analyze_wildcard" : true}}}}}`
| `In`
| `findByNameIn(Collection<String>names)`
| `{"bool" : {"must" : {"bool" : {"should" : [ {"field" :
{"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}`
| `NotIn`
| `findByNameNotIn(Collection<String>names)`
| `{"bool" : {"must_not" : {"bool" : {"should" : {"field" :
{"name" : "?"}}}}}}`
| `Near`
| `findByStoreNear`
| `Not Supported Yet !`
| `True`
| `findByAvailableTrue`
| `{"bool" : {"must" : {"field" : {"available" : true}}}}`
| `False`
| `findByAvailableFalse`
| `{"bool" : {"must" : {"field" : {"available" : false}}}}`
| `OrderBy`
| `findByAvailableTrueOrderByNameDesc`
| `{"sort" : [{ "name" : {"order" : "desc"} }],"bool" :
{"must" : {"field" : {"available" : true}}}}`
|===
[[elasticsearch.query-methods.at-query]]
=== Using @Query Annotation
.Declare query at the method using the `@Query` annotation.
====
[source,java]
----
public interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page<Book> findByName(String name,Pageable pageable);
}
----
====
@@ -0,0 +1,72 @@
[[elasticsearch.misc]]
= Miscellaneous Elasticsearch Operation Support
This chapter covers additional support for Elasticsearch operations that cannot be directly accessed via the repository interface. It is recommended to add those operations as custom implementation as described in <<repositories.custom-implementations>> .
[[elasticsearch.misc.filter]]
== Filter Builder
Filter Builder improves query speed.
====
[source,java]
----
private ElasticsearchTemplate elasticsearchTemplate;
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(boolFilter().must(termFilter("id", documentId)))
.build();
Page<SampleEntity> sampleEntities =
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
----
====
[[elasticsearch.scan.and.scroll]]
== Using Scan And Scroll For Big Result Set
Elasticsearch has scan and scroll feature for getting big result set in chunks. `ElasticsearchTemplate` has scan and scroll methods that can be used as below.
.Using Scan and Scroll
====
[source,java]
----
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withIndices("test-index")
.withTypes("test-type")
.withPageable(new PageRequest(0,1))
.build();
String scrollId = elasticsearchTemplate.scan(searchQuery,1000,false);
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
boolean hasRecords = true;
while (hasRecords){
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L , new ResultsMapper<SampleEntity>()
{
@Override
public Page<SampleEntity> mapResults(SearchResponse response) {
List<SampleEntity> chunk = new ArrayList<SampleEntity>();
for(SearchHit searchHit : response.getHits()){
if(response.getHits().getHits().length <= 0) {
return null;
}
SampleEntity user = new SampleEntity();
user.setId(searchHit.getId());
user.setMessage((String)searchHit.getSource().get("message"));
chunk.add(user);
}
return new PageImpl<SampleEntity>(chunk);
}
});
if(page != null) {
sampleEntities.addAll(page.getContent());
hasRecords = page.hasNextPage();
}
else{
hasRecords = false;
}
}
}
----
====
@@ -0,0 +1,42 @@
/*
* Copyright 2013 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
*
* http://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.*;
/**
* Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
*
* @author Mewes Kochheim
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface CompletionField {
String searchAnalyzer() default "simple";
String analyzer() default "simple";
boolean payloads() default false;
boolean preserveSeparators() default true;
boolean preservePositionIncrements() default true;
int maxInputLength() default 50;
}
@@ -24,6 +24,7 @@ import org.springframework.data.annotation.Persistent;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mason Chan
*/
@Persistent
@@ -36,6 +37,8 @@ public @interface Document {
String type() default "";
boolean useServerConfiguration() default false;
short shards() default 5;
short replicas() default 1;
@@ -43,4 +46,6 @@ public @interface Document {
String refreshInterval() default "1s";
String indexStoreType() default "fs";
boolean createIndex() default true;
}
@@ -15,7 +15,12 @@
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Rizwan Idrees
@@ -23,10 +28,12 @@ import java.lang.annotation.*;
* @author Artur Konczak
* @author Jonathan Yan
* @author Jakub Vavrik
* @author Kevin Leturc
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {
FieldType type() default FieldType.Auto;
@@ -41,7 +48,9 @@ public @interface Field {
String searchAnalyzer() default "";
String indexAnalyzer() default "";
String analyzer() default "";
String[] ignoreFields() default {};
boolean includeInParent() default false;
}
@@ -18,7 +18,9 @@ package org.springframework.data.elasticsearch.annotations;
/**
* @author Artur Konczak
* @author Mohsin Husen
* @author Alexander Volz
* @author Dennis Maaß
*/
public enum FieldIndex {
not_analyzed, analyzed
not_analyzed, analyzed, no
}
@@ -21,5 +21,5 @@ package org.springframework.data.elasticsearch.annotations;
* @author Artur Konczak
*/
public enum FieldType {
String, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip
String, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment
}
@@ -25,4 +25,8 @@ import java.lang.annotation.*;
@Documented
public @interface GeoPointField {
boolean geoHashPrefix() default false;
String geoHashPrecision() default "0";
}
@@ -25,9 +25,9 @@ import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NestedField {
public @interface InnerField {
String dotSuffix();
String suffix();
FieldType type();
@@ -26,7 +26,7 @@ import org.springframework.data.annotation.Persistent;
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface Mapping {
String mappingPath() default "";
@@ -30,5 +30,5 @@ public @interface MultiField {
public Field mainField();
public NestedField[] otherFields() default {};
public InnerField[] otherFields() default {};
}
@@ -0,0 +1,19 @@
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.*;
/**
* @author Ryan Murfitt
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface ScriptedField {
/**
* (Optional) The name of the scripted field. Defaults to
* the field name.
*/
String name() default "";
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2015 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,9 +17,11 @@ package org.springframework.data.elasticsearch.client;
import static org.elasticsearch.node.NodeBuilder.*;
import java.io.InputStream;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
@@ -40,6 +42,9 @@ public class NodeClientFactoryBean implements FactoryBean<NodeClient>, Initializ
private boolean enableHttp;
private String clusterName;
private NodeClient nodeClient;
private String pathData;
private String pathHome;
private String pathConfiguration;
NodeClientFactoryBean() {
}
@@ -65,13 +70,25 @@ public class NodeClientFactoryBean implements FactoryBean<NodeClient>, Initializ
@Override
public void afterPropertiesSet() throws Exception {
ImmutableSettings.Builder settings = ImmutableSettings.settingsBuilder().put("http.enabled",
String.valueOf(this.enableHttp));
nodeClient = (NodeClient) nodeBuilder().settings(settings).clusterName(this.clusterName).local(this.local).node()
nodeClient = (NodeClient) nodeBuilder().settings(Settings.builder().put(loadConfig())
.put("http.enabled", String.valueOf(this.enableHttp))
.put("path.home", this.pathHome)
.put("path.data", this.pathData))
.clusterName(this.clusterName).local(this.local).node()
.client();
}
private Settings loadConfig() {
if (StringUtils.isNotBlank(pathConfiguration)) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(pathConfiguration);
if (stream != null) {
return Settings.builder().loadFromStream(pathConfiguration, getClass().getClassLoader().getResourceAsStream(pathConfiguration)).build();
}
logger.error(String.format("Unable to read node configuration from file [%s]", pathConfiguration));
}
return Settings.builder().build();
}
public void setLocal(boolean local) {
this.local = local;
}
@@ -84,6 +101,18 @@ public class NodeClientFactoryBean implements FactoryBean<NodeClient>, Initializ
this.clusterName = clusterName;
}
public void setPathData(String pathData) {
this.pathData = pathData;
}
public void setPathHome(String pathHome) {
this.pathHome = pathHome;
}
public void setPathConfiguration(String configuration) {
this.pathConfiguration = configuration;
}
@Override
public void destroy() throws Exception {
try {
@@ -16,8 +16,8 @@
package org.springframework.data.elasticsearch.client;
import static org.apache.commons.lang.StringUtils.*;
import static org.elasticsearch.common.settings.ImmutableSettings.*;
import java.net.InetAddress;
import java.util.Properties;
import org.elasticsearch.client.transport.TransportClient;
@@ -86,24 +86,24 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
}
protected void buildClient() throws Exception {
client = new TransportClient(settings());
client = TransportClient.builder().settings(settings()).build();
Assert.hasText(clusterNodes, "[Assertion failed] clusterNodes settings missing.");
for (String clusterNode : split(clusterNodes, COMMA)) {
String hostName = substringBefore(clusterNode, COLON);
String port = substringAfter(clusterNode, COLON);
String hostName = substringBeforeLast(clusterNode, COLON);
String port = substringAfterLast(clusterNode, COLON);
Assert.hasText(hostName, "[Assertion failed] missing host name in 'clusterNodes'");
Assert.hasText(port, "[Assertion failed] missing port in 'clusterNodes'");
logger.info("adding transport node : " + clusterNode);
client.addTransportAddress(new InetSocketTransportAddress(hostName, Integer.valueOf(port)));
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port)));
}
client.connectedNodes();
}
private Settings settings() {
if (properties != null) {
return settingsBuilder().put(properties).build();
return Settings.builder().put(properties).build();
}
return settingsBuilder()
return Settings.builder()
.put("cluster.name", clusterName)
.put("client.transport.sniff", clientTransportSniff)
.put("client.transport.ignore_cluster_name", clientIgnoreClusterName)
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2015 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.
@@ -43,6 +43,9 @@ public class NodeClientBeanDefinitionParser extends AbstractBeanDefinitionParser
builder.addPropertyValue("local", Boolean.valueOf(element.getAttribute("local")));
builder.addPropertyValue("clusterName", element.getAttribute("cluster-name"));
builder.addPropertyValue("enableHttp", Boolean.valueOf(element.getAttribute("http-enabled")));
builder.addPropertyValue("pathData", element.getAttribute("path-data"));
builder.addPropertyValue("pathHome", element.getAttribute("path-home"));
builder.addPropertyValue("pathConfiguration", element.getAttribute("path-configuration"));
}
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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,6 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
import java.util.Iterator;
@@ -23,36 +22,45 @@ import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.GeoBoundingBoxFilterBuilder;
import org.elasticsearch.index.query.GeoDistanceFilterBuilder;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.index.query.*;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
* CriteriaFilterProcessor
*
* @author Franck Marchand
* @author Mohsin Husen
* @author Artur Konczak
*
*/
class CriteriaFilterProcessor {
FilterBuilder createFilterFromCriteria(Criteria criteria) {
List<FilterBuilder> fbList = new LinkedList<FilterBuilder>();
FilterBuilder filter = null;
QueryBuilder createFilterFromCriteria(Criteria criteria) {
List<QueryBuilder> fbList = new LinkedList<QueryBuilder>();
QueryBuilder filter = null;
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
while (chainIterator.hasNext()) {
FilterBuilder fb = null;
QueryBuilder fb = null;
Criteria chainedCriteria = chainIterator.next();
if (chainedCriteria.isOr()) {
fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{}));
fb = QueryBuilders.boolQuery();
for(QueryBuilder f: createFilterFragmentForCriteria(chainedCriteria)){
((BoolQueryBuilder)fb).should(f);
}
fbList.add(fb);
} else if (chainedCriteria.isNegating()) {
List<FilterBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator());
List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator());
if (!negationFilters.isEmpty()) {
fbList.addAll(negationFilters);
@@ -66,21 +74,23 @@ class CriteriaFilterProcessor {
if (fbList.size() == 1) {
filter = fbList.get(0);
} else {
filter = andFilter(fbList.toArray(new FilterBuilder[]{}));
filter = QueryBuilders.boolQuery();
for(QueryBuilder f: fbList) {
((BoolQueryBuilder)filter).must(f);
}
}
}
return filter;
}
private List<FilterBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) {
private List<QueryBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) {
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getFilterCriteriaEntries().iterator();
List<FilterBuilder> filterList = new LinkedList<FilterBuilder>();
List<QueryBuilder> filterList = new LinkedList<QueryBuilder>();
String fieldName = chainedCriteria.getField().getName();
Assert.notNull(fieldName, "Unknown field");
FilterBuilder filter = null;
QueryBuilder filter = null;
while (it.hasNext()) {
Criteria.CriteriaEntry entry = it.next();
@@ -92,42 +102,53 @@ class CriteriaFilterProcessor {
}
private FilterBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
if (value == null) {
return null;
}
FilterBuilder filter = null;
QueryBuilder filter = null;
switch (key) {
case WITHIN: {
filter = geoDistanceFilter(fieldName);
GeoDistanceQueryBuilder geoDistanceQueryBuilder = QueryBuilders.geoDistanceQuery(fieldName);
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
Object[] valArray = (Object[]) value;
Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter.");
Assert.isTrue(valArray.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String, "First element of a geo distance filter must be a GeoLocation or String");
Assert.isTrue(valArray[1] instanceof String, "Second element of a geo distance filter must be a String");
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point, "First element of a geo distance filter must be a GeoPoint, a Point or a String");
Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance, "Second element of a geo distance filter must be a String or a Distance");
StringBuilder dist = new StringBuilder();
if (valArray[1] instanceof Distance) {
extractDistanceString((Distance) valArray[1], dist);
} else {
dist.append((String) valArray[1]);
}
String dist = (String) valArray[1];
if (valArray[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) valArray[0];
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist);
geoDistanceQueryBuilder.lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else if (valArray[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]);
geoDistanceQueryBuilder.lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else {
String loc = (String) valArray[0];
if (loc.contains(",")) {
String c[] = loc.split(",");
((GeoDistanceFilterBuilder) filter).lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])).distance(dist);
geoDistanceQueryBuilder.lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else {
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist);
geoDistanceQueryBuilder.geohash(loc).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
}
}
filter = geoDistanceQueryBuilder;
break;
}
case BBOX: {
filter = geoBoundingBoxFilter(fieldName);
filter = QueryBuilders.geoBoundingBoxQuery(fieldName);
Assert.isTrue(value instanceof Object[], "Value of a boundedBy filter should be an array of one or two values.");
Object[] valArray = (Object[]) value;
@@ -135,11 +156,11 @@ class CriteriaFilterProcessor {
if (valArray.length == 1) {
//GeoEnvelop
oneParameterBBox((GeoBoundingBoxFilterBuilder) filter, valArray[0]);
oneParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray[0]);
} else if (valArray.length == 2) {
//2x GeoPoint
//2x String
twoParameterBBox((GeoBoundingBoxFilterBuilder) filter, valArray);
twoParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray);
} else {
//error
Assert.isTrue(false, "Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash)).");
@@ -151,9 +172,40 @@ class CriteriaFilterProcessor {
return filter;
}
private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) {
Assert.isTrue(value instanceof GeoBox, "single-element of boundedBy filter must be type of GeoBox");
GeoBox geoBBox = (GeoBox) value;
/**
* extract the distance string from a {@link org.springframework.data.geo.Distance} object.
*
* @param distance distance object to extract string from
* @param sb StringBuilder to build the distance string
*/
private void extractDistanceString(Distance distance, StringBuilder sb) {
// handle Distance object
sb.append((int) distance.getValue());
Metrics metric = (Metrics) distance.getMetric();
switch (metric) {
case KILOMETERS:
sb.append("km");
break;
case MILES:
sb.append("mi");
break;
}
}
private void oneParameterBBox(GeoBoundingBoxQueryBuilder filter, Object value) {
Assert.isTrue(value instanceof GeoBox || value instanceof Box, "single-element of boundedBy filter must be type of GeoBox or Box");
GeoBox geoBBox;
if (value instanceof Box) {
Box sdbox = (Box) value;
geoBBox = GeoBox.fromBox(sdbox);
} else {
geoBBox = (GeoBox) value;
}
filter.topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon());
filter.bottomRight(geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon());
}
@@ -167,7 +219,7 @@ class CriteriaFilterProcessor {
return true;
}
private void twoParameterBBox(GeoBoundingBoxFilterBuilder filter, Object[] values) {
private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object[] values) {
Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class), " both elements of boundedBy filter must be type of GeoPoint or String(format lat,lon or geohash)");
if (values[0] instanceof GeoPoint) {
GeoPoint topLeft = (GeoPoint) values[0];
@@ -182,12 +234,12 @@ class CriteriaFilterProcessor {
}
}
private List<FilterBuilder> buildNegationFilter(String fieldName, Iterator<Criteria.CriteriaEntry> it) {
List<FilterBuilder> notFilterList = new LinkedList<FilterBuilder>();
private List<QueryBuilder> buildNegationFilter(String fieldName, Iterator<Criteria.CriteriaEntry> it) {
List<QueryBuilder> notFilterList = new LinkedList<QueryBuilder>();
while (it.hasNext()) {
Criteria.CriteriaEntry criteriaEntry = it.next();
FilterBuilder notFilter = notFilter(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
QueryBuilder notFilter = QueryBuilders.boolQuery().mustNot(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
notFilterList.add(notFilter);
}
@@ -27,6 +27,7 @@ import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.BoostableQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.util.Assert;
@@ -36,6 +37,7 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
*/
class CriteriaQueryProcessor {
@@ -49,11 +51,19 @@ class CriteriaQueryProcessor {
List<QueryBuilder> mustQueryBuilderList = new LinkedList<QueryBuilder>();
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
QueryBuilder firstQuery = null;
boolean negateFirstQuery = false;
while (chainIterator.hasNext()) {
Criteria chainedCriteria = chainIterator.next();
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
if (queryFragmentForCriteria != null) {
if (firstQuery == null) {
firstQuery = queryFragmentForCriteria;
negateFirstQuery = chainedCriteria.isNegating();
continue;
}
if (chainedCriteria.isOr()) {
shouldQueryBuilderList.add(queryFragmentForCriteria);
} else if (chainedCriteria.isNegating()) {
@@ -64,6 +74,18 @@ class CriteriaQueryProcessor {
}
}
if (firstQuery != null) {
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
shouldQueryBuilderList.add(0, firstQuery);
} else {
if (negateFirstQuery) {
mustNotQueryBuilderList.add(0, firstQuery);
} else {
mustQueryBuilderList.add(0, firstQuery);
}
}
}
BoolQueryBuilder query = null;
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
@@ -98,12 +120,12 @@ class CriteriaQueryProcessor {
if (singeEntryCriteria) {
Criteria.CriteriaEntry entry = it.next();
query = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
query = processCriteriaEntry(entry, fieldName);
} else {
query = boolQuery();
while (it.hasNext()) {
Criteria.CriteriaEntry entry = it.next();
((BoolQueryBuilder) query).must(processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName));
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
}
}
@@ -112,46 +134,68 @@ class CriteriaQueryProcessor {
}
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry,/* OperationKey key, Object value,*/ String fieldName) {
Object value = entry.getValue();
if (value == null) {
return null;
}
OperationKey key = entry.getKey();
QueryBuilder query = null;
String searchText = StringUtils.toString(value);
Iterable<Object> collection = null;
switch (key) {
case EQUALS:
query = queryString(searchText).field(fieldName);
query = queryStringQuery(searchText).field(fieldName).defaultOperator(QueryStringQueryBuilder.Operator.AND);
break;
case CONTAINS:
query = queryString("*" + searchText + "*").field(fieldName).analyzeWildcard(true);
query = queryStringQuery("*" + searchText + "*").field(fieldName).analyzeWildcard(true);
break;
case STARTS_WITH:
query = queryString(searchText + "*").field(fieldName).analyzeWildcard(true);
query = queryStringQuery(searchText + "*").field(fieldName).analyzeWildcard(true);
break;
case ENDS_WITH:
query = queryString("*" + searchText).field(fieldName).analyzeWildcard(true);
query = queryStringQuery("*" + searchText).field(fieldName).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryString((String) value).field(fieldName);
query = queryStringQuery(searchText).field(fieldName);
break;
case LESS_EQUAL:
query = rangeQuery(fieldName).lte(value);
break;
case GREATER_EQUAL:
query = rangeQuery(fieldName).gte(value);
break;
case BETWEEN:
Object[] ranges = (Object[]) value;
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
break;
case LESS:
query = rangeQuery(fieldName).lt(value);
break;
case GREATER:
query = rangeQuery(fieldName).gt(value);
break;
case FUZZY:
query = fuzzyQuery(fieldName, (String) value);
query = fuzzyQuery(fieldName, searchText);
break;
case IN:
query = boolQuery();
Iterable<Object> collection = (Iterable<Object>) value;
collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).should(queryString((String) item).field(fieldName));
((BoolQueryBuilder) query).should(queryStringQuery(item.toString()).field(fieldName));
}
break;
case NOT_IN:
query = boolQuery();
collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).mustNot(queryStringQuery(item.toString()).field(fieldName));
}
break;
}
return query;
}
@@ -16,9 +16,27 @@
package org.springframework.data.elasticsearch.core;
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.geo.CustomGeoModule;
import org.springframework.data.geo.*;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* DocumentMapper using jackson
@@ -34,6 +52,7 @@ public class DefaultEntityMapper implements EntityMapper {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.registerModule(new CustomGeoModule());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 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,28 +18,28 @@ package org.springframework.data.elasticsearch.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.base.Strings;
import org.elasticsearch.common.jackson.core.JsonEncoding;
import org.elasticsearch.common.jackson.core.JsonFactory;
import org.elasticsearch.common.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.facet.Facet;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.facet.DefaultFacetMapper;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.PersistentProperty;
@@ -47,6 +47,8 @@ import org.springframework.data.mapping.context.MappingContext;
/**
* @author Artur Konczak
* @author Young Gu
* @author Oliver Gierke
*/
public class DefaultResultMapper extends AbstractResultMapper {
@@ -65,36 +67,58 @@ public class DefaultResultMapper extends AbstractResultMapper {
super(entityMapper);
}
public DefaultResultMapper(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext,
EntityMapper entityMapper) {
super(entityMapper);
this.mappingContext = mappingContext;
}
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
public <T> Page<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
long totalHits = response.getHits().totalHits();
List<T> results = new ArrayList<T>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
T result = null;
if (!Strings.isNullOrEmpty(hit.sourceAsString())) {
if (StringUtils.isNotBlank(hit.sourceAsString())) {
result = mapEntity(hit.sourceAsString(), clazz);
} else {
result = mapEntity(hit.getFields().values(), clazz);
}
setPersistentEntityId(result, hit.getId(), clazz);
populateScriptFields(result, hit);
results.add(result);
}
}
List<FacetResult> facets = new ArrayList<FacetResult>();
if (response.getFacets() != null) {
for (Facet facet : response.getFacets()) {
FacetResult facetResult = DefaultFacetMapper.parse(facet);
if (facetResult != null) {
facets.add(facetResult);
}
}
}
return new FacetedPageImpl<T>(results, pageable, totalHits, facets);
return new PageImpl<T>(results, pageable, totalHits);
}
private <T> T mapEntity(Collection<SearchHitField> values, Class<T> clazz) {
private <T> void populateScriptFields(T result, SearchHit hit) {
if (hit.getFields() != null && !hit.getFields().isEmpty() && result != null) {
for (java.lang.reflect.Field field : result.getClass().getDeclaredFields()) {
ScriptedField scriptedField = field.getAnnotation(ScriptedField.class);
if (scriptedField != null) {
String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name();
SearchHitField searchHitField = hit.getFields().get(name);
if (searchHitField != null) {
field.setAccessible(true);
try {
field.set(result, searchHitField.getValue());
} catch (IllegalArgumentException e) {
throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "
+ searchHitField.getValue(), e);
} catch (IllegalAccessException e) {
throw new ElasticsearchException("failed to access scripted field: " + name, e);
}
}
}
}
}
}
private <T> T mapEntity(Collection<SearchHitField> values, Class<T> clazz) {
return mapEntity(buildJSONFromFields(values), clazz);
}
@@ -138,6 +162,7 @@ public class DefaultResultMapper extends AbstractResultMapper {
for (MultiGetItemResponse response : responses.getResponses()) {
if (!response.isFailed() && response.getResponse().isExists()) {
T result = mapEntity(response.getResponse().getSourceAsString(), clazz);
setPersistentEntityId(result, response.getResponse().getId(), clazz);
list.add(result);
}
}
@@ -145,18 +170,15 @@ public class DefaultResultMapper extends AbstractResultMapper {
}
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
PersistentProperty<ElasticsearchPersistentProperty> idProperty = mappingContext.getPersistentEntity(clazz).getIdProperty();
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz);
PersistentProperty<?> idProperty = persistentEntity.getIdProperty();
// Only deal with String because ES generated Ids are strings !
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
Method setter = idProperty.getSetter();
if (setter != null) {
try {
setter.invoke(result, id);
} catch (Throwable t) {
t.printStackTrace();
}
}
persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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,21 +15,25 @@
*/
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.util.CloseableIterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.action.update.UpdateResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.query.*;
/**
* ElasticsearchOperations
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
public interface ElasticsearchOperations {
@@ -38,6 +42,11 @@ public interface ElasticsearchOperations {
*/
ElasticsearchConverter getElasticsearchConverter();
/**
* @return elasticsearch client
*/
Client getClient();
/**
* Create an index for a class
*
@@ -170,7 +179,7 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz);
<T> Page<T> queryForPage(SearchQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
@@ -179,7 +188,7 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
<T> Page<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
/**
* Execute the query against elasticsearch and return result as {@link Page}
@@ -197,7 +206,7 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz);
<T> Page<T> queryForPage(StringQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
@@ -206,7 +215,47 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper);
<T> Page<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper);
/**
* Executes the given {@link CriteriaQuery} against elasticsearch and return result as {@link CloseableIterator}.
* <p>
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
*
* @param <T> element return type
* @param query
* @param clazz
* @return
* @since 1.3
*/
<T> CloseableIterator<T> stream(CriteriaQuery query, Class<T> clazz);
/**
* Executes the given {@link SearchQuery} against elasticsearch and return result as {@link CloseableIterator}.
* <p>
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
*
* @param <T> element return type
* @param query
* @param clazz
* @return
* @since 1.3
*/
<T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz);
/**
* Executes the given {@link SearchQuery} against elasticsearch and return result as {@link CloseableIterator} using custom mapper.
* <p>
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
*
* @param <T> element return type
* @param query
* @param clazz
* @param mapper
* @return
* @since 1.3
*/
<T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
/**
* Execute the criteria query against elasticsearch and return result as {@link List}
@@ -246,6 +295,23 @@ public interface ElasticsearchOperations {
*/
<T> List<String> queryForIds(SearchQuery query);
/**
* return number of elements found by given query
*
* @param query
* @param clazz
* @return
*/
<T> long count(CriteriaQuery query, Class<T> clazz);
/**
* return number of elements found by given query
*
* @param query
* @return
*/
<T> long count(CriteriaQuery query);
/**
* return number of elements found by given query
*
@@ -305,6 +371,13 @@ public interface ElasticsearchOperations {
*/
void bulkIndex(List<IndexQuery> queries);
/**
* Bulk update all objects. Will do update
*
* @param queries
*/
void bulkUpdate(List<UpdateQuery> queries);
/**
* Delete the one object with provided id
*
@@ -315,6 +388,14 @@ public interface ElasticsearchOperations {
*/
String delete(String indexName, String type, String id);
/**
* Delete all records matching the criteria
*
* @param clazz
* @param criteriaQuery
*/
<T> void delete(CriteriaQuery criteriaQuery, Class<T> clazz);
/**
* Delete the one object with provided id
*
@@ -356,14 +437,6 @@ public interface ElasticsearchOperations {
*/
boolean deleteIndex(String indexName);
/**
* Deletes a type in an index
*
* @param index
* @param type
*/
void deleteType(String index, String type);
/**
* check if index is exists
*
@@ -394,28 +467,70 @@ public interface ElasticsearchOperations {
* refresh the index
*
* @param indexName
* @param waitForOperation
*
*/
void refresh(String indexName, boolean waitForOperation);
void refresh(String indexName);
/**
* refresh the index
*
* @param clazz
* @param waitForOperation
*
*/
<T> void refresh(Class<T> clazz, boolean waitForOperation);
<T> void refresh(Class<T> clazz);
/**
* Returns scroll id for criteria query
*
* @param query The criteria query.
* @param scrollTimeInMillis The time in millisecond for scroll feature
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
* @param noFields The no fields support
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setNoFields()}.
* @return The scan id for input query.
*/
String scan(CriteriaQuery query, long scrollTimeInMillis, boolean noFields);
/**
* Returns scroll id for criteria query
*
* @param query The criteria query.
* @param scrollTimeInMillis The time in millisecond for scroll feature
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
* @param noFields The no fields support
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setNoFields()}.
* @param clazz The class of entity to retrieve.
* @param <T> The type of entity to retrieve.
* @return The scan id for input query.
*/
<T> String scan(CriteriaQuery query, long scrollTimeInMillis, boolean noFields, Class<T> clazz);
/**
* Returns scroll id for scan query
*
* @param query
* @param scrollTimeInMillis
* @param noFields
* @return
* @param query The search query.
* @param scrollTimeInMillis The time in millisecond for scroll feature
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
* @param noFields The no fields support
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setNoFields()}.
* @return The scan id for input query.
*/
String scan(SearchQuery query, long scrollTimeInMillis, boolean noFields);
/**
* Returns scroll id for scan query
*
* @param query The search query.
* @param scrollTimeInMillis The time in millisecond for scroll feature
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
* @param noFields The no fields support
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setNoFields()}.
* @param clazz The class of entity to retrieve.
* @param <T> The type of entity to retrieve.
* @return The scan id for input query.
*/
<T> String scan(SearchQuery query, long scrollTimeInMillis, boolean noFields, Class<T> clazz);
/**
* Scrolls the results for give scroll id
*
@@ -438,6 +553,14 @@ public interface ElasticsearchOperations {
*/
<T> Page<T> scroll(String scrollId, long scrollTimeInMillis, SearchResultMapper mapper);
/**
* Clears the search contexts associated with specified scroll ids.
*
* @param scrollId
*
*/
<T> void clearScroll(String scrollId);
/**
* more like this query to search for documents that are "like" a specific document.
*
@@ -470,5 +593,11 @@ public interface ElasticsearchOperations {
* @param indexName
* @return
*/
Set<String> queryForAlias(String indexName);
List<AliasMetaData> queryForAlias(String indexName);
<T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor);
ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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,26 +15,25 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.*;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.elasticsearch.action.search.SearchType.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.cluster.metadata.AliasAction.Type.*;
import static org.elasticsearch.common.collect.Sets.*;
import static org.elasticsearch.index.VersionType.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import static org.springframework.util.CollectionUtils.isEmpty;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
@@ -47,43 +46,47 @@ import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetRequestBuilder;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.suggest.SuggestRequestBuilder;
import org.elasticsearch.action.suggest.SuggestResponse;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.*;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.CloseableIterator;
import org.springframework.util.Assert;
/**
@@ -92,36 +95,58 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Kevin Leturc
* @author Mason Chan
* @author Young Gu
* @author Oliver Gierke
*/
public class ElasticsearchTemplate implements ElasticsearchOperations {
public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchTemplate.class);
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private ResultsMapper resultsMapper;
private String searchTimeout;
public ElasticsearchTemplate(Client client) {
this(client, null, null);
this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()));
}
public ElasticsearchTemplate(Client client, EntityMapper entityMapper) {
this(client, null, new DefaultResultMapper(entityMapper));
this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), entityMapper);
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, EntityMapper entityMapper) {
this(client, elasticsearchConverter, new DefaultResultMapper(elasticsearchConverter.getMappingContext(), entityMapper));
}
public ElasticsearchTemplate(Client client, ResultsMapper resultsMapper) {
this(client, null, resultsMapper);
this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), resultsMapper);
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) {
this(client, elasticsearchConverter, null);
this(client, elasticsearchConverter, new DefaultResultMapper(elasticsearchConverter.getMappingContext()));
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, ResultsMapper resultsMapper) {
Assert.notNull(client, "Client must not be null!");
Assert.notNull(elasticsearchConverter, "ElasticsearchConverter must not be null!");
Assert.notNull(resultsMapper, "ResultsMapper must not be null!");
this.client = client;
this.elasticsearchConverter = (elasticsearchConverter == null) ? new MappingElasticsearchConverter(
new SimpleElasticsearchMappingContext()) : elasticsearchConverter;
this.resultsMapper = (resultsMapper == null) ? new DefaultResultMapper(this.elasticsearchConverter.getMappingContext()) : resultsMapper;
this.elasticsearchConverter = elasticsearchConverter;
this.resultsMapper = resultsMapper;
}
@Override
public Client getClient() {
return client;
}
public void setSearchTimeout(String searchTimeout) {
this.searchTimeout = searchTimeout;
}
@Override
@@ -188,7 +213,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
Assert.notNull(type, "No type defined for putMapping()");
Map mappings = null;
try {
mappings = client.admin().indices().getMappings(new GetMappingsRequest().indices(indexName).types(type))
mappings = client.admin().indices().getMappings(new GetMappingsRequest().indices(indexName).types(type))
.actionGet().getMappings().get(indexName).get(type).getSourceAsMap();
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + indexName + " type : " + type + " " + e.getMessage());
@@ -237,16 +262,22 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz) {
public <T> Page<T> queryForPage(SearchQuery query, Class<T> clazz) {
return queryForPage(query, clazz, resultsMapper);
}
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper) {
public <T> Page<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = doSearch(prepareSearch(query, clazz), query);
return mapper.mapResults(response, clazz, query.getPageable());
}
@Override
public <T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor) {
SearchResponse response = doSearch(prepareSearch(query), query);
return resultsExtractor.extract(response);
}
@Override
public <T> List<T> queryForList(CriteriaQuery query, Class<T> clazz) {
return queryForPage(query, clazz).getContent();
@@ -268,14 +299,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
if (query.getFilter() != null) {
request.setPostFilter(query.getFilter());
}
SearchResponse response = request.execute().actionGet();
SearchResponse response = getSearchResponse(request.execute());
return extractIds(response);
}
@Override
public <T> Page<T> queryForPage(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
FilterBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
SearchRequestBuilder searchRequestBuilder = prepareSearch(criteriaQuery, clazz);
if (elasticsearchQuery != null) {
@@ -290,27 +321,158 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
if (elasticsearchFilter != null)
searchRequestBuilder.setPostFilter(elasticsearchFilter);
if (logger.isDebugEnabled()) {
logger.debug("doSearch query:\n" + searchRequestBuilder.toString());
}
SearchResponse response = searchRequestBuilder
.execute().actionGet();
SearchResponse response = getSearchResponse(searchRequestBuilder
.execute());
return resultsMapper.mapResults(response, clazz, criteriaQuery.getPageable());
}
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz) {
public <T> Page<T> queryForPage(StringQuery query, Class<T> clazz) {
return queryForPage(query, clazz, resultsMapper);
}
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = prepareSearch(query, clazz).setQuery(query.getSource()).execute().actionGet();
public <T> Page<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = getSearchResponse(prepareSearch(query, clazz).setQuery(query.getSource()).execute());
return mapper.mapResults(response, clazz, query.getPageable());
}
@Override
public <T> CloseableIterator<T> stream(CriteriaQuery query, Class<T> clazz) {
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
final String initScrollId = scan(query, scrollTimeInMillis, false, clazz);
return doStream(initScrollId, scrollTimeInMillis, clazz, resultsMapper);
}
@Override
public <T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz) {
return stream(query, clazz, resultsMapper);
}
@Override
public <T> CloseableIterator<T> stream(SearchQuery query, final Class<T> clazz, final SearchResultMapper mapper) {
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
final String initScrollId = scan(query, scrollTimeInMillis, false, clazz);
return doStream(initScrollId, scrollTimeInMillis, clazz, mapper);
}
private <T> CloseableIterator<T> doStream(final String initScrollId, final long scrollTimeInMillis, final Class<T> clazz, final SearchResultMapper mapper) {
return new CloseableIterator<T>() {
/** As we couldn't retrieve single result with scroll, store current hits. */
private volatile Iterator<T> currentHits;
/** The scroll id. */
private volatile String scrollId = initScrollId;
/** If stream is finished (ie: cluster returns no results. */
private volatile boolean finished;
@Override
public void close() {
try {
// Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done)
if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) {
client.prepareClearScroll().addScrollId(scrollId).execute().actionGet();
}
} finally {
currentHits = null;
scrollId = null;
}
}
@Override
public boolean hasNext() {
// Test if stream is finished
if (finished) {
return false;
}
// Test if it remains hits
if (currentHits == null || !currentHits.hasNext()) {
// Do a new request
SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute());
// Save hits and scroll id
currentHits = mapper.mapResults(response, clazz, null).iterator();
finished = !currentHits.hasNext();
scrollId = response.getScrollId();
}
return currentHits.hasNext();
}
@Override
public T next() {
if (hasNext()) {
return currentHits.next();
}
throw new NoSuchElementException();
}
};
}
@Override
public <T> long count(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
if (elasticsearchFilter == null) {
return doCount(prepareCount(criteriaQuery, clazz), elasticsearchQuery);
} else {
// filter could not be set into CountRequestBuilder, convert request into search request
return doCount(prepareSearch(criteriaQuery, clazz), elasticsearchQuery, elasticsearchFilter);
}
}
@Override
public <T> long count(SearchQuery searchQuery, Class<T> clazz) {
String indexName[] = isNotEmpty(searchQuery.getIndices()) ? searchQuery.getIndices().toArray(new String[searchQuery.getIndices().size()]) : retrieveIndexNameFromPersistentEntity(clazz);
String types[] = isNotEmpty(searchQuery.getTypes()) ? searchQuery.getTypes().toArray(new String[searchQuery.getTypes().size()]) : retrieveTypeFromPersistentEntity(clazz);
QueryBuilder elasticsearchQuery = searchQuery.getQuery();
QueryBuilder elasticsearchFilter = searchQuery.getFilter();
if (elasticsearchFilter == null) {
return doCount(prepareCount(searchQuery, clazz), elasticsearchQuery);
} else {
// filter could not be set into CountRequestBuilder, convert request into search request
return doCount(prepareSearch(searchQuery, clazz), elasticsearchQuery, elasticsearchFilter);
}
}
@Override
public <T> long count(CriteriaQuery query) {
return count(query, null);
}
@Override
public <T> long count(SearchQuery query) {
return count(query, null);
}
private long doCount(CountRequestBuilder countRequestBuilder, QueryBuilder elasticsearchQuery) {
if (elasticsearchQuery != null) {
countRequestBuilder.setQuery(elasticsearchQuery);
}
return countRequestBuilder.execute().actionGet().getCount();
}
private long doCount(SearchRequestBuilder searchRequestBuilder, QueryBuilder elasticsearchQuery, QueryBuilder elasticsearchFilter) {
if (elasticsearchQuery != null) {
searchRequestBuilder.setQuery(elasticsearchQuery);
} else {
searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
}
if (elasticsearchFilter != null) {
searchRequestBuilder.setPostFilter(elasticsearchFilter);
}
searchRequestBuilder.setSearchType(SearchType.COUNT);
return searchRequestBuilder.execute().actionGet().getHits().getTotalHits();
}
private <T> CountRequestBuilder prepareCount(Query query, Class<T> clazz) {
String indexName[] = !isEmpty(query.getIndices()) ? query.getIndices().toArray(new String[query.getIndices().size()]) : retrieveIndexNameFromPersistentEntity(clazz);
String types[] = !isEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()]) : retrieveTypeFromPersistentEntity(clazz);
Assert.notNull(indexName, "No index defined for Query");
@@ -319,15 +481,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
if (types != null) {
countRequestBuilder.setTypes(types);
}
if (searchQuery.getQuery() != null) {
countRequestBuilder.setQuery(searchQuery.getQuery());
}
return countRequestBuilder.execute().actionGet().getCount();
}
@Override
public <T> long count(SearchQuery query) {
return count(query, null);
return countRequestBuilder;
}
@Override
@@ -337,8 +491,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
private <T> MultiGetResponse getMultiResponse(Query searchQuery, Class<T> clazz) {
String indexName = isNotEmpty(searchQuery.getIndices()) ? searchQuery.getIndices().get(0) : getPersistentEntityFor(clazz).getIndexName();
String type = isNotEmpty(searchQuery.getTypes()) ? searchQuery.getTypes().get(0) : getPersistentEntityFor(clazz).getIndexType();
String indexName = !isEmpty(searchQuery.getIndices()) ? searchQuery.getIndices().get(0) : getPersistentEntityFor(clazz).getIndexName();
String type = !isEmpty(searchQuery.getTypes()) ? searchQuery.getTypes().get(0) : getPersistentEntityFor(clazz).getIndexType();
Assert.notNull(indexName, "No index defined for Query");
Assert.notNull(type, "No type define for Query");
@@ -379,20 +533,33 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public UpdateResponse update(UpdateQuery query) {
return this.prepareUpdate(query).execute().actionGet();
}
private UpdateRequestBuilder prepareUpdate(UpdateQuery query) {
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : getPersistentEntityFor(query.getClazz()).getIndexName();
String type = isNotBlank(query.getType()) ? query.getType() : getPersistentEntityFor(query.getClazz()).getIndexType();
Assert.notNull(indexName, "No index defined for Query");
Assert.notNull(type, "No type define for Query");
Assert.notNull(query.getId(), "No Id define for Query");
Assert.notNull(query.getIndexRequest(), "No IndexRequest define for Query");
Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query");
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId());
if (query.DoUpsert()) {
updateRequestBuilder.setDocAsUpsert(true)
.setUpsert(query.getIndexRequest()).setDoc(query.getIndexRequest());
updateRequestBuilder.setRouting(query.getUpdateRequest().routing());
if (query.getUpdateRequest().script() == null) {
// doc
if (query.DoUpsert()) {
updateRequestBuilder.setDocAsUpsert(true)
.setDoc(query.getUpdateRequest().doc());
} else {
updateRequestBuilder.setDoc(query.getUpdateRequest().doc());
}
} else {
updateRequestBuilder.setDoc(query.getIndexRequest());
// or script
updateRequestBuilder.setScript(query.getUpdateRequest().script());
}
return updateRequestBuilder.execute().actionGet();
return updateRequestBuilder;
}
@Override
@@ -410,7 +577,28 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
throw new ElasticsearchException(
"Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
+ failedDocuments + "]", failedDocuments);
+ failedDocuments + "]", failedDocuments
);
}
}
@Override
public void bulkUpdate(List<UpdateQuery> queries) {
BulkRequestBuilder bulkRequest = client.prepareBulk();
for (UpdateQuery query : queries) {
bulkRequest.add(prepareUpdate(query));
}
BulkResponse bulkResponse = bulkRequest.execute().actionGet();
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<String, String>();
for (BulkItemResponse item : bulkResponse.getItems()) {
if (item.isFailed())
failedDocuments.put(item.getId(), item.getFailureMessage());
}
throw new ElasticsearchException(
"Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
+ failedDocuments + "]", failedDocuments
);
}
}
@@ -427,7 +615,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public boolean typeExists(String index, String type) {
return client.admin().cluster().prepareState().execute().actionGet()
.getState().metaData().index(index).mappings().containsKey(type);
.getState().metaData().index(index).getMappings().containsKey(type);
}
@Override
@@ -444,15 +632,6 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
return false;
}
@Override
public void deleteType(String index, String type) {
ImmutableOpenMap<String, MappingMetaData> mappings = client.admin().cluster().prepareState().execute().actionGet()
.getState().metaData().index(index).mappings();
if (mappings.containsKey(type)) {
client.admin().indices().deleteMapping(new DeleteMappingRequest(index).types(type)).actionGet();
}
}
@Override
public String delete(String indexName, String type, String id) {
return client.prepareDelete(indexName, type, id).execute().actionGet().getId();
@@ -466,61 +645,168 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.prepareDeleteByQuery(persistentEntity.getIndexName()).setTypes(persistentEntity.getIndexType())
.setQuery(deleteQuery.getQuery()).execute().actionGet();
String indexName = isNotBlank(deleteQuery.getIndex()) ? deleteQuery.getIndex() : getPersistentEntityFor(clazz).getIndexName();
String typeName = isNotBlank(deleteQuery.getType()) ? deleteQuery.getType() : getPersistentEntityFor(clazz).getIndexType();
Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000;
Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis() : 10000l;
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery())
.withIndices(indexName)
.withTypes(typeName)
.withPageable(new PageRequest(0, pageSize))
.build();
String scrollId = scan(searchQuery, scrollTimeInMillis, true);
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
List<String> ids = new ArrayList<String>();
boolean hasRecords = true;
while (hasRecords) {
Page<String> page = scroll(scrollId, scrollTimeInMillis, new SearchResultMapper() {
@Override
public <T> Page<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<String> result = new ArrayList<String>();
for (SearchHit searchHit : response.getHits()) {
String id = searchHit.getId();
result.add(id);
}
if (result.size() > 0) {
return new PageImpl<T>((List<T>) result);
}
return null;
}
});
if (page != null && page.getContent().size() > 0) {
ids.addAll(page.getContent());
} else {
hasRecords = false;
}
}
for(String id : ids) {
bulkRequestBuilder.add(client.prepareDelete(indexName, typeName, id));
}
if(bulkRequestBuilder.numberOfActions() > 0) {
bulkRequestBuilder.execute().actionGet();
}
clearScroll(scrollId);
}
@Override
public void delete(DeleteQuery deleteQuery) {
Assert.notNull(deleteQuery.getIndex(), "No index defined for Query");
Assert.notNull(deleteQuery.getType(), "No type define for Query");
client.prepareDeleteByQuery(deleteQuery.getIndex()).setTypes(deleteQuery.getType())
.setQuery(deleteQuery.getQuery()).execute().actionGet();
delete(deleteQuery, null);
}
@Override
public <T> void delete(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
Assert.notNull(elasticsearchQuery, "Query can not be null.");
DeleteQuery deleteQuery = new DeleteQuery();
deleteQuery.setQuery(elasticsearchQuery);
delete(deleteQuery, clazz);
}
@Override
public String scan(CriteriaQuery criteriaQuery, long scrollTimeInMillis, boolean noFields) {
return doScan(prepareScan(criteriaQuery, scrollTimeInMillis, noFields), criteriaQuery);
}
@Override
public <T> String scan(CriteriaQuery criteriaQuery, long scrollTimeInMillis, boolean noFields, Class<T> clazz) {
return doScan(prepareScan(criteriaQuery, scrollTimeInMillis, noFields, clazz), criteriaQuery);
}
@Override
public String scan(SearchQuery searchQuery, long scrollTimeInMillis, boolean noFields) {
Assert.notNull(searchQuery.getIndices(), "No index defined for Query");
Assert.notNull(searchQuery.getTypes(), "No type define for Query");
Assert.notNull(searchQuery.getPageable(), "Query.pageable is required for scan & scroll");
return doScan(prepareScan(searchQuery, scrollTimeInMillis, noFields), searchQuery);
}
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(searchQuery.getIndices())).setSearchType(SCAN)
.setQuery(searchQuery.getQuery()).setTypes(toArray(searchQuery.getTypes()))
@Override
public <T> String scan(SearchQuery searchQuery, long scrollTimeInMillis, boolean noFields, Class<T> clazz) {
return doScan(prepareScan(searchQuery, scrollTimeInMillis, noFields, clazz), searchQuery);
}
private <T> SearchRequestBuilder prepareScan(Query query, long scrollTimeInMillis, boolean noFields, Class<T> clazz) {
setPersistentEntityIndexAndType(query, clazz);
return prepareScan(query, scrollTimeInMillis, noFields);
}
private SearchRequestBuilder prepareScan(Query query, long scrollTimeInMillis, boolean noFields) {
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(query.getIndices())).setSearchType(SCAN)
.setTypes(toArray(query.getTypes()))
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0)
.setSize(searchQuery.getPageable().getPageSize());
.setSize(query.getPageable().getPageSize());
if (searchQuery.getFilter() != null) {
requestBuilder.setPostFilter(searchQuery.getFilter());
}
if(isNotEmpty(searchQuery.getFields())) {
requestBuilder.addFields(toArray(searchQuery.getFields()));
if (!isEmpty(query.getFields())) {
requestBuilder.addFields(toArray(query.getFields()));
}
if (noFields) {
requestBuilder.setNoFields();
}
return requestBuilder.execute().actionGet().getScrollId();
return requestBuilder;
}
private String doScan(SearchRequestBuilder requestBuilder, CriteriaQuery criteriaQuery) {
Assert.notNull(criteriaQuery.getIndices(), "No index defined for Query");
Assert.notNull(criteriaQuery.getTypes(), "No type define for Query");
Assert.notNull(criteriaQuery.getPageable(), "Query.pageable is required for scan & scroll");
QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
if (elasticsearchQuery != null) {
requestBuilder.setQuery(elasticsearchQuery);
} else {
requestBuilder.setQuery(QueryBuilders.matchAllQuery());
}
if (elasticsearchFilter != null) {
requestBuilder.setPostFilter(elasticsearchFilter);
}
return getSearchResponse(requestBuilder.execute()).getScrollId();
}
private String doScan(SearchRequestBuilder requestBuilder, SearchQuery searchQuery) {
Assert.notNull(searchQuery.getIndices(), "No index defined for Query");
Assert.notNull(searchQuery.getTypes(), "No type define for Query");
Assert.notNull(searchQuery.getPageable(), "Query.pageable is required for scan & scroll");
if (searchQuery.getFilter() != null) {
requestBuilder.setPostFilter(searchQuery.getFilter());
}
return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()).execute()).getScrollId();
}
@Override
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, Class<T> clazz) {
SearchResponse response = client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute().actionGet();
SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute());
return resultsMapper.mapResults(response, clazz, null);
}
@Override
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, SearchResultMapper mapper) {
SearchResponse response = client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute().actionGet();
SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute());
return mapper.mapResults(response, null, null);
}
@Override
public void clearScroll(String scrollId) {
client.prepareClearScroll().addScrollId(scrollId).execute().actionGet();
}
@Override
public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz) {
int startRecord = 0;
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType();
@@ -529,56 +815,35 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery");
Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");
MoreLikeThisRequestBuilder requestBuilder = client.prepareMoreLikeThis(indexName, type, query.getId());
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = moreLikeThisQuery()
.addLikeItem(new MoreLikeThisQueryBuilder.Item(indexName, type, query.getId()));
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
requestBuilder.setSearchSize(query.getPageable().getPageSize());
}
requestBuilder.setSearchFrom(startRecord);
if (isNotEmpty(query.getSearchIndices())) {
requestBuilder.setSearchIndices(toArray(query.getSearchIndices()));
}
if (isNotEmpty(query.getSearchTypes())) {
requestBuilder.setSearchTypes(toArray(query.getSearchTypes()));
}
if (isNotEmpty(query.getFields())) {
requestBuilder.setField(toArray(query.getFields()));
}
if (isNotBlank(query.getRouting())) {
requestBuilder.setRouting(query.getRouting());
}
if (query.getPercentTermsToMatch() != null) {
requestBuilder.setPercentTermsToMatch(query.getPercentTermsToMatch());
}
if (query.getMinTermFreq() != null) {
requestBuilder.setMinTermFreq(query.getMinTermFreq());
moreLikeThisQueryBuilder.minTermFreq(query.getMinTermFreq());
}
if (query.getMaxQueryTerms() != null) {
requestBuilder.maxQueryTerms(query.getMaxQueryTerms());
moreLikeThisQueryBuilder.maxQueryTerms(query.getMaxQueryTerms());
}
if (isNotEmpty(query.getStopWords())) {
requestBuilder.setStopWords(toArray(query.getStopWords()));
if (!isEmpty(query.getStopWords())) {
moreLikeThisQueryBuilder.stopWords(toArray(query.getStopWords()));
}
if (query.getMinDocFreq() != null) {
requestBuilder.setMinDocFreq(query.getMinDocFreq());
moreLikeThisQueryBuilder.minDocFreq(query.getMinDocFreq());
}
if (query.getMaxDocFreq() != null) {
requestBuilder.setMaxDocFreq(query.getMaxDocFreq());
moreLikeThisQueryBuilder.maxDocFreq(query.getMaxDocFreq());
}
if (query.getMinWordLen() != null) {
requestBuilder.setMinWordLen(query.getMinWordLen());
moreLikeThisQueryBuilder.minWordLength(query.getMinWordLen());
}
if (query.getMaxWordLen() != null) {
requestBuilder.setMaxWordLen(query.getMaxWordLen());
moreLikeThisQueryBuilder.maxWordLength(query.getMaxWordLen());
}
if (query.getBoostTerms() != null) {
requestBuilder.setBoostTerms(query.getBoostTerms());
moreLikeThisQueryBuilder.boostTerms(query.getBoostTerms());
}
SearchResponse response = requestBuilder.execute().actionGet();
return resultsMapper.mapResults(response, clazz, query.getPageable());
return queryForPage(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz);
}
private SearchResponse doSearch(SearchRequestBuilder searchRequest, SearchQuery searchQuery) {
@@ -586,19 +851,16 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
searchRequest.setPostFilter(searchQuery.getFilter());
}
if (CollectionUtils.isNotEmpty(searchQuery.getElasticsearchSorts())) {
if (!isEmpty(searchQuery.getElasticsearchSorts())) {
for (SortBuilder sort : searchQuery.getElasticsearchSorts()) {
searchRequest.addSort(sort);
}
}
if (CollectionUtils.isNotEmpty(searchQuery.getFacets())) {
for (FacetRequest facetRequest : searchQuery.getFacets()) {
FacetBuilder facet = facetRequest.getFacet();
if (facetRequest.applyQueryFilter() && searchQuery.getFilter() != null) {
facet.facetFilter(searchQuery.getFilter());
}
searchRequest.addFacet(facet);
if (!searchQuery.getScriptFields().isEmpty()) {
searchRequest.addField("_source");
for (ScriptField scriptedField : searchQuery.getScriptFields()) {
searchRequest.addScriptField(scriptedField.fieldName(), scriptedField.script());
}
}
@@ -608,7 +870,22 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
}
return searchRequest.setQuery(searchQuery.getQuery()).execute().actionGet();
if (!isEmpty(searchQuery.getIndicesBoost())) {
for (IndexBoost indexBoost : searchQuery.getIndicesBoost()) {
searchRequest.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
}
}
if (!isEmpty(searchQuery.getAggregations())) {
for (AbstractAggregationBuilder aggregationBuilder : searchQuery.getAggregations()) {
searchRequest.addAggregation(aggregationBuilder);
}
}
return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()).execute());
}
private SearchResponse getSearchResponse(ListenableActionFuture<SearchResponse> response) {
return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout);
}
private <T> boolean createIndexIfNotCreated(Class<T> clazz) {
@@ -649,6 +926,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
private <T> Map getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
if (persistentEntity.isUseServerConfiguration())
return new HashMap();
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
@@ -668,12 +949,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
private <T> SearchRequestBuilder prepareSearch(Query query, Class<T> clazz) {
if (query.getIndices().isEmpty()) {
query.addIndices(retrieveIndexNameFromPersistentEntity(clazz));
}
if (query.getTypes().isEmpty()) {
query.addTypes(retrieveTypeFromPersistentEntity(clazz));
}
setPersistentEntityIndexAndType(query, clazz);
return prepareSearch(query);
}
@@ -685,6 +961,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices()))
.setSearchType(query.getSearchType()).setTypes(toArray(query.getTypes()));
if (query.getSourceFilter() != null) {
SourceFilter sourceFilter = query.getSourceFilter();
searchRequestBuilder.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
}
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
searchRequestBuilder.setSize(query.getPageable().getPageSize());
@@ -750,15 +1031,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
@Override
public void refresh(String indexName, boolean waitForOperation) {
client.admin().indices().refresh(refreshRequest(indexName).force(waitForOperation)).actionGet();
public void refresh(String indexName) {
Assert.notNull(indexName, "No index defined for refresh()");
client.admin().indices().refresh(refreshRequest(indexName)).actionGet();
}
@Override
public <T> void refresh(Class<T> clazz, boolean waitForOperation) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.admin().indices()
.refresh(refreshRequest(persistentEntity.getIndexName()).force(waitForOperation)).actionGet();
public <T> void refresh(Class<T> clazz) {
refresh(getPersistentEntityFor(clazz).getIndexName());
}
@Override
@@ -789,49 +1069,43 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
@Override
public Set<String> queryForAlias(String indexName) {
ClusterStateRequest clusterStateRequest = Requests.clusterStateRequest()
.routingTable(true).nodes(true).indices(indexName);
Iterator<String> iterator = client.admin().cluster().state(clusterStateRequest).actionGet().getState().getMetaData().aliases().keysIt();
return newHashSet(iterator);
public List<AliasMetaData> queryForAlias(String indexName) {
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName))
.actionGet().getAliases().get(indexName);
}
private ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
@Override
public ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
Assert.isTrue(clazz.isAnnotationPresent(Document.class), "Unable to identify index name. " + clazz.getSimpleName()
+ " is not a Document. Make sure the document class is annotated with @Document(indexName=\"foo\")");
return elasticsearchConverter.getMappingContext().getPersistentEntity(clazz);
}
private String getPersistentEntityId(Object entity) {
PersistentProperty idProperty = getPersistentEntityFor(entity.getClass()).getIdProperty();
if (idProperty != null) {
Method getter = idProperty.getGetter();
if (getter != null) {
try {
Object id = getter.invoke(entity);
if (id != null) {
return String.valueOf(id);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
return null;
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
Object identifier = persistentEntity.getIdentifierAccessor(entity).getIdentifier();
return identifier == null ? null : String.valueOf(identifier);
}
private void setPersistentEntityId(Object entity, String id) {
PersistentProperty idProperty = getPersistentEntityFor(entity.getClass()).getIdProperty();
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
PersistentProperty<?> idProperty = persistentEntity.getIdProperty();
// Only deal with String because ES generated Ids are strings !
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
Method setter = idProperty.getSetter();
if (setter != null) {
try {
setter.invoke(entity, id);
} catch (Throwable t) {
t.printStackTrace();
}
}
persistentEntity.getPropertyAccessor(entity).setProperty(idProperty,id);
}
}
private void setPersistentEntityIndexAndType(Query query, Class clazz) {
if (query.getIndices().isEmpty()) {
query.addIndices(retrieveIndexNameFromPersistentEntity(clazz));
}
if (query.getTypes().isEmpty()) {
query.addTypes(retrieveTypeFromPersistentEntity(clazz));
}
}
@@ -859,6 +1133,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
return ids;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (elasticsearchConverter instanceof ApplicationContextAware) {
((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context);
}
}
private static String[] toArray(List<String> values) {
String[] valuesAsArray = new String[values.size()];
return values.toArray(valuesAsArray);
@@ -883,8 +1164,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
bufferedReader = new BufferedReader(inputStreamReader);
String line;
String lineSeparator = System.getProperty("line.separator");
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
stringBuilder.append(line).append(lineSeparator);
}
} catch (Exception e) {
logger.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
@@ -893,11 +1175,21 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
if (bufferedReader != null)
try {
bufferedReader.close();
} catch (IOException e) {
} catch (IOException e) {
logger.debug(String.format("Unable to close buffered reader.. %s", e.getMessage()));
}
}
return stringBuilder.toString();
}
public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder<?> suggestion, String... indices) {
SuggestRequestBuilder suggestRequestBuilder = client.prepareSuggest(indices);
suggestRequestBuilder.addSuggestion(suggestion);
return suggestRequestBuilder.execute().actionGet();
}
public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder<?> suggestion, Class clazz) {
return suggest(suggestion, retrieveIndexNameFromPersistentEntity(clazz));
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
/**
* Container for query result and facet results
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class FacetedPageImpl<T> extends PageImpl<T> implements FacetedPage<T> {
private List<FacetResult> facets;
private Map<String, FacetResult> mapOfFacets = new HashMap<String, FacetResult>();
public FacetedPageImpl(List<T> content) {
super(content);
}
public FacetedPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public FacetedPageImpl(List<T> content, Pageable pageable, long total, List<FacetResult> facets) {
super(content, pageable, total);
this.facets = facets;
for (FacetResult facet : facets) {
mapOfFacets.put(facet.getName(), facet);
}
}
@Override
public boolean hasFacets() {
return CollectionUtils.isNotEmpty(facets);
}
@Override
public List<FacetResult> getFacets() {
return facets;
}
@Override
public FacetResult getFacet(String name) {
return mapOfFacets.get(name);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 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,14 +20,19 @@ import static org.elasticsearch.common.xcontent.XContentFactory.*;
import static org.springframework.util.StringUtils.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import org.springframework.data.elasticsearch.core.completion.Completion;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
@@ -37,6 +42,9 @@ import org.springframework.data.util.TypeInformation;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Kevin Leturc
* @author Alexander Volz
* @author Dennis Maaß
*/
class MappingBuilder {
@@ -46,13 +54,21 @@ class MappingBuilder {
public static final String FIELD_INDEX = "index";
public static final String FIELD_FORMAT = "format";
public static final String FIELD_SEARCH_ANALYZER = "search_analyzer";
public static final String FIELD_INDEX_ANALYZER = "index_analyzer";
public static final String FIELD_INDEX_ANALYZER = "analyzer";
public static final String FIELD_PROPERTIES = "properties";
public static final String FIELD_PARENT = "_parent";
public static final String COMPLETION_PAYLOADS = "payloads";
public static final String COMPLETION_PRESERVE_SEPARATORS = "preserve_separators";
public static final String COMPLETION_PRESERVE_POSITION_INCREMENTS = "preserve_position_increments";
public static final String COMPLETION_MAX_INPUT_LENGTH = "max_input_length";
public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed";
public static final String TYPE_VALUE_STRING = "string";
public static final String TYPE_VALUE_GEO_POINT = "geo_point";
public static final String TYPE_VALUE_COMPLETION = "completion";
public static final String TYPE_VALUE_GEO_HASH_PREFIX = "geohash_prefix";
public static final String TYPE_VALUE_GEO_HASH_PRECISION = "geohash_precision";
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
@@ -67,22 +83,27 @@ class MappingBuilder {
// Properties
XContentBuilder xContentBuilder = mapping.startObject(FIELD_PROPERTIES);
mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, false, FieldType.Auto);
mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, false, FieldType.Auto, null);
return xContentBuilder.endObject().endObject().endObject();
}
private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName,
String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType) throws IOException {
String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, Field fieldAnnotation) throws IOException {
java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
java.lang.reflect.Field[] fields = retrieveFields(clazz);
if (!isRootObject && (isAnyPropertyAnnotatedAsField(fields) || nestedOrObjectField)) {
String type = FieldType.Object.toString().toLowerCase();
if (nestedOrObjectField) {
type = fieldType.toString().toLowerCase();
}
xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type).startObject(FIELD_PROPERTIES);
XContentBuilder t = xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type);
if (nestedOrObjectField && FieldType.Nested == fieldType && fieldAnnotation.includeInParent()) {
t.field("include_in_parent", fieldAnnotation.includeInParent());
}
t.startObject(FIELD_PROPERTIES);
}
for (java.lang.reflect.Field field : fields) {
@@ -91,15 +112,27 @@ class MappingBuilder {
continue;
}
boolean isGeoField = isGeoField(field);
if (field.isAnnotationPresent(Mapping.class)) {
String mappingPath = field.getAnnotation(Mapping.class).mappingPath();
if (isNotBlank(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
xContentBuilder.rawField(field.getName(), mappings.getInputStream());
continue;
}
}
}
boolean isGeoPointField = isGeoPointField(field);
boolean isCompletionField = isCompletionField(field);
Field singleField = field.getAnnotation(Field.class);
if (!isGeoField && isEntity(field) && !isAnnotated(field)) {
if (!isGeoPointField && !isCompletionField && isEntity(field) && isAnnotated(field)) {
if (singleField == null) {
continue;
}
boolean nestedOrObject = isNestedOrObjectField(field);
mapEntity(xContentBuilder, getFieldType(field), false, EMPTY, field.getName(), nestedOrObject, field.getAnnotation(Field.class).type());
mapEntity(xContentBuilder, getFieldType(field), false, EMPTY, field.getName(), nestedOrObject, singleField.type(), field.getAnnotation(Field.class));
if (nestedOrObject) {
continue;
}
@@ -107,16 +140,21 @@ class MappingBuilder {
MultiField multiField = field.getAnnotation(MultiField.class);
if (isGeoField) {
if (isGeoPointField) {
applyGeoPointFieldMapping(xContentBuilder, field);
}
if (isCompletionField) {
CompletionField completionField = field.getAnnotation(CompletionField.class);
applyCompletionFieldMapping(xContentBuilder, field, completionField);
}
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
applyDefaultIdFieldMapping(xContentBuilder, field);
} else if (multiField != null) {
addMultiFieldMapping(xContentBuilder, field, multiField);
addMultiFieldMapping(xContentBuilder, field, multiField, isNestedOrObjectField(field));
} else if (singleField != null) {
addSingleFieldMapping(xContentBuilder, field, singleField);
addSingleFieldMapping(xContentBuilder, field, singleField, isNestedOrObjectField(field));
}
}
@@ -125,14 +163,65 @@ class MappingBuilder {
}
}
private static java.lang.reflect.Field[] retrieveFields(Class clazz) {
// Create list of fields.
List<java.lang.reflect.Field> fields = new ArrayList<java.lang.reflect.Field>();
// Keep backing up the inheritance hierarchy.
Class targetClass = clazz;
do {
fields.addAll(Arrays.asList(targetClass.getDeclaredFields()));
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return fields.toArray(new java.lang.reflect.Field[fields.size()]);
}
private static boolean isAnnotated(java.lang.reflect.Field field) {
return field.getAnnotation(Field.class) == null && field.getAnnotation(MultiField.class) == null && field.getAnnotation(GeoPointField.class) == null;
return field.getAnnotation(Field.class) != null ||
field.getAnnotation(MultiField.class) != null ||
field.getAnnotation(GeoPointField.class) != null ||
field.getAnnotation(CompletionField.class) != null;
}
private static void applyGeoPointFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT)
.endObject();
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT);
GeoPointField annotation = field.getAnnotation(GeoPointField.class);
if (annotation != null) {
if (annotation.geoHashPrefix()) {
xContentBuilder.field(TYPE_VALUE_GEO_HASH_PREFIX, true);
if (StringUtils.isNotEmpty(annotation.geoHashPrecision())) {
if (NumberUtils.isNumber(annotation.geoHashPrecision())) {
xContentBuilder.field(TYPE_VALUE_GEO_HASH_PRECISION, Integer.parseInt(annotation.geoHashPrecision()));
} else {
xContentBuilder.field(TYPE_VALUE_GEO_HASH_PRECISION, annotation.geoHashPrecision());
}
}
}
}
xContentBuilder.endObject();
}
private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field, CompletionField annotation) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION);
if (annotation != null) {
xContentBuilder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength());
xContentBuilder.field(COMPLETION_PAYLOADS, annotation.payloads());
xContentBuilder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements());
xContentBuilder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators());
if (isNotBlank(annotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
}
if (isNotBlank(annotation.analyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.analyzer());
}
}
xContentBuilder.endObject();
}
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
@@ -146,15 +235,14 @@ class MappingBuilder {
/**
* Apply mapping for a single @Field annotation
*
* @param xContentBuilder
* @param field
* @param fieldAnnotation
* @throws IOException
*/
private static void addSingleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field,
Field fieldAnnotation) throws IOException {
Field fieldAnnotation, boolean nestedOrObjectField) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
if(!nestedOrObjectField) {
xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
}
if (FieldType.Auto != fieldAnnotation.type()) {
xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type().name().toLowerCase());
if (FieldType.Date == fieldAnnotation.type() && DateFormat.none != fieldAnnotation.format()) {
@@ -162,14 +250,14 @@ class MappingBuilder {
? fieldAnnotation.pattern() : fieldAnnotation.format());
}
}
if (FieldIndex.not_analyzed == fieldAnnotation.index()) {
if (FieldIndex.not_analyzed == fieldAnnotation.index() || FieldIndex.no == fieldAnnotation.index()) {
xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index().name().toLowerCase());
}
if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer());
}
if (isNotBlank(fieldAnnotation.indexAnalyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.indexAnalyzer());
if (isNotBlank(fieldAnnotation.analyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.analyzer());
}
xContentBuilder.endObject();
}
@@ -177,15 +265,12 @@ class MappingBuilder {
/**
* Apply mapping for a single nested @Field annotation
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addNestedFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
NestedField annotation) throws IOException {
builder.startObject(field.getName() + "." + annotation.dotSuffix());
builder.field(FIELD_STORE, annotation.store());
InnerField annotation) throws IOException {
builder.startObject(annotation.suffix());
//builder.field(FIELD_STORE, annotation.store());
if (FieldType.Auto != annotation.type()) {
builder.field(FIELD_TYPE, annotation.type().name().toLowerCase());
}
@@ -204,58 +289,22 @@ class MappingBuilder {
/**
* Multi field mappings for string type fields, support for sorts and facets
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
MultiField annotation) throws IOException {
MultiField annotation, boolean nestedOrObjectField) throws IOException {
builder.startObject(field.getName());
builder.field(FIELD_TYPE, "multi_field");
builder.startObject("fields");
//add standard field
addSingleFieldMapping(builder, field, annotation.mainField());
for (NestedField nestedField : annotation.otherFields()) {
addNestedFieldMapping(builder, field, nestedField);
addSingleFieldMapping(builder, field, annotation.mainField(),nestedOrObjectField);
for (InnerField innerField : annotation.otherFields()) {
addNestedFieldMapping(builder, field, innerField);
}
builder.endObject();
builder.endObject();
}
/**
* Facet field for string type, for other types we don't need it(long, int, double, float)
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addFacetMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation) throws IOException {
builder.startObject(FacetRequest.FIELD_UNTOUCHED)
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, INDEX_VALUE_NOT_ANALYZED)
.field(FIELD_STORE, true);
builder.endObject();
}
/**
* Sort field for string type, for other types we don't need it(long, int, double, float)
* value of the field should be converted to lowercase and not analise
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addSortMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation) throws IOException {
builder.startObject(FacetRequest.FIELD_SORT)
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, "keyword")
.field(FIELD_STORE, true);
builder.endObject();
}
protected static boolean isEntity(java.lang.reflect.Field field) {
TypeInformation typeInformation = ClassTypeInformation.from(field.getType());
Class<?> clazz = getFieldType(field);
@@ -301,7 +350,11 @@ class MappingBuilder {
return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type());
}
private static boolean isGeoField(java.lang.reflect.Field field) {
private static boolean isGeoPointField(java.lang.reflect.Field field) {
return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null;
}
private static boolean isCompletionField(java.lang.reflect.Field field) {
return field.getType() == Completion.class;
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2013-2014 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
*
* http://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.core;
import org.elasticsearch.action.search.SearchResponse;
public interface ResultsExtractor<T> {
T extract(SearchResponse response);
}
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.search.SearchResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
/**
@@ -23,5 +24,5 @@ import org.springframework.data.domain.Pageable;
*/
public interface SearchResultMapper {
<T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable);
<T> Page<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable);
}
@@ -0,0 +1,57 @@
package org.springframework.data.elasticsearch.core.completion;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
*
* @author Mewes Kochheim
*/
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class Completion {
private String[] input;
private String output;
private Integer weight;
private Object payload;
private Completion() {
//required by mapper to instantiate object
}
public Completion(String[] input) {
this.input = input;
}
public String[] getInput() {
return input;
}
public void setInput(String[] input) {
this.input = input;
}
public String getOutput() {
return output;
}
public void setOutput(String output) {
this.output = output;
}
public Object getPayload() {
return payload;
}
public void setPayload(Object payload) {
this.payload = payload;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
@@ -61,5 +61,8 @@ public class MappingElasticsearchConverter implements ElasticsearchConverter, Ap
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (mappingContext instanceof ApplicationContextAware) {
((ApplicationContextAware) mappingContext).setApplicationContext(applicationContext);
}
}
}
@@ -1,45 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet;
import org.springframework.util.Assert;
/**
* @author Artur Konczak
*/
public abstract class AbstractFacetRequest implements FacetRequest {
private String name;
private boolean applyQueryFilter;
public AbstractFacetRequest(String name) {
Assert.hasText(name, "Facet can't be null or empty !!!");
this.name = name;
}
protected String getName() {
return name;
}
public void setApplyQueryFilter(boolean applyQueryFilter) {
this.applyQueryFilter = applyQueryFilter;
}
@Override
public boolean applyQueryFilter() {
return applyQueryFilter;
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet;
import org.springframework.util.Assert;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class AbstractFacetResult implements FacetResult {
private final String name;
private final FacetType type;
protected AbstractFacetResult(String name, FacetType type) {
Assert.hasText(name, "Facet name can't be null and should have a value");
this.name = name;
this.type = type;
}
@Override
public String getName() {
return name;
}
@Override
public FacetType getType() {
return type;
}
}
@@ -1,81 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet;
import java.util.ArrayList;
import java.util.List;
import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.histogram.HistogramFacet;
import org.elasticsearch.search.facet.range.RangeFacet;
import org.elasticsearch.search.facet.statistical.StatisticalFacet;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.springframework.data.elasticsearch.core.facet.result.*;
/**
* @author Artur Konczak
* @author Petar Tahchiev
*/
public class DefaultFacetMapper {
public static FacetResult parse(Facet facet) {
if (facet instanceof TermsFacet) {
return parseTerm((TermsFacet) facet);
}
if (facet instanceof RangeFacet) {
return parseRange((RangeFacet) facet);
}
if (facet instanceof StatisticalFacet) {
return parseStatistical((StatisticalFacet) facet);
}
if (facet instanceof HistogramFacet) {
return parseHistogram((HistogramFacet) facet);
}
return null;
}
private static FacetResult parseTerm(TermsFacet facet) {
List<Term> entries = new ArrayList<Term>();
for (TermsFacet.Entry entry : facet.getEntries()) {
entries.add(new Term(entry.getTerm().toString(), entry.getCount()));
}
return new TermResult(facet.getName(), entries);
}
private static FacetResult parseRange(RangeFacet facet) {
List<Range> entries = new ArrayList<Range>();
for (RangeFacet.Entry entry : facet.getEntries()) {
entries.add(new Range(entry.getFrom() == Double.NEGATIVE_INFINITY ? null : entry.getFrom(), entry.getTo() == Double.POSITIVE_INFINITY ? null : entry.getTo(), entry.getCount(), entry.getTotal(), entry.getTotalCount(), entry.getMin(), entry.getMax()));
}
return new RangeResult(facet.getName(), entries);
}
private static FacetResult parseStatistical(StatisticalFacet facet) {
return new StatisticalResult(facet.getName(), facet.getCount(), facet.getMax(), facet.getMin(), facet.getMean(), facet.getStdDeviation(), facet.getSumOfSquares(), facet.getTotal(), facet.getVariance());
}
private static FacetResult parseHistogram(HistogramFacet facet) {
List<IntervalUnit> entries = new ArrayList<IntervalUnit>();
for (HistogramFacet.Entry entry : facet.getEntries()) {
entries.add(new IntervalUnit(entry.getKey(), entry.getCount(), entry.getTotalCount(), entry.getTotal(), entry.getMean(), entry.getMin(), entry.getMax()));
}
return new HistogramResult(facet.getName(), entries);
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.histogram.HistogramFacetBuilder;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
public class HistogramFacetRequest extends AbstractFacetRequest {
private String field;
private long interval;
private TimeUnit timeUnit;
public HistogramFacetRequest(String name) {
super(name);
}
public void setField(String field) {
this.field = field;
}
public void setInterval(long interval) {
this.interval = interval;
}
public void setTimeUnit(TimeUnit timeUnit) {
this.timeUnit = timeUnit;
}
public FacetBuilder getFacet() {
Assert.notNull(getName(), "Facet name can't be a null !!!");
Assert.isTrue(StringUtils.isNotBlank(field), "Please select field on which to build the facet !!!");
Assert.isTrue(interval > 0, "Please provide interval as positive value greater them zero !!!");
HistogramFacetBuilder builder = FacetBuilders.histogramFacet(getName());
builder.field(field);
if (timeUnit != null) {
builder.interval(interval, timeUnit);
} else {
builder.interval(interval);
}
return builder;
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import java.util.concurrent.TimeUnit;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* @author Artur Konczak
*/
public class HistogramFacetRequestBuilder {
HistogramFacetRequest result;
public HistogramFacetRequestBuilder(String name) {
result = new HistogramFacetRequest(name);
}
public HistogramFacetRequestBuilder field(String field) {
result.setField(field);
return this;
}
public HistogramFacetRequestBuilder interval(long interval) {
result.setInterval(interval);
return this;
}
public HistogramFacetRequestBuilder timeUnit(TimeUnit timeUnit) {
result.setTimeUnit(timeUnit);
return this;
}
public FacetRequest build() {
return result;
}
public HistogramFacetRequestBuilder applyQueryFilter() {
result.setApplyQueryFilter(true);
return this;
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.elasticsearch.search.facet.FacetBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
public class NativeFacetRequest implements FacetRequest {
private FacetBuilder facet;
private boolean applyQueryFilter;
public NativeFacetRequest(FacetBuilder facet) {
this(facet, false);
}
public NativeFacetRequest(FacetBuilder facet, boolean applyQueryFilter) {
this.facet = facet;
this.applyQueryFilter = applyQueryFilter;
}
@Override
public FacetBuilder getFacet() {
return facet;
}
@Override
public boolean applyQueryFilter() {
return applyQueryFilter;
}
}
@@ -1,132 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.range.RangeFacetBuilder;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
/**
* Range facet for numeric fields
*
* @author Artur Konczak
* @author Akos Bordas
*/
public class RangeFacetRequest extends AbstractFacetRequest {
private String field;
private String keyField;
private String valueField;
private List<Entry> entries = new ArrayList<Entry>();
public RangeFacetRequest(String name) {
super(name);
}
public void setField(String field) {
this.field = field;
}
public void setFields(String keyField, String valueField) {
this.keyField = keyField;
this.valueField = valueField;
}
public void range(Double from, Double to) {
entries.add(new DoubleEntry(from, to));
}
public void range(String from, String to) {
entries.add(new StringEntry(from, to));
}
public void addRange(Double from, Double to) {
entries.add(new DoubleEntry(from, to));
}
public void addRange(String from, String to) {
entries.add(new StringEntry(from, to));
}
@Override
public FacetBuilder getFacet() {
Assert.notNull(getName(), "Facet name can't be a null !!!");
Assert.isTrue(StringUtils.isNotBlank(field) || StringUtils.isNotBlank(keyField) && StringUtils.isNotBlank(valueField), "Please select field or key field and value field !!!");
RangeFacetBuilder builder = FacetBuilders.rangeFacet(getName());
if (StringUtils.isNotBlank(keyField)) {
builder.keyField(keyField).valueField(valueField);
} else {
builder.field(field);
}
for (Entry entry : entries) {
if (entry instanceof DoubleEntry) {
DoubleEntry doubleEntry = (DoubleEntry) entry;
builder.addRange(validateValue(doubleEntry.getFrom(), Double.NEGATIVE_INFINITY), validateValue(doubleEntry.getTo(), Double.POSITIVE_INFINITY));
} else {
StringEntry stringEntry = (StringEntry) entry;
builder.addRange(stringEntry.getFrom(), stringEntry.getTo());
}
}
return builder;
}
private double validateValue(Double value, double defaultValue) {
return value == null ? defaultValue : value;
}
static class DoubleEntry extends Entry<Double> {
DoubleEntry(Double from, Double to) {
super(from, to);
}
}
static class StringEntry extends Entry<String> {
StringEntry(String from, String to) {
super(from, to);
}
}
static class Entry<T> {
T from;
T to;
Entry(T from, T to) {
this.from = from;
this.to = to;
}
public T getFrom() {
return from;
}
public T getTo() {
return to;
}
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* Basic range facet
*
* @author Artur Konczak
*/
public class RangeFacetRequestBuilder {
RangeFacetRequest result;
public RangeFacetRequestBuilder(String name) {
result = new RangeFacetRequest(name);
}
public RangeFacetRequestBuilder field(String field) {
result.setField(field);
return this;
}
public RangeFacetRequestBuilder fields(String keyField, String valueField) {
result.setFields(keyField, valueField);
return this;
}
public RangeFacetRequestBuilder range(double from, double to) {
result.range(from, to);
return this;
}
public RangeFacetRequestBuilder range(String from, String to) {
result.range(from, to);
return this;
}
public RangeFacetRequestBuilder from(double from) {
result.range(from, null);
return this;
}
public RangeFacetRequestBuilder to(double to) {
result.range(null, to);
return this;
}
public RangeFacetRequestBuilder from(String from) {
result.range(from, null);
return this;
}
public RangeFacetRequestBuilder to(String to) {
result.range(null, to);
return this;
}
public RangeFacetRequestBuilder applyQueryFilter() {
result.setApplyQueryFilter(true);
return this;
}
public FacetRequest build() {
return result;
}
}
@@ -1,60 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.statistical.StatisticalFacetBuilder;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
/**
* @author Petar Tahchiev
*/
public class StatisticalFacetRequest extends AbstractFacetRequest {
private String field;
private String[] fields;
public StatisticalFacetRequest(String name) {
super(name);
}
public void setField(String field) {
this.field = field;
}
public void setFields(String... fields) {
this.fields = fields;
}
public FacetBuilder getFacet() {
Assert.notNull(getName(), "Facet name can't be a null !!!");
Assert.isTrue(StringUtils.isNotBlank(field) && fields == null, "Please select field or fields on which to build the facets !!!");
StatisticalFacetBuilder builder = FacetBuilders.statisticalFacet(getName());
if (ArrayUtils.isNotEmpty(fields)) {
builder.fields(fields);
} else {
builder.field(field);
}
return builder;
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* @author Petar Tahchiev
*/
public class StatisticalFacetRequestBuilder {
StatisticalFacetRequest result;
public StatisticalFacetRequestBuilder(String name) {
result = new StatisticalFacetRequest(name);
}
public StatisticalFacetRequestBuilder field(String field) {
result.setField(field);
return this;
}
public StatisticalFacetRequestBuilder fields(String... fields) {
result.setFields(fields);
return this;
}
public StatisticalFacetRequestBuilder applyQueryFilter() {
result.setApplyQueryFilter(true);
return this;
}
public FacetRequest build() {
return result;
}
}
@@ -1,108 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.elasticsearch.search.facet.terms.TermsFacetBuilder;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
/**
* Term facet
*
* @author Artur Konczak
*/
public class TermFacetRequest extends AbstractFacetRequest {
private String[] fields;
private Object[] excludeTerms;
private int size = 10;
private TermFacetOrder order = TermFacetOrder.descCount;
private boolean allTerms = false;
private String regex = null;
private int regexFlag = 0;
public TermFacetRequest(String name) {
super(name);
}
public void setFields(String... fields) {
this.fields = fields;
}
public void setSize(int size) {
Assert.isTrue(size >= 0, "Size should be bigger then zero !!!");
this.size = size;
}
public void setOrder(TermFacetOrder order) {
this.order = order;
}
public void setExcludeTerms(Object... excludeTerms) {
this.excludeTerms = excludeTerms;
}
public void setAllTerms(boolean allTerms) {
this.allTerms = allTerms;
}
public void setRegex(String regex) {
this.regex = regex;
}
public void setRegex(String regex, int regexFlag) {
this.regex = regex;
this.regexFlag = regexFlag;
}
@Override
public FacetBuilder getFacet() {
Assert.notEmpty(fields, "Please select at last one field !!!");
TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields).size(size);
switch (order) {
case descTerm:
builder.order(TermsFacet.ComparatorType.REVERSE_TERM);
break;
case ascTerm:
builder.order(TermsFacet.ComparatorType.TERM);
break;
case ascCount:
builder.order(TermsFacet.ComparatorType.REVERSE_COUNT);
break;
default:
builder.order(TermsFacet.ComparatorType.COUNT);
}
if (ArrayUtils.isNotEmpty(excludeTerms)) {
builder.exclude(excludeTerms);
}
if (allTerms) {
builder.allTerms(allTerms);
}
if (StringUtils.isNotBlank(regex)) {
builder.regex(regex, regexFlag);
}
return builder;
}
}
@@ -1,91 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.request;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* Basic term facet
*
* @author Artur Konczak
*/
public class TermFacetRequestBuilder {
private TermFacetRequest result;
public TermFacetRequestBuilder(String name) {
result = new TermFacetRequest(name);
}
public TermFacetRequestBuilder fields(String... fields) {
result.setFields(fields);
return this;
}
public TermFacetRequestBuilder size(int size) {
result.setSize(size);
return this;
}
public TermFacetRequestBuilder excludeTerms(Object... terms) {
result.setExcludeTerms(terms);
return this;
}
public TermFacetRequestBuilder allTerms() {
result.setAllTerms(true);
return this;
}
public TermFacetRequestBuilder regex(String regex) {
result.setRegex(regex);
return this;
}
public TermFacetRequestBuilder regex(String regex, int regexFlag) {
result.setRegex(regex, regexFlag);
return this;
}
public TermFacetRequestBuilder ascTerm() {
result.setOrder(TermFacetOrder.ascTerm);
return this;
}
public TermFacetRequestBuilder descTerm() {
result.setOrder(TermFacetOrder.descTerm);
return this;
}
public TermFacetRequestBuilder ascCount() {
result.setOrder(TermFacetOrder.ascCount);
return this;
}
public TermFacetRequestBuilder descCount() {
result.setOrder(TermFacetOrder.descCount);
return this;
}
public TermFacetRequestBuilder applyQueryFilter() {
result.setApplyQueryFilter(true);
return this;
}
public FacetRequest build() {
return result;
}
}
@@ -1,38 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
import java.util.List;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetResult;
import org.springframework.data.elasticsearch.core.facet.FacetType;
/**
* @author Artur Konczak
*/
public class HistogramResult extends AbstractFacetResult {
private List<IntervalUnit> terms;
public HistogramResult(String name, List<IntervalUnit> terms) {
super(name, FacetType.term);
this.terms = terms;
}
public List<IntervalUnit> getIntervalUnit() {
return terms;
}
}
@@ -1,91 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Single term
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class IntervalUnit {
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
long key;
long count;
long totalCount;
double total;
double mean;
double min;
double max;
public IntervalUnit(long key, long count, long totalCount, double total, double mean, double min, double max) {
this.key = key;
this.count = count;
this.totalCount = totalCount;
this.total = total;
this.mean = mean;
this.min = min;
this.max = max;
}
public long getKey() {
return key;
}
public long getCount() {
return count;
}
public long getTotalCount() {
return totalCount;
}
public double getTotal() {
return total;
}
public double getMean() {
return mean;
}
public double getMin() {
return min;
}
public double getMax() {
return max;
}
@Override
public String toString() {
return "IntervalUnit{" +
"key=" + format.format(new Date(key)) +
", count=" + count +
", totalCount=" + totalCount +
", total=" + total +
", mean=" + mean +
", min=" + min +
", max=" + max +
'}';
}
}
@@ -1,78 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
/**
* Single range
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class Range {
private Double from;
private Double to;
private long count;
private double total;
private double totalCount;
private double min = Double.POSITIVE_INFINITY;
private double max = Double.NEGATIVE_INFINITY;
public Range(Double from, Double to, long count, double total, double totalCount, double min, double max) {
this.from = from;
this.to = to;
this.count = count;
this.total = total;
this.totalCount = totalCount;
this.min = min;
this.max = max;
}
public Double getFrom() {
return from;
}
public Double getTo() {
return to;
}
/**
* Return number of documents in range
*
* @return
*/
public long getCount() {
return count;
}
public double getTotal() {
return total;
}
public double getTotalCount() {
return totalCount;
}
public double getMin() {
return min;
}
public double getMax() {
return max;
}
}
@@ -1,43 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
import java.util.List;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetResult;
import org.springframework.data.elasticsearch.core.facet.FacetType;
/**
* Basic term facet result
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class RangeResult extends AbstractFacetResult {
private List<Range> ranges;
public RangeResult(String name, List<Range> ranges) {
super(name, FacetType.range);
this.ranges = ranges;
}
public List<Range> getRanges() {
return ranges;
}
}
@@ -1,85 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetResult;
import org.springframework.data.elasticsearch.core.facet.FacetType;
/**
* @author Petar Tahchiev
*/
public class StatisticalResult extends AbstractFacetResult {
private long count;
private double max;
private double min;
private double mean;
private double stdDeviation;
private double sumOfSquares;
private double total;
private double variance;
public StatisticalResult(String name, long count, double max, double min, double mean, double stdDeviation, double sumOfSquares, double total, double variance) {
super(name, FacetType.statistical);
this.count = count;
this.max = max;
this.min = min;
this.mean = mean;
this.stdDeviation = stdDeviation;
this.sumOfSquares = sumOfSquares;
this.total = total;
this.variance = variance;
}
public long getCount() {
return count;
}
public double getMax() {
return max;
}
public double getMin() {
return min;
}
public double getMean() {
return mean;
}
public double getStdDeviation() {
return stdDeviation;
}
public double getSumOfSquares() {
return sumOfSquares;
}
public double getTotal() {
return total;
}
public double getVariance() {
return variance;
}
}
@@ -1,43 +0,0 @@
/*
* Copyright 2014 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
*
* http://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.core.facet.result;
import java.util.List;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetResult;
import org.springframework.data.elasticsearch.core.facet.FacetType;
/**
* Basic term facet result
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class TermResult extends AbstractFacetResult {
private List<Term> terms;
public TermResult(String name, List<Term> terms) {
super(name, FacetType.term);
this.terms = terms;
}
public List<Term> getTerms() {
return terms;
}
}
@@ -0,0 +1,70 @@
package org.springframework.data.elasticsearch.core.geo;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.data.geo.Point;
/**
* @author Artur Konaczak
*/
public class CustomGeoModule extends SimpleModule {
private static final long serialVersionUID = 1L;
/**
* Creates a new {@link org.springframework.data.elasticsearch.core.geo.CustomGeoModule} registering serializers and deserializers for spring data commons geo-spatial types.
*/
public CustomGeoModule() {
super("Spring Data Elasticsearch Geo", new Version(1, 0, 0, null, "org.springframework.data.elasticsearch", "spring-data-elasticsearch-geo"));
addSerializer(Point.class, new PointSerializer());
addDeserializer(Point.class, new PointDeserializer());
}
}
class PointSerializer extends JsonSerializer<Point> {
@Override
public void serialize(Point value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeObject(GeoPoint.fromPoint(value));
// gen.writeStartObject();
// gen.writeNumberField("lat", value.getY());
// gen.writeNumberField("lon", value.getX());
// gen.writeEndObject();
}
}
class PointDeserializer extends JsonDeserializer<Point> {
@Override
public Point deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
GeoPoint point = p.readValueAs(GeoPoint.class);
// Double lat = null;
// Double lon = null;
// //skipp field name
// p.nextFieldName();
// if ("lat".equals(p.getCurrentName())) {
// //get value
// p.nextFieldName();
// lat = p.getDoubleValue();
// p.nextFieldName();
// }
// if ("lon".equals(p.getCurrentName())) {
// //get value
// p.nextFieldName();
// lon = p.getDoubleValue();
// }
// return new Point(lon, lat);
return GeoPoint.toPoint(point);
}
}
@@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.geo;
import org.springframework.data.geo.Box;
/**
* Geo bbox used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
@@ -37,4 +39,18 @@ public class GeoBox {
public GeoPoint getBottomRight() {
return bottomRight;
}
/**
* return a {@link org.springframework.data.elasticsearch.core.geo.GeoBox}
* from a {@link org.springframework.data.geo.Box}.
*
* @param box {@link org.springframework.data.geo.Box} to use
* @return a {@link org.springframework.data.elasticsearch.core.geo.GeoBox}
*/
public static GeoBox fromBox(Box box) {
GeoPoint topLeft = GeoPoint.fromPoint(box.getFirst());
GeoPoint bottomRight = GeoPoint.fromPoint(box.getSecond());
return new GeoBox(topLeft, bottomRight);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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,10 +15,13 @@
*/
package org.springframework.data.elasticsearch.core.geo;
import org.springframework.data.geo.Point;
/**
* geo-location used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
* @author Franck Marchand
* @author Mohsin Husen
*/
public class GeoPoint {
@@ -41,4 +44,20 @@ public class GeoPoint {
public double getLon() {
return lon;
}
/**
* build a GeoPoint from a {@link org.springframework.data.geo.Point}
*
* @param point {@link org.springframework.data.geo.Point}
* @return a {@link org.springframework.data.elasticsearch.core.geo.GeoPoint}
*/
public static GeoPoint fromPoint(Point point) {
return new GeoPoint(point.getX(), point.getY());
}
public static Point toPoint(GeoPoint point) {
return new Point(point.getLat(), point.getLon());
}
}
@@ -34,6 +34,8 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
short getReplicas();
boolean isUseServerConfiguration();
String getRefreshInterval();
String getIndexStoreType();
@@ -45,4 +47,6 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
ElasticsearchPersistentProperty getParentIdProperty();
String settingPath();
boolean isCreateIndexAndMapping();
}
@@ -18,6 +18,9 @@ package org.springframework.data.elasticsearch.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
@@ -30,11 +33,18 @@ import org.springframework.data.util.TypeInformation;
*/
public class SimpleElasticsearchMappingContext extends
AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> {
AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> implements ApplicationContextAware {
private ApplicationContext context;
@Override
protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new SimpleElasticsearchPersistentEntity<T>(typeInformation);
final SimpleElasticsearchPersistentEntity<T> persistentEntity =
new SimpleElasticsearchPersistentEntity<T>(typeInformation);
if (context != null) {
persistentEntity.setApplicationContext(context);
}
return persistentEntity;
}
@Override
@@ -42,4 +52,9 @@ public class SimpleElasticsearchMappingContext extends
SimpleElasticsearchPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new SimpleElasticsearchPersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}
@@ -29,6 +29,9 @@ import org.springframework.data.elasticsearch.annotations.Parent;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
@@ -43,8 +46,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
implements ElasticsearchPersistentEntity<T>, ApplicationContextAware {
private final StandardEvaluationContext context;
private final SpelExpressionParser parser;
private String indexName;
private String indexType;
private boolean useServerConfiguration;
private short shards;
private short replicas;
private String refreshInterval;
@@ -52,21 +58,26 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private String parentType;
private ElasticsearchPersistentProperty parentIdProperty;
private String settingPath;
private boolean createIndexAndMapping;
public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation);
this.context = new StandardEvaluationContext();
this.parser = new SpelExpressionParser();
Class<T> clazz = typeInformation.getType();
if (clazz.isAnnotationPresent(Document.class)) {
Document document = clazz.getAnnotation(Document.class);
Assert.hasText(document.indexName(),
" Unknown indexName. Make sure the indexName is defined. e.g @Document(indexName=\"foo\")");
this.indexName = typeInformation.getType().getAnnotation(Document.class).indexName();
this.indexName = document.indexName();
this.indexType = hasText(document.type()) ? document.type() : clazz.getSimpleName().toLowerCase(Locale.ENGLISH);
this.shards = typeInformation.getType().getAnnotation(Document.class).shards();
this.replicas = typeInformation.getType().getAnnotation(Document.class).replicas();
this.refreshInterval = typeInformation.getType().getAnnotation(Document.class).refreshInterval();
this.indexStoreType = typeInformation.getType().getAnnotation(Document.class).indexStoreType();
this.useServerConfiguration = document.useServerConfiguration();
this.shards = document.shards();
this.replicas = document.replicas();
this.refreshInterval = document.refreshInterval();
this.indexStoreType = document.indexStoreType();
this.createIndexAndMapping = document.createIndex();
}
if (clazz.isAnnotationPresent(Setting.class)) {
this.settingPath = typeInformation.getType().getAnnotation(Setting.class).settingPath();
@@ -82,12 +93,14 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
@Override
public String getIndexName() {
return indexName;
Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
@Override
public String getIndexType() {
return indexType;
Expression expression = parser.parseExpression(indexType, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
@Override
@@ -105,6 +118,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return replicas;
}
@Override
public boolean isUseServerConfiguration() {
return useServerConfiguration;
}
@Override
public String getRefreshInterval() {
return refreshInterval;
@@ -125,6 +143,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return settingPath;
}
@Override
public boolean isCreateIndexAndMapping() {
return createIndexAndMapping;
}
@Override
public void addPersistentProperty(ElasticsearchPersistentProperty property) {
super.addPersistentProperty(property);
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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,12 +15,10 @@
*/
package org.springframework.data.elasticsearch.core.query;
import static org.apache.commons.collections.CollectionUtils.*;
import static java.util.Collections.addAll;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.elasticsearch.action.search.SearchType;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -39,6 +37,7 @@ abstract class AbstractQuery implements Query {
protected List<String> indices = new ArrayList<String>();
protected List<String> types = new ArrayList<String>();
protected List<String> fields = new ArrayList<String>();
protected SourceFilter sourceFilter;
protected float minScore;
protected Collection<String> ids;
protected String route;
@@ -91,6 +90,16 @@ abstract class AbstractQuery implements Query {
return types;
}
@Override
public void addSourceFilter(SourceFilter sourceFilter) {
this.sourceFilter = sourceFilter;
}
@Override
public SourceFilter getSourceFilter() {
return sourceFilter;
}
@SuppressWarnings("unchecked")
public final <T extends Query> T addSort(Sort sort) {
if (sort == null) {
@@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.Map;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
/**
* @author Mohsin Husen
@@ -26,7 +26,7 @@ public class AliasBuilder {
private String indexName;
private String aliasName;
private FilterBuilder filterBuilder;
private QueryBuilder filterBuilder;
private Map<String, Object> filter;
private String searchRouting;
private String indexRouting;
@@ -42,7 +42,7 @@ public class AliasBuilder {
return this;
}
public AliasBuilder withFilterBuilder(FilterBuilder filterBuilder) {
public AliasBuilder withFilterBuilder(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
return this;
}
@@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.Map;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
/**
* AliasQuery is useful for creating new alias or deleting existing ones
@@ -28,7 +28,7 @@ public class AliasQuery {
private String indexName;
private String aliasName;
private FilterBuilder filterBuilder;
private QueryBuilder filterBuilder;
private Map<String, Object> filter;
private String searchRouting;
private String indexRouting;
@@ -50,11 +50,11 @@ public class AliasQuery {
this.aliasName = aliasName;
}
public FilterBuilder getFilterBuilder() {
public QueryBuilder getFilterBuilder() {
return filterBuilder;
}
public void setFilterBuilder(FilterBuilder filterBuilder) {
public void setFilterBuilder(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
}
@@ -21,6 +21,9 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
@@ -33,6 +36,17 @@ import org.springframework.util.Assert;
*/
public class Criteria {
@Override
public String toString() {
return "Criteria{" +
"field=" + field.getName() +
", boost=" + boost +
", negating=" + negating +
", queryCriteria=" + StringUtils.join(queryCriteria, '|') +
", filterCriteria=" + StringUtils.join(filterCriteria, '|') +
'}';
}
public static final String WILDCARD = "*";
public static final String CRITERIA_VALUE_SEPERATOR = " ";
@@ -69,7 +83,6 @@ public class Criteria {
public Criteria(Field field) {
Assert.notNull(field, "Field for criteria must not be null");
Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty");
this.criteriaChain.add(this);
this.field = field;
}
@@ -302,7 +315,18 @@ public class Criteria {
* @return
*/
public Criteria lessThanEqual(Object upperBound) {
between(null, upperBound);
if (upperBound == null) {
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.LESS_EQUAL, upperBound));
return this;
}
public Criteria lessThan(Object upperBound) {
if (upperBound == null) {
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.LESS, upperBound));
return this;
}
@@ -313,7 +337,18 @@ public class Criteria {
* @return
*/
public Criteria greaterThanEqual(Object lowerBound) {
between(lowerBound, null);
if (lowerBound == null) {
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER_EQUAL, lowerBound));
return this;
}
public Criteria greaterThan(Object lowerBound) {
if (lowerBound == null) {
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER, lowerBound));
return this;
}
@@ -324,12 +359,7 @@ public class Criteria {
* @return
*/
public Criteria in(Object... values) {
if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) {
throw new InvalidDataAccessApiUsageException("At least one element "
+ (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "")
+ " has to be present.");
}
return in(Arrays.asList(values));
return in(toCollection(values));
}
/**
@@ -344,6 +374,25 @@ public class Criteria {
return this;
}
private List<Object> toCollection(Object... values) {
if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) {
throw new InvalidDataAccessApiUsageException("At least one element "
+ (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "")
+ " has to be present.");
}
return Arrays.asList(values);
}
public Criteria notIn(Object... values) {
return notIn(toCollection(values));
}
public Criteria notIn(Iterable<?> values) {
Assert.notNull(values, "Collection of 'NotIn' values must not be null");
queryCriteria.add(new CriteriaEntry(OperationKey.NOT_IN, values));
return this;
}
/**
* Creates new CriteriaEntry for {@code location WITHIN distance}
*
@@ -360,6 +409,21 @@ public class Criteria {
return this;
}
/**
* Creates new CriteriaEntry for {@code location WITHIN distance}
*
* @param location {@link org.springframework.data.geo.Point} center coordinates
* @param distance {@link org.springframework.data.geo.Distance} radius
* .
* @return Criteria the chaind criteria with the new 'within' criteria included.
*/
public Criteria within(Point location, Distance distance) {
Assert.notNull(location, "Location value for near criteria must not be null");
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{location, distance}));
return this;
}
/**
* Creates new CriteriaEntry for {@code geoLocation WITHIN distance}
*
@@ -390,6 +454,17 @@ public class Criteria {
return this;
}
/**
* Creates new CriteriaEntry for {@code location Box bounding box}
*
* @param boundingBox {@link org.springframework.data.elasticsearch.core.geo.GeoBox} bounding box(left top corner + right bottom corner)
* @return Criteria the chaind criteria with the new 'boundingBox' criteria included.
*/
public Criteria boundedBy(Box boundingBox) {
Assert.notNull(boundingBox, "boundingBox value for boundedBy criteria must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{boundingBox.getFirst(), boundingBox.getSecond()}));
return this;
}
/**
* Creates new CriteriaEntry for bounding box created from points
@@ -419,10 +494,17 @@ public class Criteria {
return this;
}
public Criteria boundedBy(Point topLeftPoint, Point bottomRightPoint) {
Assert.notNull(topLeftPoint, "topLeftPoint must not be null");
Assert.notNull(bottomRightPoint, "bottomRightPoint must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{GeoPoint.fromPoint(topLeftPoint), GeoPoint.fromPoint(bottomRightPoint)}));
return this;
}
private void assertNoBlankInWildcardedQuery(String searchString, boolean leadingWildcard, boolean trailingWildcard) {
if (StringUtils.contains(searchString, CRITERIA_VALUE_SEPERATOR)) {
throw new InvalidDataAccessApiUsageException("Cannot constructQuery '" + (leadingWildcard ? "*" : "") + "\""
+ searchString + "\"" + (trailingWildcard ? "*" : "") + "'. Use epxression or mulitple clauses instead.");
+ searchString + "\"" + (trailingWildcard ? "*" : "") + "'. Use expression or multiple clauses instead.");
}
}
@@ -505,7 +587,7 @@ public class Criteria {
}
public enum OperationKey {
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR;
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, NOT_IN, WITHIN, BBOX, NEAR, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL;
}
public static class CriteriaEntry {
@@ -525,5 +607,13 @@ public class Criteria {
public Object getValue() {
return value;
}
@Override
public String toString() {
return "CriteriaEntry{" +
"key=" + key +
", value=" + value +
'}';
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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.
@@ -28,6 +28,8 @@ public class DeleteQuery {
private QueryBuilder query;
private String index;
private String type;
private Integer pageSize;
private Long scrollTimeInMillis;
public QueryBuilder getQuery() {
return query;
@@ -52,4 +54,20 @@ public class DeleteQuery {
public void setType(String type) {
this.type = type;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Long getScrollTimeInMillis() {
return scrollTimeInMillis;
}
public void setScrollTimeInMillis(Long scrollTimeInMillis) {
this.scrollTimeInMillis = scrollTimeInMillis;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016 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
*
* http://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.core.query;
/**
* SourceFilter implementation for providing includes and excludes.
*
* @Author Jon Tsiros
*/
public class FetchSourceFilter implements SourceFilter {
private final String[] includes;
private final String[] excludes;
public FetchSourceFilter(final String[] includes, final String[] excludes) {
this.includes = includes;
this.excludes = excludes;
}
@Override
public String[] getIncludes() {
return includes;
}
@Override
public String[] getExcludes() {
return excludes;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2016 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
*
* http://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.core.query;
/**
* SourceFilter builder for providing includes and excludes.
*
* @Author Jon Tsiros
*/
public class FetchSourceFilterBuilder {
private String[] includes;
private String[] excludes;
public FetchSourceFilterBuilder withIncludes(String... includes) {
this.includes = includes;
return this;
}
public FetchSourceFilterBuilder withExcludes(String... excludes) {
this.excludes = excludes;
return this;
}
public SourceFilter build() {
if (includes == null) includes = new String[0];
if (excludes == null) excludes = new String[0];
SourceFilter sourceFilter = new FetchSourceFilter(includes, excludes);
return sourceFilter;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016 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
*
* http://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.core.query;
/**
* Defines a IndexBoost to be applied on the "indices_boost" query clause
*
* @author Thiago Locatelli
*/
public class IndexBoost {
private String indexName;
private float boost;
public IndexBoost(String indexName, float boost) {
this.indexName = indexName;
this.boost = boost;
}
public String getIndexName() {
return indexName;
}
public float getBoost() {
return boost;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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.core.query;
import static org.apache.commons.collections.CollectionUtils.*;
import static java.util.Collections.addAll;
import static org.springframework.data.elasticsearch.core.query.AbstractQuery.*;
import java.util.ArrayList;
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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,11 +18,12 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.ArrayList;
import java.util.List;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import java.util.Arrays;
/**
* NativeSearchQuery
@@ -34,28 +35,31 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
private QueryBuilder query;
private FilterBuilder filter;
private QueryBuilder filter;
private List<SortBuilder> sorts;
private List<FacetRequest> facets;
private final List<ScriptField> scriptFields = new ArrayList<ScriptField>();
/*private List<FacetRequest> facets;*/
private List<AbstractAggregationBuilder> aggregations;
private HighlightBuilder.Field[] highlightFields;
private List<IndexBoost> indicesBoost;
public NativeSearchQuery(QueryBuilder query) {
this.query = query;
}
public NativeSearchQuery(QueryBuilder query, FilterBuilder filter) {
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter) {
this.query = query;
this.filter = filter;
}
public NativeSearchQuery(QueryBuilder query, FilterBuilder filter, List<SortBuilder> sorts) {
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
}
public NativeSearchQuery(QueryBuilder query, FilterBuilder filter, List<SortBuilder> sorts, HighlightBuilder.Field[] highlightFields) {
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts, HighlightBuilder.Field[] highlightFields) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
@@ -66,7 +70,7 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
return query;
}
public FilterBuilder getFilter() {
public QueryBuilder getFilter() {
return filter;
}
@@ -79,7 +83,18 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
return highlightFields;
}
public void addFacet(FacetRequest facetRequest) {
@Override
public List<ScriptField> getScriptFields() { return scriptFields; }
public void setScriptFields(List<ScriptField> scriptFields) {
this.scriptFields.addAll(scriptFields);
}
public void addScriptField(ScriptField... scriptField) {
scriptFields.addAll(Arrays.asList(scriptField));
}
/* public void addFacet(FacetRequest facetRequest) {
if (facets == null) {
facets = new ArrayList<FacetRequest>();
}
@@ -93,5 +108,32 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
@Override
public List<FacetRequest> getFacets() {
return facets;
}*/
@Override
public List<AbstractAggregationBuilder> getAggregations() {
return aggregations;
}
public void addAggregation(AbstractAggregationBuilder aggregationBuilder) {
if (aggregations == null) {
aggregations = new ArrayList<AbstractAggregationBuilder>();
}
aggregations.add(aggregationBuilder);
}
public void setAggregations(List<AbstractAggregationBuilder> aggregations) {
this.aggregations = aggregations;
}
@Override
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
public void setIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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,18 +15,16 @@
*/
package org.springframework.data.elasticsearch.core.query;
import static org.springframework.util.CollectionUtils.isEmpty;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* NativeSearchQuery
@@ -39,14 +37,17 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
public class NativeSearchQueryBuilder {
private QueryBuilder queryBuilder;
private FilterBuilder filterBuilder;
private QueryBuilder filterBuilder;
private List<ScriptField> scriptFields = new ArrayList<ScriptField>();
private List<SortBuilder> sortBuilders = new ArrayList<SortBuilder>();
private List<FacetRequest> facetRequests = new ArrayList<FacetRequest>();
private List<AbstractAggregationBuilder> aggregationBuilders = new ArrayList<AbstractAggregationBuilder>();
private HighlightBuilder.Field[] highlightFields;
private Pageable pageable;
private String[] indices;
private String[] types;
private String[] fields;
private SourceFilter sourceFilter;
private List<IndexBoost> indicesBoost;
private float minScore;
private Collection<String> ids;
private String route;
@@ -57,7 +58,7 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withFilter(FilterBuilder filterBuilder) {
public NativeSearchQueryBuilder withFilter(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
return this;
}
@@ -67,16 +68,31 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withFacet(FacetRequest facetRequest) {
facetRequests.add(facetRequest);
public NativeSearchQueryBuilder withScriptField(ScriptField scriptField) {
this.scriptFields.add(scriptField);
return this;
}
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder aggregationBuilder) {
this.aggregationBuilders.add(aggregationBuilder);
return this;
}
/* public NativeSearchQueryBuilder withFacet(FacetRequest facetRequest) {
facetRequests.add(facetRequest);
return this;
}*/
public NativeSearchQueryBuilder withHighlightFields(HighlightBuilder.Field... highlightFields) {
this.highlightFields = highlightFields;
return this;
}
public NativeSearchQueryBuilder withIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
return this;
}
public NativeSearchQueryBuilder withPageable(Pageable pageable) {
this.pageable = pageable;
return this;
@@ -97,6 +113,11 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withSourceFilter(SourceFilter sourceFilter) {
this.sourceFilter = sourceFilter;
return this;
}
public NativeSearchQueryBuilder withMinScore(float minScore) {
this.minScore = minScore;
return this;
@@ -135,8 +156,20 @@ public class NativeSearchQueryBuilder {
nativeSearchQuery.addFields(fields);
}
if (CollectionUtils.isNotEmpty(facetRequests)) {
nativeSearchQuery.setFacets(facetRequests);
if (sourceFilter != null) {
nativeSearchQuery.addSourceFilter(sourceFilter);
}
if(indicesBoost != null) {
nativeSearchQuery.setIndicesBoost(indicesBoost);
}
if (!isEmpty(scriptFields)) {
nativeSearchQuery.setScriptFields(scriptFields);
}
if (!isEmpty(aggregationBuilders)) {
nativeSearchQuery.setAggregations(aggregationBuilders);
}
if (minScore > 0) {
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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.
@@ -111,6 +111,21 @@ public interface Query {
*/
List<String> getFields();
/**
* Add source filter to be added as part of search request
*
* @param sourceFilter
*/
void addSourceFilter(SourceFilter sourceFilter);
/**
* Get SourceFilter to be returned to get include and exclude source
* fields as part of search request.
*
* @return SourceFilter
*/
SourceFilter getSourceFilter();
/**
* Get minimum score
*
@@ -135,6 +150,7 @@ public interface Query {
/**
* Type of search
*
* @return
*/
SearchType getSearchType();
@@ -0,0 +1,26 @@
package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.script.Script;
/**
* @author Ryan Murfitt
* @author Artur Konczak
*/
public class ScriptField {
private final String fieldName;
private final Script script;
public ScriptField(String fieldName, Script script) {
this.fieldName = fieldName;
this.script = script;
}
public String fieldName() {
return fieldName;
}
public Script script() {
return script;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,10 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.List;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
/**
* NativeSearchQuery
@@ -34,11 +33,18 @@ public interface SearchQuery extends Query {
QueryBuilder getQuery();
FilterBuilder getFilter();
QueryBuilder getFilter();
List<SortBuilder> getElasticsearchSorts();
List<FacetRequest> getFacets();
/*List<FacetRequest> getFacets();*/
List<AbstractAggregationBuilder> getAggregations();
HighlightBuilder.Field[] getHighlightFields();
List<IndexBoost> getIndicesBoost();
List<ScriptField> getScriptFields();
}
@@ -0,0 +1,28 @@
/*
* Copyright 2016 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
*
* http://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.core.query;
/**
* SourceFilter for providing includes and excludes.
*
* @Author Jon Tsiros
*/
public interface SourceFilter {
String[] getIncludes();
String[] getExcludes();
}
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
/**
* @author Rizwan Idrees
@@ -24,7 +25,7 @@ import org.elasticsearch.action.index.IndexRequest;
public class UpdateQuery {
private String id;
private IndexRequest indexRequest;
private UpdateRequest updateRequest;
private String indexName;
private String type;
private Class clazz;
@@ -38,12 +39,12 @@ public class UpdateQuery {
this.id = id;
}
public IndexRequest getIndexRequest() {
return indexRequest;
public UpdateRequest getUpdateRequest() {
return updateRequest;
}
public void setIndexRequest(IndexRequest indexRequest) {
this.indexRequest = indexRequest;
public void setUpdateRequest(UpdateRequest updateRequest) {
this.updateRequest = updateRequest;
}
public String getIndexName() {
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
/**
* @author Rizwan Idrees
@@ -24,6 +25,7 @@ import org.elasticsearch.action.index.IndexRequest;
public class UpdateQueryBuilder {
private String id;
private UpdateRequest updateRequest;
private IndexRequest indexRequest;
private String indexName;
private String type;
@@ -35,6 +37,11 @@ public class UpdateQueryBuilder {
return this;
}
public UpdateQueryBuilder withUpdateRequest(UpdateRequest updateRequest) {
this.updateRequest = updateRequest;
return this;
}
public UpdateQueryBuilder withIndexRequest(IndexRequest indexRequest) {
this.indexRequest = indexRequest;
return this;
@@ -66,7 +73,13 @@ public class UpdateQueryBuilder {
updateQuery.setIndexName(indexName);
updateQuery.setType(type);
updateQuery.setClazz(clazz);
updateQuery.setIndexRequest(indexRequest);
if (this.indexRequest != null) {
if (this.updateRequest == null) {
updateRequest = new UpdateRequest();
}
updateRequest.doc(indexRequest);
}
updateQuery.setUpdateRequest(updateRequest);
updateQuery.setDoUpsert(doUpsert);
return updateQuery;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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,7 +20,6 @@ import java.io.Serializable;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.repository.NoRepositoryBean;
@@ -37,9 +36,11 @@ public interface ElasticsearchRepository<T, ID extends Serializable> extends Ela
Iterable<T> search(QueryBuilder query);
FacetedPage<T> search(QueryBuilder query, Pageable pageable);
Page<T> search(QueryBuilder query, Pageable pageable);
FacetedPage<T> search(SearchQuery searchQuery);
Page<T> search(SearchQuery searchQuery);
Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);
void refresh();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -24,6 +24,7 @@ import java.util.Set;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactory;
import org.springframework.data.repository.cdi.CdiRepositoryBean;
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
import org.springframework.util.Assert;
/**
@@ -31,24 +32,36 @@ import org.springframework.util.Assert;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
*/
public class ElasticsearchRepositoryBean<T> extends CdiRepositoryBean<T> {
private final Bean<ElasticsearchOperations> elasticsearchOperationsBean;
/**
* Creates a new {@link ElasticsearchRepositoryBean}.
*
* @param operations must not be {@literal null}.
* @param qualifiers must not be {@literal null}.
* @param repositoryType must not be {@literal null}.
* @param beanManager must not be {@literal null}.
* @param detector detector for the custom {@link org.springframework.data.repository.Repository} implementations
* {@link CustomRepositoryImplementationDetector}, can be {@literal null}.
*/
public ElasticsearchRepositoryBean(Bean<ElasticsearchOperations> operations, Set<Annotation> qualifiers,
Class<T> repositoryType, BeanManager beanManager) {
super(qualifiers, repositoryType, beanManager);
Class<T> repositoryType, BeanManager beanManager, CustomRepositoryImplementationDetector detector) {
super(qualifiers, repositoryType, beanManager, detector);
Assert.notNull(operations, "Cannot create repository with 'null' for ElasticsearchOperations.");
this.elasticsearchOperationsBean = operations;
}
@Override
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType, Object customImplementation) {
ElasticsearchOperations elasticsearchOperations = getDependencyInstance(elasticsearchOperationsBean,
ElasticsearchOperations.class);
return new ElasticsearchRepositoryFactory(elasticsearchOperations).getRepository(repositoryType);
return new ElasticsearchRepositoryFactory(elasticsearchOperations).getRepository(repositoryType,
customImplementation);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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.
@@ -33,22 +33,23 @@ import org.springframework.data.repository.cdi.CdiRepositoryBean;
import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport;
/**
* ElasticsearchRepositoryExtension
* CDI extension to export Elasticsearch repositories.
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Oliver Gierke
* @author Mark Paluch
*/
public class ElasticsearchRepositoryExtension extends CdiRepositoryExtensionSupport {
private final Map<String, Bean<ElasticsearchOperations>> elasticsearchOperationsMap = new HashMap<String, Bean<ElasticsearchOperations>>();
private final Map<Set<Annotation>, Bean<ElasticsearchOperations>> elasticsearchOperationsMap = new HashMap<Set<Annotation>, Bean<ElasticsearchOperations>>();
@SuppressWarnings("unchecked")
<T> void processBean(@Observes ProcessBean<T> processBean) {
Bean<T> bean = processBean.getBean();
for (Type type : bean.getTypes()) {
if (type instanceof Class<?> && ElasticsearchOperations.class.isAssignableFrom((Class<?>) type)) {
elasticsearchOperationsMap.put(bean.getQualifiers().toString(), ((Bean<ElasticsearchOperations>) bean));
elasticsearchOperationsMap.put(bean.getQualifiers(), ((Bean<ElasticsearchOperations>) bean));
}
}
}
@@ -65,16 +66,17 @@ public class ElasticsearchRepositoryExtension extends CdiRepositoryExtensionSupp
}
}
private <T> CdiRepositoryBean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers, BeanManager beanManager) {
private <T> CdiRepositoryBean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers,
BeanManager beanManager) {
Bean<ElasticsearchOperations> elasticsearchOperationsBean = this.elasticsearchOperationsMap.get(qualifiers
.toString());
Bean<ElasticsearchOperations> elasticsearchOperationsBean = this.elasticsearchOperationsMap.get(qualifiers);
if (elasticsearchOperationsBean == null) {
throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.",
ElasticsearchOperations.class.getName(), qualifiers));
}
return new ElasticsearchRepositoryBean<T>(elasticsearchOperationsBean, qualifiers, repositoryType, beanManager);
return new ElasticsearchRepositoryBean<T>(elasticsearchOperationsBean, qualifiers, repositoryType, beanManager,
getCustomImplementationDetector());
}
}
@@ -15,8 +15,16 @@
*/
package org.springframework.data.elasticsearch.repository.config;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
@@ -72,4 +80,22 @@ public class ElasticsearchRepositoryConfigExtension extends RepositoryConfigurat
Element element = config.getElement();
builder.addPropertyReference("elasticsearchOperations", element.getAttribute("elasticsearch-template-ref"));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations()
*/
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Collections.<Class<? extends Annotation>>singleton(Document.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingTypes()
*/
@Override
protected Collection<Class<?>> getIdentifyingTypes() {
return Arrays.<Class<?>>asList(ElasticsearchRepository.class, ElasticsearchCrudRepository.class);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -21,6 +21,7 @@ import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean;
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
/**
@@ -29,6 +30,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -102,6 +104,13 @@ public @interface EnableElasticsearchRepositories {
*/
Class<?> repositoryFactoryBeanClass() default ElasticsearchRepositoryFactoryBean.class;
/**
* Configure the repository base class to be used to create repository proxies for this particular configuration.
*
* @return
*/
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
// Elasticsearch specific configuration
/**
@@ -111,4 +120,10 @@ public @interface EnableElasticsearchRepositories {
* @return
*/
String elasticsearchTemplateRef() default "elasticsearchTemplate";
/**
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
* repositories infrastructure.
*/
boolean considerNestedRepositories() default false;
}
@@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
@@ -22,12 +23,16 @@ import org.springframework.data.elasticsearch.repository.query.parser.Elasticsea
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.ClassUtils;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.StreamUtils;
/**
* ElasticsearchPartQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery {
@@ -44,18 +49,55 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
public Object execute(Object[] parameters) {
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
CriteriaQuery query = createQuery(accessor);
if (queryMethod.isPageQuery()) {
if(tree.isDelete()) {
Object result = countOrGetDocumentsForDelete(query, accessor);
elasticsearchOperations.delete(query, queryMethod.getEntityInformation().getJavaType());
return result;
} else if (queryMethod.isPageQuery()) {
query.setPageable(accessor.getPageable());
return elasticsearchOperations.queryForPage(query, queryMethod.getEntityInformation().getJavaType());
} else if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable() != null) {
query.setPageable(accessor.getPageable());
} else if (queryMethod.isStreamQuery()) {
Class<?> entityType = queryMethod.getEntityInformation().getJavaType();
if (query.getPageable() == null) {
query.setPageable(new PageRequest(0, 20));
}
return StreamUtils.createStreamFromIterator((CloseableIterator<Object>) elasticsearchOperations.stream(query, entityType));
} else if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable() == null) {
int itemCount = (int) elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
query.setPageable(new PageRequest(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
return elasticsearchOperations.queryForList(query, queryMethod.getEntityInformation().getJavaType());
} else if (tree.isCountProjection()) {
return elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
}
return elasticsearchOperations.queryForObject(query, queryMethod.getEntityInformation().getJavaType());
}
private Object countOrGetDocumentsForDelete(CriteriaQuery query, ParametersParameterAccessor accessor) {
Object result = null;
if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable() == null) {
int itemCount = (int) elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
query.setPageable(new PageRequest(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
result = elasticsearchOperations.queryForList(query, queryMethod.getEntityInformation().getJavaType());
}
if (ClassUtils.isAssignable(Number.class, queryMethod.getReturnedObjectType())) {
result = elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
}
return result;
}
public CriteriaQuery createQuery(ParametersParameterAccessor accessor) {
return new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2015 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,8 +19,7 @@ import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchEntityInformation;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchEntityInformationCreator;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.util.StringUtils;
@@ -30,30 +29,23 @@ import org.springframework.util.StringUtils;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Oliver Gierke
*/
public class ElasticsearchQueryMethod extends QueryMethod {
private final ElasticsearchEntityInformation<?, ?> entityInformation;
private Method method;
private final Query queryAnnotation;
public ElasticsearchQueryMethod(Method method, RepositoryMetadata metadata,
ElasticsearchEntityInformationCreator elasticsearchEntityInformationCreator) {
super(method, metadata);
this.entityInformation = elasticsearchEntityInformationCreator.getEntityInformation(metadata
.getReturnedDomainClass(method));
this.method = method;
public ElasticsearchQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
super(method, metadata, factory);
this.queryAnnotation = method.getAnnotation(Query.class);
}
public boolean hasAnnotatedQuery() {
return getQueryAnnotation() != null;
return this.queryAnnotation != null;
}
public String getAnnotatedQuery() {
String query = (String) AnnotationUtils.getValue(getQueryAnnotation(), "value");
String query = (String) AnnotationUtils.getValue(queryAnnotation, "value");
return StringUtils.hasText(query) ? query : null;
}
private Query getQueryAnnotation() {
return this.method.getAnnotation(Query.class);
}
}
@@ -20,9 +20,14 @@ import java.util.Iterator;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.repository.query.ParameterAccessor;
@@ -35,6 +40,8 @@ import org.springframework.data.repository.query.parser.PartTree;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
*/
public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuery, CriteriaQuery> {
@@ -55,7 +62,7 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
protected CriteriaQuery create(Part part, Iterator<Object> iterator) {
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context
.getPersistentPropertyPath(part.getProperty());
return new CriteriaQuery(from(part.getType(),
return new CriteriaQuery(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.PropertyToFieldNameConverter.INSTANCE)), iterator));
}
@@ -66,7 +73,7 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
}
PersistentPropertyPath<ElasticsearchPersistentProperty> path = context
.getPersistentPropertyPath(part.getProperty());
return base.addCriteria(from(part.getType(),
return base.addCriteria(from(part,
new Criteria(path.toDotPath(ElasticsearchPersistentProperty.PropertyToFieldNameConverter.INSTANCE)), iterator));
}
@@ -83,7 +90,9 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
return query.addSort(sort);
}
private Criteria from(Part.Type type, Criteria instance, Iterator<?> parameters) {
private Criteria from(Part part, Criteria instance, Iterator<?> parameters) {
Part.Type type = part.getType();
Criteria criteria = instance;
if (criteria == null) {
criteria = new Criteria();
@@ -93,8 +102,6 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
return criteria.is(true);
case FALSE:
return criteria.is(false);
case SIMPLE_PROPERTY:
return criteria.is(parameters.next());
case NEGATING_SIMPLE_PROPERTY:
return criteria.is(parameters.next()).not();
case REGEX:
@@ -106,12 +113,14 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
return criteria.endsWith(parameters.next().toString());
case CONTAINING:
return criteria.contains(parameters.next().toString());
case AFTER:
case GREATER_THAN:
return criteria.greaterThan(parameters.next());
case AFTER:
case GREATER_THAN_EQUAL:
return criteria.greaterThanEqual(parameters.next());
case BEFORE:
case LESS_THAN:
return criteria.lessThan(parameters.next());
case BEFORE:
case LESS_THAN_EQUAL:
return criteria.lessThanEqual(parameters.next());
case BETWEEN:
@@ -119,7 +128,57 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
case IN:
return criteria.in(asArray(parameters.next()));
case NOT_IN:
return criteria.in(asArray(parameters.next())).not();
return criteria.notIn(asArray(parameters.next()));
case SIMPLE_PROPERTY:
case WITHIN: {
Object firstParameter = parameters.next();
Object secondParameter = null;
if (type == Part.Type.SIMPLE_PROPERTY) {
if (part.getProperty().getType() != GeoPoint.class)
return criteria.is(firstParameter);
else {
// it means it's a simple find with exact geopoint matching (e.g. findByLocation)
// and because Elasticsearch does not have any kind of query with just a geopoint
// as argument we use a "geo distance" query with a distance of one meter.
secondParameter = ".001km";
}
} else {
secondParameter = parameters.next();
}
if (firstParameter instanceof GeoPoint && secondParameter instanceof String)
return criteria.within((GeoPoint) firstParameter, (String) secondParameter);
if (firstParameter instanceof Point && secondParameter instanceof Distance)
return criteria.within((Point) firstParameter, (Distance) secondParameter);
if (firstParameter instanceof String && secondParameter instanceof String)
return criteria.within((String) firstParameter, (String) secondParameter);
}
case NEAR: {
Object firstParameter = parameters.next();
if (firstParameter instanceof GeoBox) {
return criteria.boundedBy((GeoBox) firstParameter);
}
if (firstParameter instanceof Box) {
return criteria.boundedBy(GeoBox.fromBox((Box) firstParameter));
}
Object secondParameter = parameters.next();
// "near" query can be the same query as the "within" query
if (firstParameter instanceof GeoPoint && secondParameter instanceof String)
return criteria.within((GeoPoint) firstParameter, (String) secondParameter);
if (firstParameter instanceof Point && secondParameter instanceof Distance)
return criteria.within((Point) firstParameter, (Distance) secondParameter);
if (firstParameter instanceof String && secondParameter instanceof String)
return criteria.within((String) firstParameter, (String) secondParameter);
}
default:
throw new InvalidDataAccessApiUsageException("Illegal criteria found '" + type + "'.");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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.
@@ -21,19 +21,16 @@ import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.index.query.QueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.*;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.util.Assert;
@@ -45,6 +42,7 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Ryan Henszey
* @author Kevin Leturc
*/
public abstract class AbstractElasticsearchRepository<T, ID extends Serializable> implements
ElasticsearchRepository<T, ID> {
@@ -69,8 +67,10 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
this.entityInformation = metadata;
setEntityClass(this.entityInformation.getJavaType());
try {
createIndex();
putMapping();
if (createIndexAndMapping()) {
createIndex();
putMapping();
}
} catch (ElasticsearchException exception) {
LOGGER.error("failed to load elasticsearch nodes : " + exception.getDetailedMessage());
}
@@ -84,6 +84,10 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
elasticsearchOperations.putMapping(getEntityClass());
}
private boolean createIndexAndMapping() {
return elasticsearchOperations.getPersistentEntityFor(getEntityClass()).isCreateIndexAndMapping();
}
@Override
public T findOne(ID id) {
GetQuery query = new GetQuery();
@@ -121,9 +125,9 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
public Iterable<T> findAll(Iterable<ID> ids) {
Assert.notNull(ids, "ids can't be null.");
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(inQuery(entityInformation.getIdAttribute(), Lists.newArrayList(ids)))
.withIds(stringIdsRepresentation(ids))
.build();
return elasticsearchOperations.queryForPage(query, getEntityClass());
return elasticsearchOperations.multiGet(query, getEntityClass());
}
@Override
@@ -136,7 +140,7 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Cannot save 'null' entity.");
elasticsearchOperations.index(createIndexQuery(entity));
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
return entity;
}
@@ -148,7 +152,7 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
queries.add(createIndexQuery(s));
}
elasticsearchOperations.bulkIndex(queries);
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
return entities;
}
@@ -160,15 +164,12 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
@Override
public <S extends T> Iterable<S> save(Iterable<S> entities) {
Assert.notNull(entities, "Cannot insert 'null' as a List.");
if (!(entities instanceof Collection<?>)) {
throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection");
}
List<IndexQuery> queries = new ArrayList<IndexQuery>();
for (S s : entities) {
queries.add(createIndexQuery(s));
}
elasticsearchOperations.bulkIndex(queries);
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
return entities;
}
@@ -189,13 +190,13 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
}
@Override
public FacetedPage<T> search(QueryBuilder query, Pageable pageable) {
public Page<T> search(QueryBuilder query, Pageable pageable) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).withPageable(pageable).build();
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
}
@Override
public FacetedPage<T> search(SearchQuery query) {
public Page<T> search(SearchQuery query) {
return elasticsearchOperations.queryForPage(query, getEntityClass());
}
@@ -217,14 +218,14 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
Assert.notNull(id, "Cannot delete entity with id 'null'.");
elasticsearchOperations.delete(entityInformation.getIndexName(), entityInformation.getType(),
stringIdRepresentation(id));
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
}
@Override
public void delete(T entity) {
Assert.notNull(entity, "Cannot delete 'null' entity.");
delete(extractIdFromBean(entity));
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
}
@Override
@@ -240,7 +241,12 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
DeleteQuery deleteQuery = new DeleteQuery();
deleteQuery.setQuery(matchAllQuery());
elasticsearchOperations.delete(deleteQuery, getEntityClass());
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
elasticsearchOperations.refresh(entityInformation.getIndexName());
}
@Override
public void refresh() {
elasticsearchOperations.refresh(getEntityClass());
}
private IndexQuery createIndexQuery(T entity) {
@@ -302,6 +308,15 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
return null;
}
private List<String> stringIdsRepresentation(Iterable<ID> ids) {
Assert.notNull(ids, "ids can't be null.");
List<String> stringIds = new ArrayList<String>();
for (ID id : ids) {
stringIds.add(stringIdRepresentation(id));
}
return stringIds;
}
protected abstract String stringIdRepresentation(ID id);
private Long extractVersionFromBean(T entity) {
@@ -47,6 +47,7 @@ public class ElasticsearchEntityInformationCreatorImpl implements ElasticsearchE
.getPersistentEntity(domainClass);
Assert.notNull(persistentEntity, String.format("Unable to obtain mapping metadata for %s!", domainClass));
Assert.notNull(persistentEntity.getIdProperty(), String.format("No id property found for %s!", domainClass));
return new MappingElasticsearchEntityInformation<T, ID>(persistentEntity);
}
@@ -25,8 +25,10 @@ import org.springframework.data.elasticsearch.repository.ElasticsearchRepository
import org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery;
import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod;
import org.springframework.data.elasticsearch.repository.query.ElasticsearchStringQuery;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.QueryLookupStrategy;
@@ -59,28 +61,8 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
protected Object getTargetRepository(RepositoryMetadata metadata) {
ElasticsearchEntityInformation<?, ?> entityInformation = getEntityInformation(metadata.getDomainType());
AbstractElasticsearchRepository repository;
// Probably a better way to store and look these up.
if (Integer.class.isAssignableFrom(entityInformation.getIdType())
|| Long.class.isAssignableFrom(entityInformation.getIdType())
|| Double.class.isAssignableFrom(entityInformation.getIdType())) {
// logger.debug("Using NumberKeyedRepository for " + metadata.getRepositoryInterface());
repository = new NumberKeyedRepository(getEntityInformation(metadata.getDomainType()), elasticsearchOperations);
} else if (entityInformation.getIdType() == String.class) {
// logger.debug("Using SimpleElasticsearchRepository for " + metadata.getRepositoryInterface());
repository = new SimpleElasticsearchRepository(getEntityInformation(metadata.getDomainType()),
elasticsearchOperations);
} else {
throw new IllegalArgumentException("Unsuppored ID type " + entityInformation.getIdType());
}
repository.setEntityClass(metadata.getDomainType());
return repository;
protected Object getTargetRepository(RepositoryInformation metadata) {
return getTargetRepositoryViaReflection(metadata,getEntityInformation(metadata.getDomainType()), elasticsearchOperations);
}
@Override
@@ -88,7 +70,15 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
if (isQueryDslRepository(metadata.getRepositoryInterface())) {
throw new IllegalArgumentException("QueryDsl Support has not been implemented yet.");
}
return SimpleElasticsearchRepository.class;
if (Integer.class.isAssignableFrom(metadata.getIdType())
|| Long.class.isAssignableFrom(metadata.getIdType())
|| Double.class.isAssignableFrom(metadata.getIdType())) {
return NumberKeyedRepository.class;
} else if (metadata.getIdType() == String.class) {
return SimpleElasticsearchRepository.class;
} else {
throw new IllegalArgumentException("Unsuppored ID type " + metadata.getIdType());
}
}
private static boolean isQueryDslRepository(Class<?> repositoryInterface) {
@@ -101,11 +91,16 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
}
private class ElasticsearchQueryLookupStrategy implements QueryLookupStrategy {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries) {
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method, metadata, entityInformationCreator);
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method, metadata, factory);
String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
@@ -42,6 +42,7 @@ public class ElasticsearchRepositoryFactoryBean<T extends Repository<S, ID>, S,
*/
public void setElasticsearchOperations(ElasticsearchOperations operations) {
Assert.notNull(operations);
setMappingContext(operations.getElasticsearchConverter().getMappingContext());
this.operations = operations;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2015 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.
@@ -21,8 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.data.repository.core.support.PersistentEntityInformation;
import org.springframework.util.Assert;
/**
@@ -34,44 +33,27 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Ryan Henszey
* @author Oliver Gierke
*/
public class MappingElasticsearchEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID>
public class MappingElasticsearchEntityInformation<T, ID extends Serializable> extends PersistentEntityInformation<T, ID>
implements ElasticsearchEntityInformation<T, ID> {
private static final Logger logger = LoggerFactory.getLogger(MappingElasticsearchEntityInformation.class);
private final ElasticsearchPersistentEntity<T> entityMetadata;
private final String indexName;
private final String type;
private Class<?> idClass;
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity) {
this(entity, null, null);
}
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity, String indexName, String type) {
super(entity.getType());
super(entity);
this.entityMetadata = entity;
this.indexName = indexName;
this.type = type;
this.idClass = entity.getIdProperty().getType();
}
@SuppressWarnings("unchecked")
@Override
public ID getId(T entity) {
ElasticsearchPersistentProperty id = entityMetadata.getIdProperty();
try {
return (ID) BeanWrapper.create(entity, null).getProperty(id);
} catch (Exception e) {
throw new IllegalStateException("ID could not be resolved", e);
}
}
@SuppressWarnings("unchecked")
@Override
public Class<ID> getIdType() {
return (Class<ID>) idClass;
}
@Override
public String getIdAttribute() {
@@ -96,7 +78,7 @@ public class MappingElasticsearchEntityInformation<T, ID extends Serializable> e
ElasticsearchPersistentProperty versionProperty = entityMetadata.getVersionProperty();
try {
if (versionProperty != null) {
return (Long) BeanWrapper.create(entity, null).getProperty(versionProperty);
return (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty);
}
} catch (Exception e) {
throw new IllegalStateException("failed to load version field", e);
@@ -109,7 +91,7 @@ public class MappingElasticsearchEntityInformation<T, ID extends Serializable> e
ElasticsearchPersistentProperty parentProperty = entityMetadata.getParentIdProperty();
try {
if (parentProperty != null) {
return (String) BeanWrapper.create(entity, null).getProperty(parentProperty);
return (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty);
}
} catch (Exception e) {
throw new IllegalStateException("failed to load parent ID: " + e, e);
File diff suppressed because it is too large Load Diff
+200
View File
@@ -1,6 +1,206 @@
Spring Data Elasticsearch Changelog
===================================
Changes in version 2.0.3.RELEASE (2016-09-20)
---------------------------------------------
* DATAES-281 - Can't save entity without id setter.
* DATAES-268 - Release 2.0.3 (Hopper SR3).
Changes in version 2.1.0.M1 (2016-07-27)
----------------------------------------
* DATAES-262 - Upgrade Elasticsearch to 2.3.3.
* DATAES-250 - Release 2.1 M1 (Ingalls).
Changes in version 2.0.2.RELEASE (2016-06-15)
---------------------------------------------
* DATAES-251 - Release 2.0.2 (Hopper SR2).
Changes in version 2.0.1.RELEASE (2016-04-06)
---------------------------------------------
* DATAES-249 - Release 2.0.1 (Hopper SR1).
Changes in version 2.0.0.RELEASE (2016-04-06)
---------------------------------------------
* DATAES-245 - Release 2.0 GA (Hopper).
Changes in version 2.0.0.RC1 (2016-03-18)
-----------------------------------------
* DATAES-241 - remove commons-collections dependency and use CollectionUtils from Spring Utils.
* DATAES-240 - Release 2.0 RC1 (Hopper).
* DATAES-238 - ElasticsearchTemplate.prepareUpdate() does not preserve routing parameter which is required for updating child documents.
* DATAES-237 - path-configuration fails to load configs from inside jars.
* DATAES-236 - Clear the search contexts associated with specified scroll id.
* DATAES-234 - CDI support can fail due to Set.toString() used as Map key.
* DATAES-211 - Upgrade to Elasticsearch 2.0.
* DATAES-188 - Source filtering feature Implementation.
* DATAES-124 - ElasticSearchTemplate should expose client.
Changes in version 1.4.0.M1 (2016-02-12)
----------------------------------------
* DATAES-232 - Add code of conduct.
* DATAES-231 - Release 1.4 M1 (Hopper).
* DATAES-230 - Remove unnecessary type check in AbstractElasticsearchRepository.
* DATAES-224 - ElasticsearchTemplate discards newlines in mappings and settings files.
* DATAES-221 - Adapt to changes in Spring Data Commons.
* DATAES-216 - Add support to Indices Boost.
* DATAES-210 - Typo in error message.
* DATAES-209 - Handle dynamic mapping annotation at field level.
* DATAES-194 - Tests should clean up "data" directory.
* DATAES-171 - findByIdNotIn not work.
* DATAES-94 - Bump to support ES 1.2.0.
Changes in version 1.3.2.RELEASE (2015-12-18)
---------------------------------------------
* DATAES-223 - Release 1.3.2 (Gosling).
Changes in version 1.3.1.RELEASE (2015-11-15)
---------------------------------------------
* DATAES-212 - Release 1.3.1 (Gosling).
Changes in version 1.1.4.RELEASE (2015-10-14)
---------------------------------------------
* DATAES-202 - Release 1.1.4 (Evans).
Changes in version 1.3.0.RELEASE (2015-09-01)
---------------------------------------------
* DATAES-193 - Release 1.3 GA (Gosling).
* DATAES-137 - Should work out of the box with Spring Data REST.
Changes in version 1.3.0.RC1 (2015-08-04)
-----------------------------------------
* DATAES-186 - Release 1.3 RC1 (Gosling).
* DATAES-182 - Switch from BeanWrapper to PersistentPropertyAccessor.
Changes in version 1.2.2.RELEASE (2015-07-28)
---------------------------------------------
* DATAES-184 - Release 1.2.2 (Fowler).
Changes in version 1.0.6.RELEASE (2015-07-01)
---------------------------------------------
* DATAES-173 - Release 1.0.6 (Dijkstra).
Changes in version 1.1.3.RELEASE (2015-07-01)
---------------------------------------------
* DATAES-174 - Release 1.1.3 (Evans).
Changes in version 1.2.1.RELEASE (2015-06-30)
---------------------------------------------
* DATAES-175 - Release 1.2.1 (Fowler).
* DATAES-164 - CriteriaQuery equals method has to use AND operator instead of OR.
* DATAES-155 - findAll(Iterable<ID> ids) doesn't set persistent entity id.
Changes in version 1.3.0.M1 (2015-06-02)
----------------------------------------
* DATAES-168 - Release 1.3 M1 (Gosling).
* DATAES-164 - CriteriaQuery equals method has to use AND operator instead of OR.
* DATAES-162 - Adapt API changes in Spring Data Commons to simplify custom repository base class registration.
* DATAES-159 - Bulk update with ElasticsearchTemplate.
* DATAES-158 - UpdateRequest.script not working.
* DATAES-157 - support deleteBy operation.
* DATAES-155 - findAll(Iterable<ID> ids) doesn't set persistent entity id.
Changes in version 1.2.0.RELEASE (2015-03-23)
---------------------------------------------
* DATAES-154 - Release 1.2 GA.
Changes in version 1.2.0.RC1 (2015-03-05)
-----------------------------------------
* DATAES-152 - Release 1.2 RC1.
* DATAES-151 - findAll(Iterable<ID> ids) uses id representation in source instead of ES id.
* DATAES-140 - Document fields should not be indexed for search.
* DATAES-135 - Add necessary implementation for improved multi-store behavior.
* DATAES-132 - support include_in_parent for nested fieldtype.
* DATAES-130 - Allow Spring EL usage in type attribute of @Document.
* DATAES-129 - Custom Repository Method for simple geo request does not work.
* DATAES-115 - FindBy projections for list returns only 10 results.
* DATAES-100 - Allow configurable searchTimeout.
* DATAES-94 - Bump to support ES 1.2.0.
* DATAES-91 - Support for 'suggest' operations.
Changes in version 1.1.2.RELEASE (2015-01-28)
---------------------------------------------
* DATAES-146 - Release 1.1.2.
Changes in version 1.0.5.RELEASE (2015-01-27)
---------------------------------------------
* DATAES-145 - Release 1.0.5.
Changes in version 1.2.0.M1 (2014-12-01)
----------------------------------------
* DATAES-134 - Release 1.2 M1.
* DATAES-94 - Support for Elasticsearch 1.2.0.
* DATAES-76 - Add support for mapping generation of inherited fields.
Changes in version 1.1.1.RELEASE (2014-10-30)
---------------------------------------------
* DATAES-133 - Release 1.1.1.
Changes in version 1.1.0.RELEASE (2014-09-05)
---------------------------------------------
* DATAES-128 - Release 1.1 GA.
* DATAES-125 - the name of the isAnnotated method doesn't correspond to method body.
* DATAES-123 - Custom repository implementations are not picked up when using CDI.
* DATAES-120 - Polish reference documentation.
* DATAES-106 - Add support for countBy projections.
Changes in version 1.0.4.RELEASE (2014-08-27)
---------------------------------------------
* DATAES-122 - Release 1.0.4.
Changes in version 1.1.0.RC1 (2014-08-13)
-----------------------------------------
* DATAES-119 - Release 1.1 RC1.
* DATAES-117 - Displaying proper error message for entities that don't have an Id.
* DATAES-116 - Fix url typo in readme.md.
* DATAES-114 - Move to Asciidoctor for reference documentation.
* DATAES-113 - Add support for custom implementations in CDI repositories.
* DATAES-97 - UpdateQuery and UpdateBuilder should use UpdateRequest instead of the IndexRequest.
* DATAES-93 - Allow Spring EL usage in index name attribute of @Document.
* DATAES-89 - Support geolocation queries.
Changes in version 1.0.2.RELEASE (2014-07-28)
---------------------------------------------
* DATAES-107 - Release 1.0.2.
Changes in version 1.1.0.M1 (2014-07-10)
----------------------------------------
* DATAES-102 - Release 1.1 M1.
* DATAES-96 - Add support for aggregation in ElasticsearchTemplate.
* DATAES-94 - Bump to support ES 1.2.0.
Changes in version 1.0.1.RELEASE (2014-06-30)
---------------------------------------------
* DATAES-99 - Release 1.0.1.
Changes in version 1.0.0.RELEASE (2014-05-20)
---------------------------------------------
* DATAES-90 - Release 1.0 GA.
+2 -2
View File
@@ -1,5 +1,5 @@
Spring Data Elasticsearch 1.0 GA
Copyright (c) [2013-2014] Pivotal Software, Inc.
Spring Data Elasticsearch 2.0.3
Copyright (c) [2013-2016] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
@@ -5,7 +5,8 @@
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:repository="http://www.springframework.org/schema/data/repository"
targetNamespace="http://www.springframework.org/schema/data/elasticsearch"
elementFormDefault="qualified" attributeFormDefault="unqualified">
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
@@ -37,7 +38,7 @@
<xsd:element name="node-client">
<xsd:annotation>
<xsd:documentation source="java: org.springframework.data.elasticsearch.client.NodeClientFactoryBean"/>
<xsd:documentation/>
<xsd:appinfo>
<tool:assignable-to type="org.elasticsearch.client.Client"/>
</xsd:appinfo>
@@ -48,28 +49,52 @@
<xsd:attribute name="local" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation>
<![CDATA[local here means local on the JVM (well, actually class loader) level, meaning that two local servers started within the same JVM will discover themselves and form a cluster]]></xsd:documentation>
<![CDATA[local here means local on the JVM (well, actually class loader) level, meaning that two local servers started within the same JVM will discover themselves and form a cluster]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cluster-name" type="xsd:string" default="elasticsearch">
<xsd:annotation>
<xsd:documentation>
<![CDATA[Name of the cluster in which this instance of node client will connect to]]> </xsd:documentation>
<![CDATA[Name of the cluster in which this instance of node client will connect to]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="http-enabled" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[to enable or desable http port]]> </xsd:documentation>
<xsd:documentation>
<![CDATA[ to enable or disable http port ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path-data" type="xsd:string" default="">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ path to the data folder for node client ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path-home" type="xsd:string" default="">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ path to the home folder for node client ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path-configuration" type="xsd:string" default="">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ path to configuration file for node client ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="transport-client">
<xsd:annotation>
<xsd:documentation source="java: org.springframework.data.elasticsearch.client.TransportClientFactoryBean"/>
<xsd:documentation/>
<xsd:appinfo>
<tool:assignable-to type="org.elasticsearch.client.Client"/>
</xsd:appinfo>
@@ -80,42 +105,47 @@
<xsd:attribute name="cluster-nodes" type="xsd:string" default="127.0.0.1:9300">
<xsd:annotation>
<xsd:documentation>
<![CDATA[The comma delimited list of host:port entries to use for elasticsearch cluster.]]></xsd:documentation>
<![CDATA[The comma delimited list of host:port entries to use for elasticsearch cluster.]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cluster-name" type="xsd:string" default="elasticsearch">
<xsd:annotation>
<xsd:documentation>
<![CDATA[Name of the cluster in which this instance of node client will connect to]]> </xsd:documentation>
<![CDATA[Name of the cluster in which this instance of node client will connect to]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-transport-sniff" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation>
<![CDATA[The client allows to sniff the rest of the cluster, and add those into its list of machines to use.]]> </xsd:documentation>
<![CDATA[The client allows to sniff the rest of the cluster, and add those into its list of machines to use.]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-transport-ignore-cluster-name" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation>
<![CDATA[Set to true to ignore cluster name validation of connected nodes. (since 0.19.4)]]> </xsd:documentation>
<![CDATA[Set to true to ignore cluster name validation of connected nodes. (since 0.19.4)]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-transport-ping-timeout" type="xsd:string" default="5s">
<xsd:annotation>
<xsd:documentation>
<![CDATA[The time to wait for a ping response from a node. Defaults to 5s.]]> </xsd:documentation>
<![CDATA[The time to wait for a ping response from a node. Defaults to 5s.]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-transport-nodes-sampler-interval" type="xsd:string" default="5s">
<xsd:annotation>
<xsd:documentation>
<![CDATA[How often to sample / ping the nodes listed and connected. Defaults to 5s.]]> </xsd:documentation>
<![CDATA[How often to sample / ping the nodes listed and connected. Defaults to 5s.]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</xsd:schema>
@@ -0,0 +1,80 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' at
# the root directory of all plugins.
#
# A plugin can be 'site', 'jvm', or both.
#
### example site plugin for "foo":
#
# foo.zip <-- zip file for the plugin, with this structure:
# _site/ <-- the contents that will be served
# plugin-descriptor.properties <-- example contents below:
#
# site=true
# description=My cool plugin
# version=1.0
#
### example jvm plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# <arbitrary name1>.jar <-- classes, resources, dependencies
# <arbitrary nameN>.jar <-- any number of jars
# plugin-descriptor.properties <-- example contents below:
#
# jvm=true
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=2.0.0-rc1
# elasticsearch.version=2.0
# java.version=1.7
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Groovy scripting integration for Elasticsearch
#
# 'version': plugin's version
version=2.2.0
#
# 'name': the plugin name
name=lang-groovy
### mandatory elements for site plugins:
#
# 'site': set to true to indicate contents of the _site/
# directory in the root of the plugin should be served.
site=false
#
### mandatory elements for jvm plugins :
#
# 'jvm': true if the 'classname' class should be loaded
# from jar files in the root directory of the plugin.
# Note that only jar files in the root directory are
# added to the classpath for the plugin! If you need
# other resources, package them into a resources jar.
jvm=true
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.script.groovy.GroovyPlugin
#
# 'java.version' version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.7
#
# 'elasticsearch.version' version of elasticsearch compiled against
# You will have to release a new version of the plugin for each new
# elasticsearch release. This version is checked when the plugin
# is loaded so Elasticsearch will refuse to start in the presence of
# plugins with the incorrect elasticsearch.version.
elasticsearch.version=2.2.0
#
### deprecated elements for jvm plugins :
#
# 'isolated': true if the plugin should have its own classloader.
# passing false is deprecated, and only intended to support plugins
# that have hard dependencies against each other. If this is
# not specified, then the plugin is isolated by default.
isolated=true
#

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