1
0
mirror of synced 2026-07-07 02:30:01 +00:00

Compare commits

...

125 Commits

Author SHA1 Message Date
Spring Buildmaster 552a2dd834 DATAES-223 - Release version 1.3.2.RELEASE (Gosling SR2). 2015-12-18 00:55:31 -08:00
Oliver Gierke e7cd8dc346 DATAES-223 - Prepare 1.3.2.RELEASE (Gosling SR2). 2015-12-18 08:30:37 +01:00
Oliver Gierke 565c420834 DATAES-223 - Updated changelog. 2015-12-18 08:30:31 +01:00
Oliver Gierke b6c9a79df2 DATAES-212 - After release cleanups. 2015-11-19 12:58:42 +01:00
Spring Buildmaster b4c5ec6a23 DATAES-212 - Prepare next development iteration. 2015-11-15 05:41:19 -08:00
Spring Buildmaster a05507c505 DATAES-212 - Release version 1.3.1.RELEASE (Gosling SR1). 2015-11-15 05:41:17 -08:00
Oliver Gierke 0273d593a3 DATAES-212 - Prepare 1.3.1.RELEASE (Gosling SR1). 2015-11-15 14:06:54 +01:00
Oliver Gierke 33707d1228 DATAES-212 - Updated changelog. 2015-11-15 14:06:49 +01:00
Oliver Gierke 3fe4f15828 DATAES-202 - Updated changelog. 2015-10-14 13:57:54 +02:00
Artur Konczak 1c1b124d6e DATAES-193 - After release cleanups. 2015-09-28 11:51:26 +01:00
Kevin Leturc 11e2d11b5b DATAES-200 - BACKPORT from master - Set persistent entity index and type in query when no indices/types are set. 2015-09-27 20:12:01 +01:00
Artur Konczak 7db077c2c6 DATAES-171 - BACKPORT from master - added support for missing query keywords 2015-09-27 20:10:48 +01:00
Artur Konczak e6d698e250 DATAES-193 - Prepare next development iteration. 2015-09-27 20:04:50 +01: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
106 changed files with 7120 additions and 2939 deletions
+9 -4
View File
@@ -13,7 +13,7 @@ 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)
* [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).
@@ -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.2.0.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>1.3.0.BUILD-SNAPSHOT</version>
</dependency>
<repository>
@@ -52,6 +52,11 @@ the appropriate dependency version.
</repository>
```
###Note:
Spring data elaticsearch until 1.0.0.RELEASE version is on elasticsearch 1.1.x library, which uses java 1.6 or later version.
From spring data elastic search 1.1.0.RELEASE (current) on, the version of elasticsearch 1.3.x is supporting java 1.7 or later.
### 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 +244,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.md)
+160 -149
View File
@@ -1,178 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 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>1.3.2.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.7.2.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>1.5.2</elasticsearch>
<springdata.commons>1.11.2.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>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commonscollections}</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>
</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>1.16.4</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>
</build>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>http://repo.spring.io/libs-release</url>
</repository>
</repositories>
<developers>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
<timezone>+0</timezone>
</developer>
</developers>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>http://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<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>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
<ciManagement>
<system>Bamboo</system>
<url>http://build.springsource.org/browse/SPRINGDATAES</url>
</ciManagement>
<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>
<issueManagement>
<system>JIRA</system>
<url>https://jira.springsource.org/browse/DATAES</url>
</issueManagement>
<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 indexAnalyzer() default "simple";
boolean payloads() default false;
boolean preserveSeparators() default true;
boolean preservePositionIncrements() default true;
int maxInputLength() default 50;
}
@@ -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;
@@ -44,4 +51,6 @@ public @interface Field {
String indexAnalyzer() 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
}
@@ -29,6 +29,10 @@ import org.elasticsearch.index.query.GeoDistanceFilterBuilder;
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;
/**
@@ -106,20 +110,30 @@ class CriteriaFilterProcessor {
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);
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString());
} else if (valArray[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]);
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString());
} 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);
((GeoDistanceFilterBuilder) filter).lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])).distance(dist.toString());
} else {
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist);
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist.toString());
}
}
@@ -151,9 +165,40 @@ class CriteriaFilterProcessor {
return filter;
}
/**
* 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(GeoBoundingBoxFilterBuilder filter, Object value) {
Assert.isTrue(value instanceof GeoBox, "single-element of boundedBy filter must be type of GeoBox");
GeoBox geoBBox = (GeoBox) 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());
}
@@ -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,17 +134,21 @@ 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 = queryString(searchText).field(fieldName).defaultOperator(QueryStringQueryBuilder.Operator.AND);
break;
case CONTAINS:
query = queryString("*" + searchText + "*").field(fieldName).analyzeWildcard(true);
@@ -134,24 +160,42 @@ class CriteriaQueryProcessor {
query = queryString("*" + searchText).field(fieldName).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryString((String) value).field(fieldName);
query = queryString(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(queryString(item.toString()).field(fieldName));
}
break;
case NOT_IN:
query = boolQuery();
collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).mustNot(queryString(item.toString()).field(fieldName));
}
break;
}
return query;
}
@@ -65,6 +65,13 @@ 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) {
long totalHits = response.getHits().totalHits();
@@ -138,6 +145,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);
}
}
@@ -15,21 +15,23 @@
*/
package org.springframework.data.elasticsearch.core;
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.*;
import org.springframework.data.util.CloseableIterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* ElasticsearchOperations
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
public interface ElasticsearchOperations {
@@ -208,6 +210,46 @@ public interface ElasticsearchOperations {
*/
<T> FacetedPage<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 +288,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 +364,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 +381,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
*
@@ -406,6 +480,16 @@ public interface ElasticsearchOperations {
*/
<T> void refresh(Class<T> clazz, boolean waitForOperation);
/**
* Returns scroll id for criteria query
*
* @param query
* @param scrollTimeInMillis
* @param noFields
* @return
*/
String scan(CriteriaQuery query, long scrollTimeInMillis, boolean noFields);
/**
* Returns scroll id for scan query
*
@@ -471,4 +555,7 @@ public interface ElasticsearchOperations {
* @return
*/
Set<String> queryForAlias(String indexName);
<T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor);
}
@@ -31,6 +31,7 @@ import java.lang.reflect.Method;
import java.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
@@ -50,6 +51,9 @@ 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;
@@ -64,12 +68,17 @@ import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.facet.FacetBuilder;
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;
@@ -84,6 +93,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
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,14 +102,16 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Kevin Leturc
*/
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);
@@ -124,6 +136,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
this.resultsMapper = (resultsMapper == null) ? new DefaultResultMapper(this.elasticsearchConverter.getMappingContext()) : resultsMapper;
}
public void setSearchTimeout(String searchTimeout) {
this.searchTimeout = searchTimeout;
}
@Override
public <T> boolean createIndex(Class<T> clazz) {
return createIndexIfNotCreated(clazz);
@@ -188,7 +204,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());
@@ -247,6 +263,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
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,7 +290,7 @@ 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);
}
@@ -290,9 +312,12 @@ 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());
}
@@ -303,14 +328,144 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = prepareSearch(query, clazz).setQuery(query.getSource()).execute().actionGet();
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();
setPersistentEntityIndexAndType(query, clazz);
final String initScrollId = scan(query, scrollTimeInMillis, false);
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();
setPersistentEntityIndexAndType(query, clazz);
final String initScrollId = scan(query, scrollTimeInMillis, false);
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());
FilterBuilder 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();
FilterBuilder 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, FilterBuilder 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[] = isNotEmpty(query.getIndices()) ? query.getIndices().toArray(new String[query.getIndices().size()]) : retrieveIndexNameFromPersistentEntity(clazz);
String types[] = isNotEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()]) : retrieveTypeFromPersistentEntity(clazz);
Assert.notNull(indexName, "No index defined for Query");
@@ -319,15 +474,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
@@ -379,20 +526,35 @@ 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());
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(), query.getUpdateRequest().scriptType())
.setScriptParams(query.getUpdateRequest().scriptParams())
.setScriptLang(query.getUpdateRequest().scriptLang());
}
return updateRequestBuilder.execute().actionGet();
return updateRequestBuilder;
}
@Override
@@ -410,7 +572,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
);
}
}
@@ -479,42 +662,80 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
.setQuery(deleteQuery.getQuery()).execute().actionGet();
}
@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) {
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());
FilterBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
SearchRequestBuilder requestBuilder = prepareScan(criteriaQuery, scrollTimeInMillis, noFields);
if (elasticsearchQuery != null) {
requestBuilder.setQuery(elasticsearchQuery);
} else {
requestBuilder.setQuery(QueryBuilders.matchAllQuery());
}
if (elasticsearchFilter != null) {
requestBuilder.setPostFilter(elasticsearchFilter);
}
return getSearchResponse(requestBuilder.execute()).getScrollId();
}
@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");
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(searchQuery.getIndices())).setSearchType(SCAN)
.setQuery(searchQuery.getQuery()).setTypes(toArray(searchQuery.getTypes()))
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0)
.setSize(searchQuery.getPageable().getPageSize());
SearchRequestBuilder requestBuilder = prepareScan(searchQuery, scrollTimeInMillis, noFields);
if (searchQuery.getFilter() != null) {
requestBuilder.setPostFilter(searchQuery.getFilter());
}
if(isNotEmpty(searchQuery.getFields())) {
requestBuilder.addFields(toArray(searchQuery.getFields()));
return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()).execute()).getScrollId();
}
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(query.getPageable().getPageSize());
if (isNotEmpty(query.getFields())) {
requestBuilder.addFields(toArray(query.getFields()));
}
if (noFields) {
requestBuilder.setNoFields();
}
return requestBuilder.execute().actionGet().getScrollId();
return requestBuilder;
}
@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);
}
@@ -577,7 +798,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
requestBuilder.setBoostTerms(query.getBoostTerms());
}
SearchResponse response = requestBuilder.execute().actionGet();
SearchResponse response = getSearchResponse(requestBuilder.execute());
return resultsMapper.mapResults(response, clazz, query.getPageable());
}
@@ -608,7 +829,16 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
}
return searchRequest.setQuery(searchQuery.getQuery()).execute().actionGet();
if (CollectionUtils.isNotEmpty(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) {
@@ -668,12 +898,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);
}
@@ -835,6 +1060,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
}
private void setPersistentEntityIndexAndType(Query query, Class clazz) {
if (query.getIndices().isEmpty()) {
query.addIndices(retrieveIndexNameFromPersistentEntity(clazz));
}
if (query.getTypes().isEmpty()) {
query.addTypes(retrieveTypeFromPersistentEntity(clazz));
}
}
private String[] retrieveIndexNameFromPersistentEntity(Class clazz) {
if (clazz != null) {
return new String[]{getPersistentEntityFor(clazz).getIndexName()};
@@ -859,6 +1093,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);
@@ -893,11 +1134,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));
}
}
@@ -20,13 +20,16 @@ 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.elasticsearch.common.xcontent.XContentBuilder;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.completion.Completion;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -37,6 +40,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 {
@@ -50,9 +56,15 @@ class MappingBuilder {
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";
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
@@ -67,22 +79,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) {
@@ -92,14 +109,15 @@ class MappingBuilder {
}
boolean isGeoField = isGeoField(field);
boolean isCompletionField = isCompletionField(field);
Field singleField = field.getAnnotation(Field.class);
if (!isGeoField && isEntity(field) && !isAnnotated(field)) {
if (!isGeoField && !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;
}
@@ -111,6 +129,11 @@ class MappingBuilder {
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) {
@@ -125,8 +148,23 @@ 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 {
@@ -135,6 +173,24 @@ class MappingBuilder {
.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.indexAnalyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.indexAnalyzer());
}
}
xContentBuilder.endObject();
}
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
throws IOException {
xContentBuilder.startObject(field.getName())
@@ -146,9 +202,6 @@ 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,
@@ -162,7 +215,7 @@ 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())) {
@@ -177,9 +230,6 @@ 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,
@@ -204,9 +254,6 @@ 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,
@@ -226,9 +273,6 @@ class MappingBuilder {
/**
* 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 {
@@ -243,9 +287,6 @@ class MappingBuilder {
* 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 {
@@ -304,4 +345,8 @@ class MappingBuilder {
private static boolean isGeoField(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);
}
@@ -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);
}
}
}
@@ -56,7 +56,7 @@ public class DefaultFacetMapper {
for (TermsFacet.Entry entry : facet.getEntries()) {
entries.add(new Term(entry.getTerm().toString(), entry.getCount()));
}
return new TermResult(facet.getName(), entries);
return new TermResult(facet.getName(), entries, facet.getTotalCount(), facet.getOtherCount(), facet.getMissingCount());
}
private static FacetResult parseRange(RangeFacet facet) {
@@ -27,17 +27,36 @@ import org.springframework.data.elasticsearch.core.facet.FacetType;
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
* @author Matija Obreza
*/
public class TermResult extends AbstractFacetResult {
private List<Term> terms;
private long total;
private long other;
private long missing;
public TermResult(String name, List<Term> terms) {
public TermResult(String name, List<Term> terms, long total, long other, long missing) {
super(name, FacetType.term);
this.terms = terms;
this.total = total;
this.other = other;
this.missing = missing;
}
public List<Term> getTerms() {
return terms;
}
public long getTotal() {
return total;
}
public long getOther() {
return other;
}
public long getMissing() {
return missing;
}
}
@@ -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);
}
}
@@ -15,6 +15,8 @@
*/
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}.
*
@@ -41,4 +43,16 @@ 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.getY(), point.getX());
}
}
@@ -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,6 +46,8 @@ 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 short shards;
@@ -56,6 +61,8 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
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);
@@ -82,12 +89,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
@@ -21,6 +21,8 @@ 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.Distance;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
@@ -33,6 +35,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 +82,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 +314,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 +336,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 +358,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 +373,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 +408,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}
*
@@ -505,7 +568,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 +588,13 @@ public class Criteria {
public Object getValue() {
return value;
}
@Override
public String toString() {
return "CriteriaEntry{" +
"key=" + key +
", value=" + value +
'}';
}
}
}
@@ -15,15 +15,16 @@
*/
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.ArrayList;
import java.util.List;
/**
* NativeSearchQuery
*
@@ -37,6 +38,7 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
private FilterBuilder filter;
private List<SortBuilder> sorts;
private List<FacetRequest> facets;
private List<AbstractAggregationBuilder> aggregations;
private HighlightBuilder.Field[] highlightFields;
@@ -94,4 +96,21 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
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;
}
}
@@ -23,6 +23,7 @@ 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;
@@ -42,6 +43,7 @@ public class NativeSearchQueryBuilder {
private FilterBuilder filterBuilder;
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;
@@ -67,6 +69,11 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder aggregationBuilder) {
this.aggregationBuilders.add(aggregationBuilder);
return this;
}
public NativeSearchQueryBuilder withFacet(FacetRequest facetRequest) {
facetRequests.add(facetRequest);
return this;
@@ -139,6 +146,10 @@ public class NativeSearchQueryBuilder {
nativeSearchQuery.setFacets(facetRequests);
}
if (CollectionUtils.isNotEmpty(aggregationBuilders)) {
nativeSearchQuery.setAggregations(aggregationBuilders);
}
if (minScore > 0) {
nativeSearchQuery.setMinScore(minScore);
}
@@ -135,6 +135,7 @@ public interface Query {
/**
* Type of search
*
* @return
*/
SearchType getSearchType();
@@ -19,6 +19,7 @@ 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;
@@ -40,5 +41,7 @@ public interface SearchQuery extends Query {
List<FacetRequest> getFacets();
List<AbstractAggregationBuilder> getAggregations();
HighlightBuilder.Field[] getHighlightFields();
}
@@ -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-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
@@ -38,6 +38,7 @@ import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Oliver Gierke
* @author Mark Paluch
*/
public class ElasticsearchRepositoryExtension extends CdiRepositoryExtensionSupport {
@@ -65,7 +66,8 @@ 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());
@@ -75,6 +77,7 @@ public class ElasticsearchRepositoryExtension extends CdiRepositoryExtensionSupp
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();
}
@@ -19,8 +19,6 @@ 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.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.util.StringUtils;
@@ -33,14 +31,10 @@ import org.springframework.util.StringUtils;
*/
public class ElasticsearchQueryMethod extends QueryMethod {
private final ElasticsearchEntityInformation<?, ?> entityInformation;
private Method method;
public ElasticsearchQueryMethod(Method method, RepositoryMetadata metadata,
ElasticsearchEntityInformationCreator elasticsearchEntityInformationCreator) {
public ElasticsearchQueryMethod(Method method, RepositoryMetadata metadata) {
super(method, metadata);
this.entityInformation = elasticsearchEntityInformationCreator.getEntityInformation(metadata
.getReturnedDomainClass(method));
this.method = method;
}
@@ -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 + "'.");
}
@@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.repository.support;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
@@ -26,15 +26,23 @@ 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.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
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.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.util.Assert;
@@ -45,6 +53,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> {
@@ -121,9 +130,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
@@ -302,6 +311,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);
}
@@ -27,6 +27,7 @@ import org.springframework.data.elasticsearch.repository.query.ElasticsearchQuer
import org.springframework.data.elasticsearch.repository.query.ElasticsearchStringQuery;
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 +60,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 +69,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) {
@@ -105,7 +94,7 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method, metadata, entityInformationCreator);
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method, metadata);
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
+145
View File
@@ -1,6 +1,151 @@
Spring Data Elasticsearch Changelog
===================================
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 1.3.2
Copyright (c) [2013-2015] 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,31 @@
<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: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 +84,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>
@@ -15,13 +15,20 @@
*/
package org.springframework.data.elasticsearch;
import static org.apache.commons.lang.RandomStringUtils.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
@@ -35,7 +42,12 @@ import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.elasticsearch.entities.*;
import org.springframework.data.elasticsearch.entities.Author;
import org.springframework.data.elasticsearch.entities.Book;
import org.springframework.data.elasticsearch.entities.Car;
import org.springframework.data.elasticsearch.entities.GirlFriend;
import org.springframework.data.elasticsearch.entities.Person;
import org.springframework.data.elasticsearch.entities.PersonMultipleLevelNested;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -71,17 +83,17 @@ public class NestedObjectTests {
@Test
public void shouldIndexInitialLevelNestedObject() {
List<Car> cars = new ArrayList<Car>();
final List<Car> cars = new ArrayList<Car>();
Car saturn = new Car();
final Car saturn = new Car();
saturn.setName("Saturn");
saturn.setModel("SL");
Car subaru = new Car();
final Car subaru = new Car();
subaru.setName("Subaru");
subaru.setModel("Imprezza");
Car ford = new Car();
final Car ford = new Car();
ford.setName("Ford");
ford.setModel("Focus");
@@ -89,26 +101,26 @@ public class NestedObjectTests {
cars.add(subaru);
cars.add(ford);
Person foo = new Person();
final Person foo = new Person();
foo.setName("Foo");
foo.setId("1");
foo.setCar(cars);
Car car = new Car();
final Car car = new Car();
car.setName("Saturn");
car.setModel("Imprezza");
Person bar = new Person();
final Person bar = new Person();
bar.setId("2");
bar.setName("Bar");
bar.setCar(Arrays.asList(car));
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery();
final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(foo.getId());
indexQuery1.setObject(foo);
IndexQuery indexQuery2 = new IndexQuery();
final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(bar.getId());
indexQuery2.setObject(bar);
@@ -119,10 +131,10 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Person.class, true);
QueryBuilder builder = nestedQuery("car", boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza")));
final QueryBuilder builder = nestedQuery("car", boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza")));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
assertThat(persons.size(), is(1));
}
@@ -130,7 +142,7 @@ public class NestedObjectTests {
@Test
public void shouldIndexMultipleLevelNestedObject() {
//given
List<IndexQuery> indexQueries = createPerson();
final List<IndexQuery> indexQueries = createPerson();
//when
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
@@ -138,16 +150,36 @@ public class NestedObjectTests {
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true);
//then
GetQuery getQuery = new GetQuery();
final GetQuery getQuery = new GetQuery();
getQuery.setId("1");
PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class);
final PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class);
assertThat(personIndexed, is(notNullValue()));
}
@Test
public void shouldIndexMultipleLevelNestedObjectWithIncludeInParent() {
//given
final List<IndexQuery> indexQueries = createPerson();
//when
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
elasticsearchTemplate.bulkIndex(indexQueries);
// then
final Map mapping = elasticsearchTemplate.getMapping(PersonMultipleLevelNested.class);
assertThat(mapping, is(notNullValue()));
final Map propertyMap = (Map) mapping.get("properties");
assertThat(propertyMap, is(notNullValue()));
final Map bestCarsAttributes = (Map) propertyMap.get("bestCars");
assertThat(bestCarsAttributes.get("include_in_parent"), is(notNullValue()));
}
@Test
public void shouldSearchUsingNestedQueryOnMultipleLevelNestedObject() {
//given
List<IndexQuery> indexQueries = createPerson();
final List<IndexQuery> indexQueries = createPerson();
//when
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
@@ -155,15 +187,15 @@ public class NestedObjectTests {
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class, true);
//then
BoolQueryBuilder builder = boolQuery();
final BoolQueryBuilder builder = boolQuery();
builder.must(nestedQuery("girlFriends", termQuery("girlFriends.type", "temp")))
.must(nestedQuery("girlFriends.cars", termQuery("girlFriends.cars.name", "Ford".toLowerCase())));
SearchQuery searchQuery = new NativeSearchQueryBuilder()
final SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(builder)
.build();
Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class);
final Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class);
assertThat(personIndexed, is(notNullValue()));
assertThat(personIndexed.getTotalElements(), is(1L));
assertThat(personIndexed.getContent().get(0).getId(), is("1"));
@@ -172,55 +204,55 @@ public class NestedObjectTests {
private List<IndexQuery> createPerson() {
PersonMultipleLevelNested person1 = new PersonMultipleLevelNested();
final PersonMultipleLevelNested person1 = new PersonMultipleLevelNested();
person1.setId("1");
person1.setName("name");
Car saturn = new Car();
final Car saturn = new Car();
saturn.setName("Saturn");
saturn.setModel("SL");
Car subaru = new Car();
final Car subaru = new Car();
subaru.setName("Subaru");
subaru.setModel("Imprezza");
Car car = new Car();
final Car car = new Car();
car.setName("Saturn");
car.setModel("Imprezza");
Car ford = new Car();
final Car ford = new Car();
ford.setName("Ford");
ford.setModel("Focus");
GirlFriend permanent = new GirlFriend();
final GirlFriend permanent = new GirlFriend();
permanent.setName("permanent");
permanent.setType("permanent");
permanent.setCars(Arrays.asList(saturn, subaru));
GirlFriend temp = new GirlFriend();
final GirlFriend temp = new GirlFriend();
temp.setName("temp");
temp.setType("temp");
temp.setCars(Arrays.asList(car, ford));
person1.setGirlFriends(Arrays.asList(permanent, temp));
IndexQuery indexQuery1 = new IndexQuery();
final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(person1.getId());
indexQuery1.setObject(person1);
PersonMultipleLevelNested person2 = new PersonMultipleLevelNested();
final PersonMultipleLevelNested person2 = new PersonMultipleLevelNested();
person2.setId("2");
person2.setName("name");
person2.setGirlFriends(Arrays.asList(permanent));
IndexQuery indexQuery2 = new IndexQuery();
final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(person2.getId());
indexQuery2.setObject(person2);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(indexQuery1);
indexQueries.add(indexQuery2);
@@ -230,17 +262,17 @@ public class NestedObjectTests {
@Test
public void shouldSearchBooksForPersonInitialLevelNestedType() {
List<Car> cars = new ArrayList<Car>();
final List<Car> cars = new ArrayList<Car>();
Car saturn = new Car();
final Car saturn = new Car();
saturn.setName("Saturn");
saturn.setModel("SL");
Car subaru = new Car();
final Car subaru = new Car();
subaru.setName("Subaru");
subaru.setModel("Imprezza");
Car ford = new Car();
final Car ford = new Car();
ford.setName("Ford");
ford.setModel("Focus");
@@ -248,43 +280,43 @@ public class NestedObjectTests {
cars.add(subaru);
cars.add(ford);
Book java = new Book();
final Book java = new Book();
java.setId("1");
java.setName("java");
Author javaAuthor = new Author();
final Author javaAuthor = new Author();
javaAuthor.setId("1");
javaAuthor.setName("javaAuthor");
java.setAuthor(javaAuthor);
Book spring = new Book();
final Book spring = new Book();
spring.setId("2");
spring.setName("spring");
Author springAuthor = new Author();
final Author springAuthor = new Author();
springAuthor.setId("2");
springAuthor.setName("springAuthor");
spring.setAuthor(springAuthor);
Person foo = new Person();
final Person foo = new Person();
foo.setName("Foo");
foo.setId("1");
foo.setCar(cars);
foo.setBooks(Arrays.asList(java, spring));
Car car = new Car();
final Car car = new Car();
car.setName("Saturn");
car.setModel("Imprezza");
Person bar = new Person();
final Person bar = new Person();
bar.setId("2");
bar.setName("Bar");
bar.setCar(Arrays.asList(car));
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery();
final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(foo.getId());
indexQuery1.setObject(foo);
IndexQuery indexQuery2 = new IndexQuery();
final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(bar.getId());
indexQuery2.setObject(bar);
@@ -295,22 +327,22 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Person.class, true);
QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java")));
final QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java")));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
assertThat(persons.size(), is(1));
}
/*
DATAES-73
*/
*/
@Test
public void shouldIndexAndSearchMapAsNestedType() {
//given
Book book1 = new Book();
Book book2 = new Book();
final Book book1 = new Book();
final Book book2 = new Book();
book1.setId(randomNumeric(5));
book1.setName("testBook1");
@@ -318,21 +350,21 @@ public class NestedObjectTests {
book2.setId(randomNumeric(5));
book2.setName("testBook2");
Map<Integer, Collection<String>> map1 = new HashMap<Integer, Collection<String>>();
final Map<Integer, Collection<String>> map1 = new HashMap<Integer, Collection<String>>();
map1.put(1, Arrays.asList("test1", "test2"));
Map<Integer, Collection<String>> map2 = new HashMap<Integer, Collection<String>>();
final Map<Integer, Collection<String>> map2 = new HashMap<Integer, Collection<String>>();
map2.put(1, Arrays.asList("test3", "test4"));
book1.setBuckets(map1);
book2.setBuckets(map2);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
IndexQuery indexQuery1 = new IndexQuery();
final List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
final IndexQuery indexQuery1 = new IndexQuery();
indexQuery1.setId(book1.getId());
indexQuery1.setObject(book1);
IndexQuery indexQuery2 = new IndexQuery();
final IndexQuery indexQuery2 = new IndexQuery();
indexQuery2.setId(book2.getId());
indexQuery2.setObject(book2);
@@ -342,10 +374,10 @@ public class NestedObjectTests {
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(Book.class, true);
//then
SearchQuery searchQuery = new NativeSearchQueryBuilder()
final SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3")))
.build();
Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
final Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
assertThat(books.getContent().size(), is(1));
assertThat(books.getContent().get(0).getId(), is(book2.getId()));
@@ -0,0 +1,81 @@
/*
* 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;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.entities.SpELEntity;
import org.springframework.data.elasticsearch.repositories.spel.SpELRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* SpELEntityTest
*
* @author Artur Konczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/spel-repository-test.xml")
public class SpELEntityTests {
@Autowired private SpELRepository repository;
@Autowired private ElasticsearchTemplate template;
@Before
public void init() {
repository.deleteAll();
}
@Test
public void shouldDo() {
// Given
repository.save(new SpELEntity());
repository.save(new SpELEntity());
// When
// Then
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(QueryBuilders.matchAllQuery());
nativeSearchQuery.addIndices("abz-entity");
long count = template.count(nativeSearchQuery);
assertThat(count, is(2L));
}
@Test
public void shouldSupportSpelInType() {
// Given
SpELEntity spELEntity = new SpELEntity();
repository.save(spELEntity);
// When
// Then
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(QueryBuilders.matchAllQuery());
nativeSearchQuery.addIndices("abz-entity");
nativeSearchQuery.addTypes("myType");
long count = template.count(nativeSearchQuery);
assertThat(count, is(1L));
}
}
@@ -0,0 +1,55 @@
/*
* 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.builder;
import java.util.Date;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.entities.SampleInheritedEntity;
/**
* @author Kevin Leturc
*/
public class SampleInheritedEntityBuilder {
private SampleInheritedEntity result;
public SampleInheritedEntityBuilder(String id) {
result = new SampleInheritedEntity();
result.setId(id);
}
public SampleInheritedEntityBuilder createdDate(Date createdDate) {
result.setCreatedDate(createdDate);
return this;
}
public SampleInheritedEntityBuilder message(String message) {
result.setMessage(message);
return this;
}
public SampleInheritedEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-15 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,26 +21,41 @@ import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.repositories.sample.SampleElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.repository.Repository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class EnableElasticsearchRepositoriesTests {
public class EnableElasticsearchRepositoriesTests implements ApplicationContextAware {
ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@Configuration
@EnableElasticsearchRepositories(basePackages = "org.springframework.data.elasticsearch.repositories.sample")
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repositories.sample",
"org.springframework.data.elasticsearch.config"})
static class Config {
@Bean
@@ -52,8 +67,30 @@ public class EnableElasticsearchRepositoriesTests {
@Autowired
private SampleElasticsearchRepository repository;
@Autowired(required = false)
private SampleRepository nestedRepository;
interface SampleRepository extends Repository<SampleEntity, Long> {};
@Test
public void bootstrapsRepository() {
assertThat(repository, is(notNullValue()));
}
@Test
public void shouldScanSelectedPackage() {
//given
//when
String[] beanNamesForType = context.getBeanNamesForType(ElasticsearchRepository.class);
//then
assertThat(beanNamesForType.length, is(1));
assertThat(beanNamesForType[0], is("sampleElasticsearchRepository"));
}
@Test
public void hasNotNestedRepository() {
assertNull(nestedRepository);
}
}
@@ -0,0 +1,61 @@
/*
* 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.
* 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.config;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.repository.Repository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.elasticsearch.node.NodeBuilder.nodeBuilder;
import static org.junit.Assert.assertNotNull;
/**
* @author Kevin Leturc
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class EnableNestedElasticsearchRepositoriesTests {
@Configuration
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repositories.sample",
"org.springframework.data.elasticsearch.config"}, considerNestedRepositories = true)
static class Config {
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchTemplate(nodeBuilder().local(true).clusterName("testCluster2").node().client());
}
}
@Autowired(required = false)
private SampleRepository nestedRepository;
interface SampleRepository extends Repository<SampleEntity, Long> {};
@Test
public void hasNestedRepository() {
assertNotNull(nestedRepository);
}
}
@@ -199,5 +199,4 @@ public class AliasTests {
elasticsearchTemplate.putMapping(SampleEntity.class);
elasticsearchTemplate.refresh(SampleEntity.class, true);
}
}
@@ -15,14 +15,6 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.apache.commons.lang.RandomStringUtils.*;
import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.*;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
@@ -50,14 +42,25 @@ import org.springframework.data.elasticsearch.entities.HetroEntity1;
import org.springframework.data.elasticsearch.entities.HetroEntity2;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.entities.SampleMappingEntity;
import org.springframework.data.util.CloseableIterator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.*;
import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
import static org.elasticsearch.index.query.FilterBuilders.boolFilter;
import static org.elasticsearch.index.query.FilterBuilders.termFilter;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Abdul Mohammed
* @author Kevin Leturc
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
@@ -80,6 +83,26 @@ public class ElasticsearchTemplateTests {
elasticsearchTemplate.refresh(SampleEntity.class, true);
}
/*
DATAES-106
*/
@Test
public void shouldReturnCountForGivenCriteriaQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
// when
long count = elasticsearchTemplate.count(criteriaQuery, SampleEntity.class);
// then
assertThat(count, is(equalTo(1L)));
}
@Test
public void shouldReturnCountForGivenSearchQuery() {
// given
@@ -233,6 +256,41 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getTotalElements(), is(equalTo(2L)));
}
@Test
public void shouldDoBulkUpdate() {
//given
String documentId = randomNumeric(5);
String messageBeforeUpdate = "some test message";
String messageAfterUpdate = "test message";
SampleEntity sampleEntity = new SampleEntityBuilder(documentId)
.message(messageBeforeUpdate)
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
IndexRequest indexRequest = new IndexRequest();
indexRequest.source("message", messageAfterUpdate);
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(documentId)
.withClass(SampleEntity.class).withIndexRequest(indexRequest).build();
List<UpdateQuery> queries = new ArrayList<UpdateQuery>();
queries.add(updateQuery);
// when
elasticsearchTemplate.bulkUpdate(queries);
//then
GetQuery getQuery = new GetQuery();
getQuery.setId(documentId);
SampleEntity indexedEntity = elasticsearchTemplate.queryForObject(getQuery, SampleEntity.class);
assertThat(indexedEntity.getMessage(), is(messageAfterUpdate));
}
@Test
public void shouldDeleteDocumentForGivenId() {
// given
@@ -502,6 +560,28 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntity1, is(notNullValue()));
}
@Test
public void shouldDeleteGivenCriteriaQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("test message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("message").contains("test"));
// when
elasticsearchTemplate.delete(criteriaQuery, SampleEntity.class);
// then
StringQuery stringQuery = new StringQuery(matchAllQuery().toString());
List<SampleEntity> sampleEntities = elasticsearchTemplate.queryForList(stringQuery, SampleEntity.class);
assertThat(sampleEntities.size(), is(0));
}
@Test
public void shouldReturnSpecifiedFields() {
// given
@@ -568,8 +648,39 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getContent(), hasItem(sampleEntity));
}
/*
DATAES-167
*/
@Test
public void shouldReturnResultsWithScanAndScroll() {
public void shouldReturnResultsWithScanAndScrollForGivenCriteriaQuery() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
elasticsearchTemplate.bulkIndex(entities);
elasticsearchTemplate.refresh(SampleEntity.class, true);
// then
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices(INDEX_NAME);
criteriaQuery.addTypes(TYPE_NAME);
criteriaQuery.setPageable(new PageRequest(0, 10));
String scrollId = elasticsearchTemplate.scan(criteriaQuery, 1000, false);
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
boolean hasRecords = true;
while (hasRecords) {
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L, SampleEntity.class);
if (page.hasContent()) {
sampleEntities.addAll(page.getContent());
} else {
hasRecords = false;
}
}
assertThat(sampleEntities.size(), is(equalTo(30)));
}
@Test
public void shouldReturnResultsWithScanAndScrollForGivenSearchQuery() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
@@ -594,11 +705,60 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.size(), is(equalTo(30)));
}
/*
DATAES-167
*/
@Test
public void shouldReturnResultsWithScanAndScrollForSpecifiedFieldsForCriteriaCriteria() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
elasticsearchTemplate.bulkIndex(entities);
elasticsearchTemplate.refresh(SampleEntity.class, true);
// then
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices(INDEX_NAME);
criteriaQuery.addTypes(TYPE_NAME);
criteriaQuery.addFields("message");
criteriaQuery.setPageable(new PageRequest(0, 10));
String scrollId = elasticsearchTemplate.scan(criteriaQuery, 5000, false);
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
boolean hasRecords = true;
while (hasRecords) {
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L, new SearchResultMapper() {
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<SampleEntity> result = new ArrayList<SampleEntity>();
for (SearchHit searchHit : response.getHits()) {
String message = searchHit.getFields().get("message").getValue();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(searchHit.getId());
sampleEntity.setMessage(message);
result.add(sampleEntity);
}
if (result.size() > 0) {
return new FacetedPageImpl<T>((List<T>) result);
}
return null;
}
});
if (page != null) {
sampleEntities.addAll(page.getContent());
} else {
hasRecords = false;
}
}
assertThat(sampleEntities.size(), is(equalTo(30)));
}
/*
DATAES-84
*/
@Test
public void shouldReturnResultsWithScanAndScrollForSpecifiedFields() {
public void shouldReturnResultsWithScanAndScrollForSpecifiedFieldsForSearchCriteria() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
@@ -621,7 +781,7 @@ public class ElasticsearchTemplateTests {
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<SampleEntity> result = new ArrayList<SampleEntity>();
for(SearchHit searchHit : response.getHits()){
for (SearchHit searchHit : response.getHits()) {
String message = searchHit.getFields().get("message").getValue();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(searchHit.getId());
@@ -629,7 +789,7 @@ public class ElasticsearchTemplateTests {
result.add(sampleEntity);
}
if(result.size() > 0) {
if (result.size() > 0) {
return new FacetedPageImpl<T>((List<T>) result);
}
return null;
@@ -644,8 +804,57 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.size(), is(equalTo(30)));
}
/*
DATAES-167
*/
@Test
public void shouldReturnResultsForScanAndScrollWithCustomResultMapper() {
public void shouldReturnResultsForScanAndScrollWithCustomResultMapperForGivenCriteriaQuery() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
elasticsearchTemplate.bulkIndex(entities);
elasticsearchTemplate.refresh(SampleEntity.class, true);
// then
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices(INDEX_NAME);
criteriaQuery.addTypes(TYPE_NAME);
criteriaQuery.setPageable(new PageRequest(0, 10));
String scrollId = elasticsearchTemplate.scan(criteriaQuery, 1000, false);
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
boolean hasRecords = true;
while (hasRecords) {
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L, new SearchResultMapper() {
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
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);
}
if (chunk.size() > 0) {
return new FacetedPageImpl<T>((List<T>) chunk);
}
return null;
}
});
if (page != null) {
sampleEntities.addAll(page.getContent());
} else {
hasRecords = false;
}
}
assertThat(sampleEntities.size(), is(equalTo(30)));
}
@Test
public void shouldReturnResultsForScanAndScrollWithCustomResultMapperForGivenSearchQuery() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
@@ -688,6 +897,31 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.size(), is(equalTo(30)));
}
/*
DATAES-167
*/
@Test
public void shouldReturnResultsWithStreamForGivenCriteriaQuery() {
//given
List<IndexQuery> entities = createSampleEntitiesWithMessage("Test message", 30);
// when
elasticsearchTemplate.bulkIndex(entities);
elasticsearchTemplate.refresh(SampleEntity.class, true);
// then
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices(INDEX_NAME);
criteriaQuery.addTypes(TYPE_NAME);
criteriaQuery.setPageable(new PageRequest(0, 10));
CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(criteriaQuery, SampleEntity.class);
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
while (stream.hasNext()) {
sampleEntities.add(stream.next());
}
assertThat(sampleEntities.size(), is(equalTo(30)));
}
private static List<IndexQuery> createSampleEntitiesWithMessage(String message, int numberOfEntities) {
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
for (int i = 0; i < numberOfEntities; i++) {
@@ -1187,6 +1421,27 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getTotalElements(), greaterThanOrEqualTo(1L));
}
/*
DATAES-106
*/
@Test
public void shouldReturnCountForGivenCriteriaQueryWithGivenIndexUsingCriteriaQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices("test-index");
// when
long count = elasticsearchTemplate.count(criteriaQuery);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-67
*/
@@ -1210,6 +1465,28 @@ public class ElasticsearchTemplateTests {
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldReturnCountForGivenCriteriaQueryWithGivenIndexAndTypeUsingCriteriaQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices("test-index");
criteriaQuery.addTypes("test-type");
// when
long count = elasticsearchTemplate.count(criteriaQuery);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-67
*/
@@ -1234,6 +1511,43 @@ public class ElasticsearchTemplateTests {
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldReturnCountForGivenCriteriaQueryWithGivenMultiIndices() {
// given
cleanUpIndices();
String documentId1 = randomNumeric(5);
SampleEntity sampleEntity1 = new SampleEntityBuilder(documentId1).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery1 = new IndexQueryBuilder().withId(sampleEntity1.getId())
.withIndexName("test-index-1")
.withObject(sampleEntity1)
.build();
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntityBuilder(documentId2).message("some test message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery2 = new IndexQueryBuilder().withId(sampleEntity2.getId())
.withIndexName("test-index-2")
.withObject(sampleEntity2)
.build();
elasticsearchTemplate.bulkIndex(Arrays.asList(indexQuery1, indexQuery2));
elasticsearchTemplate.refresh("test-index-1", true);
elasticsearchTemplate.refresh("test-index-2", true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices("test-index-1", "test-index-2");
// when
long count = elasticsearchTemplate.count(criteriaQuery);
// then
assertThat(count, is(equalTo(2L)));
}
/*
DATAES-67
*/
@@ -1310,6 +1624,43 @@ public class ElasticsearchTemplateTests {
assertThat(elasticsearchTemplate.indexExists("test-index"), is(false));
}
/*
DATAES-106
*/
@Test
public void shouldReturnCountForGivenCriteriaQueryWithGivenIndexNameForSpecificIndex() {
// given
cleanUpIndices();
String documentId1 = randomNumeric(5);
SampleEntity sampleEntity1 = new SampleEntityBuilder(documentId1).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery1 = new IndexQueryBuilder().withId(sampleEntity1.getId())
.withIndexName("test-index-1")
.withObject(sampleEntity1)
.build();
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntityBuilder(documentId2).message("some test message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery2 = new IndexQueryBuilder().withId(sampleEntity2.getId())
.withIndexName("test-index-2")
.withObject(sampleEntity2)
.build();
elasticsearchTemplate.bulkIndex(Arrays.asList(indexQuery1, indexQuery2));
elasticsearchTemplate.refresh("test-index-1", true);
elasticsearchTemplate.refresh("test-index-2", true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
criteriaQuery.addIndices("test-index-1");
// when
long count = elasticsearchTemplate.count(criteriaQuery);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-67
*/
@@ -1349,11 +1700,28 @@ public class ElasticsearchTemplateTests {
assertThat(count, is(equalTo(1L)));
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAnExceptionForGivenCriteriaQueryWhenNoIndexSpecifiedForCountQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria());
// when
long count = elasticsearchTemplate.count(criteriaQuery);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-67
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAnExceptionWhenNoIndexSpecifiedForCountQuery() {
public void shouldThrowAnExceptionForGivenSearchQueryWhenNoIndexSpecifiedForCountQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntityBuilder(documentId).message("some message")
@@ -1378,19 +1746,19 @@ public class ElasticsearchTemplateTests {
public void shouldCreateIndexWithGivenSettings() {
// given
String settings = "{\n" +
" \"index\": {\n" +
" \"number_of_shards\": \"1\",\n" +
" \"number_of_replicas\": \"0\",\n" +
" \"analysis\": {\n" +
" \"analyzer\": {\n" +
" \"emailAnalyzer\": {\n" +
" \"type\": \"custom\",\n" +
" \"tokenizer\": \"uax_url_email\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
" \"index\": {\n" +
" \"number_of_shards\": \"1\",\n" +
" \"number_of_replicas\": \"0\",\n" +
" \"analysis\": {\n" +
" \"analyzer\": {\n" +
" \"emailAnalyzer\": {\n" +
" \"type\": \"custom\",\n" +
" \"tokenizer\": \"uax_url_email\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
elasticsearchTemplate.deleteIndex("test-index");
// when
@@ -22,19 +22,18 @@ import static org.junit.Assert.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.builder.SampleInheritedEntityBuilder;
import org.springframework.data.elasticsearch.builder.StockPriceBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.elasticsearch.entities.MinimalEntity;
import org.springframework.data.elasticsearch.entities.SampleTransientEntity;
import org.springframework.data.elasticsearch.entities.SimpleRecursiveEntity;
import org.springframework.data.elasticsearch.entities.StockPrice;
import org.springframework.data.elasticsearch.entities.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -42,6 +41,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Stuart Stevenson
* @author Jakub Vavrik
* @author Mohsin Husen
* @author Keivn Leturc
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
@@ -107,4 +107,44 @@ public class MappingBuilderTests {
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(MinimalEntity.class, "mapping", "id", "parentType");
assertThat(xContentBuilder.string(), is(expected));
}
/*
* DATAES-76
*/
@Test
public void shouldBuildMappingWithSuperclass() throws IOException {
final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" +
"type\":\"string\",\"index\":\"not_analyzed\",\"search_analyzer\":\"standard\"," +
"\"index_analyzer\":\"standard\"},\"createdDate\":{\"store\":false," +
"\"type\":\"date\",\"index\":\"not_analyzed\"}}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleInheritedEntity.class, "mapping", "id", null);
assertThat(xContentBuilder.string(), is(expected));
}
/*
* DATAES-76
*/
@Test
public void shouldAddSampleInheritedEntityDocumentToIndex() throws IOException {
//Given
//When
elasticsearchTemplate.deleteIndex(SampleInheritedEntity.class);
elasticsearchTemplate.createIndex(SampleInheritedEntity.class);
elasticsearchTemplate.putMapping(SampleInheritedEntity.class);
Date createdDate = new Date();
String message = "msg";
String id = "abc";
elasticsearchTemplate.index(new SampleInheritedEntityBuilder(id).createdDate(createdDate).message(message).buildIndex());
elasticsearchTemplate.refresh(SampleInheritedEntity.class, true);
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
List<SampleInheritedEntity> result = elasticsearchTemplate.queryForList(searchQuery, SampleInheritedEntity.class);
//Then
assertThat(result.size(), is(1));
SampleInheritedEntity entry = result.get(0);
assertThat(entry.getCreatedDate(), is(createdDate));
assertThat(entry.getMessage(), is(message));
}
}
@@ -0,0 +1,101 @@
/*
* 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.core.aggregation;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.Aggregations;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.facet.ArticleEntity;
import org.springframework.data.elasticsearch.core.facet.ArticleEntityBuilder;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.elasticsearch.action.search.SearchType.COUNT;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Jonathan Yan
* @author Artur Konczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateAggregationTests {
public static final String RIZWAN_IDREES = "Rizwan Idrees";
public static final String MOHSIN_HUSEN = "Mohsin Husen";
public static final String JONATHAN_YAN = "Jonathan Yan";
public static final String ARTUR_KONCZAK = "Artur Konczak";
public static final int YEAR_2002 = 2002;
public static final int YEAR_2001 = 2001;
public static final int YEAR_2000 = 2000;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Before
public void before() {
elasticsearchTemplate.deleteIndex(ArticleEntity.class);
elasticsearchTemplate.createIndex(ArticleEntity.class);
elasticsearchTemplate.putMapping(ArticleEntity.class);
elasticsearchTemplate.refresh(ArticleEntity.class, true);
IndexQuery article1 = new ArticleEntityBuilder("1").title("article four").subject("computing").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).score(10).buildIndex();
IndexQuery article2 = new ArticleEntityBuilder("2").title("article three").subject("computing").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).score(20).buildIndex();
IndexQuery article3 = new ArticleEntityBuilder("3").title("article two").subject("computing").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(30).buildIndex();
IndexQuery article4 = new ArticleEntityBuilder("4").title("article one").subject("accounting").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(40).buildIndex();
elasticsearchTemplate.index(article1);
elasticsearchTemplate.index(article2);
elasticsearchTemplate.index(article3);
elasticsearchTemplate.index(article4);
elasticsearchTemplate.refresh(ArticleEntity.class, true);
}
@Test
public void shouldReturnAggregatedResponseForGivenSearchQuery() {
// given
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withSearchType(COUNT)
.withIndices("articles").withTypes("article")
.addAggregation(terms("subjects").field("subject"))
.build();
// when
Aggregations aggregations = elasticsearchTemplate.query(searchQuery, new ResultsExtractor<Aggregations>() {
@Override
public Aggregations extract(SearchResponse response) {
return response.getAggregations();
}
});
// then
assertThat(aggregations, is(notNullValue()));
assertThat(aggregations.asMap().get("subjects"), is(notNullValue()));
}
}
@@ -0,0 +1,50 @@
package org.springframework.data.elasticsearch.core.completion;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* @author Mewes Kochheim
*/
@Document(indexName = "test-completion-index", type = "annotated-completion-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class AnnotatedCompletionEntity {
@Id
private String id;
private String name;
@CompletionField(payloads = true, maxInputLength = 100)
private Completion suggest;
private AnnotatedCompletionEntity() {
}
public AnnotatedCompletionEntity(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Completion getSuggest() {
return suggest;
}
public void setSuggest(Completion suggest) {
this.suggest = suggest;
}
}
@@ -0,0 +1,70 @@
/*
* 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.completion;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
/**
* @author Franck Marchand
* @author Mohsin Husen
* @author Mewes Kochheim
*/
public class AnnotatedCompletionEntityBuilder {
private AnnotatedCompletionEntity result;
public AnnotatedCompletionEntityBuilder(String id) {
result = new AnnotatedCompletionEntity(id);
}
public AnnotatedCompletionEntityBuilder name(String name) {
result.setName(name);
return this;
}
public AnnotatedCompletionEntityBuilder suggest(String[] input) {
return suggest(input, null, null, null);
}
public AnnotatedCompletionEntityBuilder suggest(String[] input, String output) {
return suggest(input, output, null, null);
}
public AnnotatedCompletionEntityBuilder suggest(String[] input, String output, Object payload) {
return suggest(input, output, payload, null);
}
public AnnotatedCompletionEntityBuilder suggest(String[] input, String output, Object payload, Integer weight) {
Completion suggest = new Completion(input);
suggest.setOutput(output);
suggest.setPayload(payload);
suggest.setWeight(weight);
result.setSuggest(suggest);
return this;
}
public AnnotatedCompletionEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
@@ -0,0 +1,52 @@
package org.springframework.data.elasticsearch.core.completion;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* @author Franck Marchand
* @author Mohsin Husen
* @author Mewes Kochheim
*/
@Document(indexName = "test-completion-index", type = "completion-annotation-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class CompletionAnnotatedEntity {
@Id
private String id;
private String name;
@CompletionField(payloads = true)
private Completion suggest;
private CompletionAnnotatedEntity() {
}
public CompletionAnnotatedEntity(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Completion getSuggest() {
return suggest;
}
public void setSuggest(Completion suggest) {
this.suggest = suggest;
}
}
@@ -0,0 +1,48 @@
package org.springframework.data.elasticsearch.core.completion;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* @author Mewes Kochheim
*/
@Document(indexName = "test-completion-index", type = "completion-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class CompletionEntity {
@Id
private String id;
private String name;
private Completion suggest;
private CompletionEntity() {
}
public CompletionEntity(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Completion getSuggest() {
return suggest;
}
public void setSuggest(Completion suggest) {
this.suggest = suggest;
}
}
@@ -0,0 +1,75 @@
/*
* 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.completion;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
/**
* @author Franck Marchand
* @author Mohsin Husen
* @author Mewes Kochheim
*/
public class CompletionEntityAnnotatedBuilder {
private CompletionAnnotatedEntity result;
public CompletionEntityAnnotatedBuilder(String id) {
result = new CompletionAnnotatedEntity(id);
}
public CompletionEntityAnnotatedBuilder name(String name) {
result.setName(name);
return this;
}
public CompletionEntityAnnotatedBuilder suggest(String[] input) {
return suggest(input, null, null, null);
}
public CompletionEntityAnnotatedBuilder suggest(String[] input, String output) {
return suggest(input, output, null, null);
}
public CompletionEntityAnnotatedBuilder suggest(String[] input, String output, Object payload) {
return suggest(input, output, payload, null);
}
public CompletionEntityAnnotatedBuilder suggest(String[] input, String output, Object payload, Integer weight) {
Completion suggest = new Completion(input);
if (output != null) {
suggest.setOutput(output);
}
if (payload != null) {
suggest.setPayload(payload);
}
if (weight != null) {
suggest.setWeight(weight);
}
result.setSuggest(suggest);
return this;
}
public CompletionAnnotatedEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
@@ -0,0 +1,68 @@
/*
* 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.completion;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
/**
* @author Mewes Kochheim
*/
public class CompletionEntityBuilder {
private CompletionEntity result;
public CompletionEntityBuilder(String id) {
result = new CompletionEntity(id);
}
public CompletionEntityBuilder name(String name) {
result.setName(name);
return this;
}
public CompletionEntityBuilder suggest(String[] input) {
return suggest(input, null, null, null);
}
public CompletionEntityBuilder suggest(String[] input, String output) {
return suggest(input, output, null, null);
}
public CompletionEntityBuilder suggest(String[] input, String output, Object payload) {
return suggest(input, output, payload, null);
}
public CompletionEntityBuilder suggest(String[] input, String output, Object payload, Integer weight) {
Completion suggest = new Completion(input);
suggest.setOutput(output);
suggest.setPayload(payload);
suggest.setWeight(weight);
result.setSuggest(suggest);
return this;
}
public CompletionEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
@@ -0,0 +1,238 @@
/*
* 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.core.completion;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.elasticsearch.action.suggest.SuggestResponse;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionFuzzyBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.entities.NonDocumentEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
* @author Mewes Kochheim
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateCompletionTests {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
private void loadCompletionObjectEntities() {
elasticsearchTemplate.deleteIndex(CompletionEntity.class);
elasticsearchTemplate.createIndex(CompletionEntity.class);
elasticsearchTemplate.refresh(CompletionEntity.class, true);
elasticsearchTemplate.putMapping(CompletionEntity.class);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(new CompletionEntityBuilder("1").name("Rizwan Idrees").suggest(new String[]{"Rizwan Idrees"}).buildIndex());
indexQueries.add(new CompletionEntityBuilder("2").name("Franck Marchand").suggest(new String[]{"Franck", "Marchand"}).buildIndex());
indexQueries.add(new CompletionEntityBuilder("3").name("Mohsin Husen").suggest(new String[]{"Mohsin", "Husen"}, "Mohsin Husen").buildIndex());
indexQueries.add(new CompletionEntityBuilder("4").name("Artur Konczak").suggest(new String[]{"Artur", "Konczak"}, "Artur Konczak").buildIndex());
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(CompletionEntity.class, true);
}
private void loadAnnotatedCompletionObjectEntities() {
elasticsearchTemplate.deleteIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.createIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
elasticsearchTemplate.putMapping(AnnotatedCompletionEntity.class);
NonDocumentEntity nonDocumentEntity = new NonDocumentEntity();
nonDocumentEntity.setSomeField1("foo");
nonDocumentEntity.setSomeField2("bar");
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(new AnnotatedCompletionEntityBuilder("1").name("Franck Marchand").suggest(new String[]{"Franck", "Marchand"}).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("2").name("Mohsin Husen").suggest(new String[]{"Mohsin", "Husen"}, "Mohsin Husen").buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("3").name("Rizwan Idrees").suggest(new String[]{"Rizwan", "Idrees"}, "Rizwan Idrees").buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("4").name("Artur Konczak").suggest(new String[]{"Artur", "Konczak"}, "Artur Konczak").buildIndex());
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
}
private void loadAnnotatedCompletionObjectEntitiesWithPayloads() {
elasticsearchTemplate.deleteIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.createIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
elasticsearchTemplate.putMapping(AnnotatedCompletionEntity.class);
NonDocumentEntity nonDocumentEntity = new NonDocumentEntity();
nonDocumentEntity.setSomeField1("Payload");
nonDocumentEntity.setSomeField2("test");
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(new AnnotatedCompletionEntityBuilder("1").name("Mewes Kochheim1").suggest(new String[]{"Mewes Kochheim1"}, null, Double.MAX_VALUE).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("2").name("Mewes Kochheim2").suggest(new String[]{"Mewes Kochheim2"}, null, Long.MAX_VALUE).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("3").name("Mewes Kochheim3").suggest(new String[]{"Mewes Kochheim3"}, null, "Payload test").buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("4").name("Mewes Kochheim4").suggest(new String[]{"Mewes Kochheim4"}, null, nonDocumentEntity).buildIndex());
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
}
private void loadAnnotatedCompletionObjectEntitiesWithWeights() {
elasticsearchTemplate.deleteIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.createIndex(AnnotatedCompletionEntity.class);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
elasticsearchTemplate.putMapping(AnnotatedCompletionEntity.class);
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
indexQueries.add(new AnnotatedCompletionEntityBuilder("1").name("Mewes Kochheim1").suggest(new String[]{"Mewes Kochheim1"}).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("2").name("Mewes Kochheim2").suggest(new String[]{"Mewes Kochheim2"}, null, null, 0).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("3").name("Mewes Kochheim3").suggest(new String[]{"Mewes Kochheim3"}, null, null, 1).buildIndex());
indexQueries.add(new AnnotatedCompletionEntityBuilder("4").name("Mewes Kochheim4").suggest(new String[]{"Mewes Kochheim4"}, null, null, Integer.MAX_VALUE).buildIndex());
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(AnnotatedCompletionEntity.class, true);
}
@Test
public void shouldPutMappingForGivenEntity() throws Exception {
//given
Class entity = CompletionEntity.class;
elasticsearchTemplate.createIndex(entity);
//when
assertThat(elasticsearchTemplate.putMapping(entity), is(true));
}
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionEntity() {
//given
loadCompletionObjectEntities();
CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest")
.text("m")
.field("suggest");
//when
SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class);
CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();
//then
assertThat(options.size(), is(2));
assertThat(options.get(0).getText().string(), isOneOf("Marchand", "Mohsin Husen"));
assertThat(options.get(1).getText().string(), isOneOf("Marchand", "Mohsin Husen"));
}
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
//given
loadAnnotatedCompletionObjectEntities();
CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest")
.text("m")
.field("suggest");
//when
SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class);
CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();
//then
assertThat(options.size(), is(2));
assertThat(options.get(0).getText().string(), isOneOf("Marchand", "Mohsin Husen"));
assertThat(options.get(1).getText().string(), isOneOf("Marchand", "Mohsin Husen"));
}
@Test
public void shouldFindSuggestionsWithPayloadsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
//given
loadAnnotatedCompletionObjectEntitiesWithPayloads();
CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest")
.text("m")
.field("suggest");
//when
SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class);
CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();
//then
assertThat(options.size(), is(4));
for (CompletionSuggestion.Entry.Option option : options) {
if (option.getText().string().equals("Mewes Kochheim1")) {
assertEquals(Double.MAX_VALUE, option.getPayloadAsDouble(), 0);
} else if (option.getText().string().equals("Mewes Kochheim2")) {
assertEquals(Long.MAX_VALUE, option.getPayloadAsLong());
} else if (option.getText().string().equals("Mewes Kochheim3")) {
assertEquals("Payload test", option.getPayloadAsString());
} else if (option.getText().string().equals("Mewes Kochheim4")) {
assertEquals("Payload", option.getPayloadAsMap().get("someField1"));
assertEquals("test", option.getPayloadAsMap().get("someField2"));
} else {
fail("Unexpected option");
}
}
}
@Test
public void shouldFindSuggestionsWithWeightsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
//given
loadAnnotatedCompletionObjectEntitiesWithWeights();
CompletionSuggestionFuzzyBuilder completionSuggestionFuzzyBuilder = new CompletionSuggestionFuzzyBuilder("test-suggest")
.text("m")
.field("suggest");
//when
SuggestResponse suggestResponse = elasticsearchTemplate.suggest(completionSuggestionFuzzyBuilder, CompletionEntity.class);
CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();
//then
assertThat(options.size(), is(4));
for (CompletionSuggestion.Entry.Option option : options) {
if (option.getText().string().equals("Mewes Kochheim1")) {
assertEquals(4, option.getScore(), 0);
} else if (option.getText().string().equals("Mewes Kochheim2")) {
assertEquals(0, option.getScore(), 0);
} else if (option.getText().string().equals("Mewes Kochheim3")) {
assertEquals(1, option.getScore(), 0);
} else if (option.getText().string().equals("Mewes Kochheim4")) {
assertEquals(Integer.MAX_VALUE, option.getScore(), 0);
} else {
fail("Unexpected option");
}
}
}
}
@@ -1,106 +1,114 @@
/*
* 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 static org.springframework.data.elasticsearch.annotations.FieldIndex.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
import static org.springframework.data.elasticsearch.annotations.FieldType.String;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.NestedField;
/**
* Simple type to test facets
*
* @author Artur Konczak
* @author Mohsin Husen
*/
@Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1", indexStoreType = "memory")
public class ArticleEntity {
@Id
private String id;
private String title;
@MultiField(
mainField = @Field(type = String, index = analyzed),
otherFields = {
@NestedField(dotSuffix = "untouched", type = String, store = true, index = not_analyzed),
@NestedField(dotSuffix = "sort", type = String, store = true, indexAnalyzer = "keyword")
}
)
private List<String> authors = new ArrayList<String>();
@Field(type = Integer, store = true)
private List<Integer> publishedYears = new ArrayList<Integer>();
private int score;
private ArticleEntity() {
}
public ArticleEntity(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getAuthors() {
return authors;
}
public void setAuthors(List<String> authors) {
this.authors = authors;
}
public List<Integer> getPublishedYears() {
return publishedYears;
}
public void setPublishedYears(List<Integer> publishedYears) {
this.publishedYears = publishedYears;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
/*
* 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 static org.springframework.data.elasticsearch.annotations.FieldIndex.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
import static org.springframework.data.elasticsearch.annotations.FieldType.String;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.NestedField;
/**
* Simple type to test facets
*
* @author Artur Konczak
* @author Mohsin Husen
*/
@Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1", indexStoreType = "memory")
public class ArticleEntity {
@Id
private String id;
private String title;
private String subject;
@MultiField(
mainField = @Field(type = String, index = analyzed),
otherFields = {
@NestedField(dotSuffix = "untouched", type = String, store = true, index = not_analyzed),
@NestedField(dotSuffix = "sort", type = String, store = true, indexAnalyzer = "keyword")
}
)
private List<String> authors = new ArrayList<String>();
@Field(type = Integer, store = true)
private List<Integer> publishedYears = new ArrayList<Integer>();
private int score;
private ArticleEntity() {
}
public ArticleEntity(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public List<String> getAuthors() {
return authors;
}
public void setAuthors(List<String> authors) {
this.authors = authors;
}
public List<Integer> getPublishedYears() {
return publishedYears;
}
public void setPublishedYears(List<Integer> publishedYears) {
this.publishedYears = publishedYears;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
@@ -1,64 +1,69 @@
/*
* 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.data.elasticsearch.core.query.IndexQuery;
/**
* Simple type to test facets
*
* @author Artur Konczak
* @author Mohsin Husen
*/
public class ArticleEntityBuilder {
private ArticleEntity result;
public ArticleEntityBuilder(String id) {
result = new ArticleEntity(id);
}
public ArticleEntityBuilder title(String title) {
result.setTitle(title);
return this;
}
public ArticleEntityBuilder addAuthor(String author) {
result.getAuthors().add(author);
return this;
}
public ArticleEntityBuilder addPublishedYear(Integer year) {
result.getPublishedYears().add(year);
return this;
}
public ArticleEntityBuilder score(int score) {
result.setScore(score);
return this;
}
public ArticleEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
/*
* 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.data.elasticsearch.core.query.IndexQuery;
/**
* Simple type to test facets
*
* @author Artur Konczak
* @author Mohsin Husen
*/
public class ArticleEntityBuilder {
private ArticleEntity result;
public ArticleEntityBuilder(String id) {
result = new ArticleEntity(id);
}
public ArticleEntityBuilder title(String title) {
result.setTitle(title);
return this;
}
public ArticleEntityBuilder subject(String subject) {
result.setSubject(subject);
return this;
}
public ArticleEntityBuilder addAuthor(String author) {
result.getAuthors().add(author);
return this;
}
public ArticleEntityBuilder addPublishedYear(Integer year) {
result.getPublishedYears().add(year);
return this;
}
public ArticleEntityBuilder score(int score) {
result.setScore(score);
return this;
}
public ArticleEntity build() {
return result;
}
public IndexQuery buildIndex() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(result.getId());
indexQuery.setObject(result);
return indexQuery;
}
}
@@ -102,6 +102,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(JONATHAN_YAN));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(10l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -129,6 +133,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -150,6 +158,10 @@ public class ElasticsearchTemplateFacetTests {
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(5l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -180,6 +192,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
assertThat(facet.getTotal(), is(10l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -210,6 +226,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
assertThat(facet.getTotal(), is(10l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -238,6 +258,43 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(1l));
}
@Test
public void shouldReturnExistingFacetedYearsForGivenQuery() {
// given
String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFilter(FilterBuilders.notFilter(FilterBuilders.missingFilter("publishedYears")))
.withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("publishedYears").descCount().build()).build();
// when
FacetedPage<ArticleEntity> result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(3)));
TermResult facet = (TermResult) result.getFacet(facetName);
assertThat(facet.getTerms().size(), is(equalTo(3)));
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2000)));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2001)));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@@ -283,6 +340,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(6);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
assertThat(facet.getTotal(), is(16l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(1l));
}
@Test
@@ -331,6 +392,10 @@ public class ElasticsearchTemplateFacetTests {
stringTerm = stringFacet.getTerms().get(3);
assertThat(stringTerm.getTerm(), is(RIZWAN_IDREES));
assertThat(stringTerm.getCount(), is(4));
assertThat(stringFacet.getTotal(), is(10l));
assertThat(stringFacet.getOther(), is(0l));
assertThat(stringFacet.getMissing(), is(0l));
}
@@ -360,6 +425,10 @@ public class ElasticsearchTemplateFacetTests {
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(1l));
}
@Test
@@ -381,6 +450,10 @@ public class ElasticsearchTemplateFacetTests {
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(2));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(4l));
assertThat(facet.getMissing(), is(0l));
}
@Test
@@ -398,6 +471,10 @@ public class ElasticsearchTemplateFacetTests {
TermResult facet = (TermResult) result.getFacet(facetName);
assertThat(facet.getTerms().size(), is(4));
assertThat(facet.getTotal(), is(6l));
assertThat(facet.getOther(), is(0l));
assertThat(facet.getMissing(), is(0l));
}
@@ -223,12 +223,12 @@ public class CriteriaQueryTests {
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(SampleEntity.class, true);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("message").is("some message").is("test message"));
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("message").is("some message"));
// when
Page<SampleEntity> page = elasticsearchTemplate.queryForPage(criteriaQuery, SampleEntity.class);
// then
assertThat("message", is(criteriaQuery.getCriteria().getField().getName()));
assertThat(page.getTotalElements(), is(greaterThanOrEqualTo(2L)));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
@@ -0,0 +1,51 @@
/*
* 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.entities;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @author Kevin Letur
*/
public class AbstractInheritedEntity {
@Id
private String id;
@Field(type = FieldType.Date, index = FieldIndex.not_analyzed)
private Date createdDate;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
}
@@ -16,7 +16,9 @@
package org.springframework.data.elasticsearch.entities;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
/**
* Sample DynamicSettingAndMappingEntity for test out dynamic setting using @Setting Annotation
@@ -40,7 +40,7 @@ public class Person {
@Field(type = FieldType.Nested)
private List<Car> car;
@Field(type = FieldType.Nested)
@Field(type = FieldType.Nested, includeInParent = true)
private List<Book> books;
public String getId() {
@@ -43,6 +43,9 @@ public class PersonMultipleLevelNested {
@Field(type = FieldType.Nested)
private List<Car> cars;
@Field(type = FieldType.Nested, includeInParent = true)
private List<Car> bestCars;
public String getId() {
return id;
}
@@ -74,4 +77,12 @@ public class PersonMultipleLevelNested {
public void setCars(List<Car> cars) {
this.cars = cars;
}
public List<Car> getBestCars() {
return bestCars;
}
public void setBestCars(List<Car> bestCars) {
this.bestCars = bestCars;
}
}
@@ -18,12 +18,21 @@ package org.springframework.data.elasticsearch.entities;
import java.util.Date;
import java.util.List;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @author Mohsin Husen
* @author Artur Konczak
*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(indexName = "test-product-index", type = "test-product-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class Product {
@@ -42,6 +51,7 @@ public class Product {
private Float weight;
@Field(type = FieldType.Float)
private Float price;
private Integer popularity;
@@ -52,93 +62,6 @@ public class Product {
private Date lastModified;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getTitle() {
return title;
}
public void setTitle(List<String> title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<String> getCategories() {
return categories;
}
public void setCategories(List<String> categories) {
this.categories = categories;
}
public Float getWeight() {
return weight;
}
public void setWeight(Float weight) {
this.weight = weight;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public Integer getPopularity() {
return popularity;
}
public void setPopularity(Integer popularity) {
this.popularity = popularity;
}
public boolean isAvailable() {
return available;
}
public void setAvailable(boolean available) {
this.available = available;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Date getLastModified() {
return lastModified;
}
public void setLastModified(Date lastModified) {
this.lastModified = lastModified;
}
@Override
public int hashCode() {
@@ -166,12 +89,4 @@ public class Product {
}
return true;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
@@ -20,6 +20,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
/**
* @author Rizwan Idrees
@@ -35,6 +36,9 @@ public class SampleEntity {
private int rate;
private boolean available;
private String highlightedMessage;
private GeoPoint location;
@Version
private Long version;
@@ -86,6 +90,14 @@ public class SampleEntity {
this.highlightedMessage = highlightedMessage;
}
public GeoPoint getLocation() {
return location;
}
public void setLocation(GeoPoint location) {
this.location = location;
}
public Long getVersion() {
return version;
}
@@ -0,0 +1,40 @@
/*
* 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.entities;
import static org.springframework.data.elasticsearch.annotations.FieldIndex.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.String;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
/**
* @author Kevin Leturc
*/
@Document(indexName = "test-inherited-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class SampleInheritedEntity extends AbstractInheritedEntity {
@Field(type = String, index = not_analyzed, store = true, searchAnalyzer = "standard", indexAnalyzer = "standard")
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
@@ -0,0 +1,39 @@
/*
* 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.entities;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* SpELEntity
*
* @author Artur Konczak
*/
@Document(indexName = "#{'abz'+'-'+'entity'}", type = "#{'my'+'Type'}", indexStoreType = "memory", shards = 1,
replicas = 0, refreshInterval = "-1")
public class SpELEntity {
@Id private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
@@ -0,0 +1,52 @@
/*
* 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.entities;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
/**
* Sample DynamicSettingAndMappingEntity for test out dynamic setting using @Setting Annotation
*
* @author Mohsin Husen
*/
@Document(indexName = "synonym-index", type = "synonym-type")
@Setting(settingPath = "/synonyms/settings.json")
@Mapping(mappingPath = "/synonyms/mappings.json")
public class SynonymEntity {
@Id
private String id;
private String text;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
@@ -19,8 +19,10 @@ import static org.apache.commons.lang.RandomStringUtils.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
@@ -30,14 +32,21 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.repositories.custom.SampleCustomMethodRepository;
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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Kevin Leturc
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:custom-method-repository-test.xml")
@@ -53,6 +62,7 @@ public class CustomMethodRepositoryTests {
public void before() {
elasticsearchTemplate.deleteIndex(SampleEntity.class);
elasticsearchTemplate.createIndex(SampleEntity.class);
elasticsearchTemplate.putMapping(SampleEntity.class);
elasticsearchTemplate.refresh(SampleEntity.class, true);
}
@@ -112,7 +122,7 @@ public class CustomMethodRepositoryTests {
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setRate(9);
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
@@ -486,4 +496,714 @@ public class CustomMethodRepositoryTests {
assertThat(sampleEntities.isEmpty(), is(false));
assertThat(sampleEntities.size(), is(1));
}
@Test
public void shouldExecuteCustomMethodWithGeoPoint() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
// when
Page<SampleEntity> page = repository.findByLocation(new GeoPoint(45.7806d, 3.0875d), new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
public void shouldExecuteCustomMethodWithGeoPointAndString() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(48.7806d, 3.0875d));
repository.save(sampleEntity);
// when
Page<SampleEntity> page = repository.findByLocationAndMessage(new GeoPoint(45.7806d, 3.0875d), "foo", new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
public void shouldExecuteCustomMethodWithWithinGeoPoint() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
// when
Page<SampleEntity> page = repository.findByLocationWithin(new GeoPoint(45.7806d, 3.0875d), "2km", new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
public void shouldExecuteCustomMethodWithWithinPoint() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
// when
Page<SampleEntity> page = repository.findByLocationWithin(new Point(3.0875d, 45.7806d), new Distance(2, Metrics.KILOMETERS), new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
public void shouldExecuteCustomMethodWithNearBox() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test2");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("foo");
sampleEntity2.setLocation(new GeoPoint(30.7806d, 0.0875d));
repository.save(sampleEntity2);
// when
Page<SampleEntity> pageAll = repository.findAll(new PageRequest(0, 10));
// then
assertThat(pageAll, is(notNullValue()));
assertThat(pageAll.getTotalElements(), is(equalTo(2L)));
// when
Page<SampleEntity> page = repository.findByLocationNear(new Box(new Point(3d, 46d), new Point(4d, 45d)), new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
@Test
public void shouldExecuteCustomMethodWithNearPointAndDistance() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
// when
Page<SampleEntity> page = repository.findByLocationNear(new Point(3.0875d, 45.7806d), new Distance(2, Metrics.KILOMETERS), new PageRequest(0, 10));
// then
assertThat(page, is(notNullValue()));
assertThat(page.getTotalElements(), is(equalTo(1L)));
}
/*
DATAES-165
*/
@Test
public void shouldAllowReturningJava8StreamInCustomQuery() {
// given
List<SampleEntity> entities = createSampleEntities("abc", 30);
repository.save(entities);
// when
Stream<SampleEntity> stream = repository.findByType("abc");
// then
assertThat(stream, is(notNullValue()));
assertThat(stream.count(), is(equalTo(30L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethod() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test2");
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByType("test");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForNot() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("some");
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByTypeNot("test");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithBooleanParameter() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("foo");
sampleEntity.setAvailable(true);
repository.save(sampleEntity);
// given
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setMessage("bar");
sampleEntity2.setAvailable(false);
repository.save(sampleEntity2);
// when
long count = repository.countByAvailable(false);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithLessThan() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(9);
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setRate(20);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByRateLessThan(10);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithBefore() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(20);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByRateBefore(10);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithAfter() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("some message");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(0);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByRateAfter(10);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithLike() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByMessageLike("fo");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForStartingWith() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByMessageStartingWith("fo");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForEndingWith() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByMessageEndingWith("o");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForContains() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("some message");
repository.save(sampleEntity2);
// when
long count = repository.countByMessageContaining("fo");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForIn() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
// given
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setMessage("bar");
repository.save(sampleEntity2);
List<String> ids = Arrays.asList(documentId, documentId2);
// when
long count = repository.countByIdIn(ids);
// then
assertThat(count, is(equalTo(2L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForNotIn() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("foo");
repository.save(sampleEntity);
// given
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setMessage("bar");
repository.save(sampleEntity2);
List<String> ids = Arrays.asList(documentId);
// when
long count = repository.countByIdNotIn(ids);
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForTrue() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("foo");
sampleEntity.setAvailable(true);
repository.save(sampleEntity);
// given
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setMessage("bar");
sampleEntity2.setAvailable(false);
repository.save(sampleEntity2);
// when
long count = repository.countByAvailableTrue();
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodForFalse() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setMessage("foo");
sampleEntity.setAvailable(true);
repository.save(sampleEntity);
// given
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId2);
sampleEntity2.setType("test");
sampleEntity2.setMessage("bar");
sampleEntity2.setAvailable(false);
repository.save(sampleEntity2);
// when
long count = repository.countByAvailableFalse();
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithWithinGeoPoint() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("foo");
sampleEntity2.setLocation(new GeoPoint(30.7806d, 0.0875d));
repository.save(sampleEntity2);
// when
long count = repository.countByLocationWithin(new GeoPoint(45.7806d, 3.0875d), "2km");
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithWithinPoint() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("foo");
sampleEntity2.setLocation(new GeoPoint(30.7806d, 0.0875d));
repository.save(sampleEntity2);
// when
long count = repository.countByLocationWithin(new Point(3.0875d, 45.7806d), new Distance(2, Metrics.KILOMETERS));
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithNearBox() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test2");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("foo");
sampleEntity2.setLocation(new GeoPoint(30.7806d, 0.0875d));
repository.save(sampleEntity2);
// when
long count = repository.countByLocationNear(new Box(new Point(3d, 46d), new Point(4d, 45d)));
// then
assertThat(count, is(equalTo(1L)));
}
/*
DATAES-106
*/
@Test
public void shouldCountCustomMethodWithNearPointAndDistance() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setType("test");
sampleEntity.setRate(10);
sampleEntity.setMessage("foo");
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
repository.save(sampleEntity);
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("test");
sampleEntity2.setRate(10);
sampleEntity2.setMessage("foo");
sampleEntity2.setLocation(new GeoPoint(30.7806d, 0.0875d));
repository.save(sampleEntity2);
// when
long count = repository.countByLocationNear(new Point(3.0875d, 45.7806d), new Distance(2, Metrics.KILOMETERS));
// then
assertThat(count, is(equalTo(1L)));
}
private List<SampleEntity> createSampleEntities(String type, int numberOfEntities) {
List<SampleEntity> entities = new ArrayList<SampleEntity>();
for (int i = 0; i < numberOfEntities; i++) {
SampleEntity entity = new SampleEntity();
entity.setId(randomNumeric(numberOfEntities));
entity.setAvailable(true);
entity.setMessage("Message");
entity.setType(type);
entities.add(entity);
}
return entities;
}
}
@@ -24,6 +24,8 @@ import javax.inject.Inject;
class CdiRepositoryClient {
private CdiProductRepository repository;
private SamplePersonRepository samplePersonRepository;
public CdiProductRepository getRepository() {
return repository;
@@ -33,4 +35,13 @@ class CdiRepositoryClient {
public void setRepository(CdiProductRepository repository) {
this.repository = repository;
}
public SamplePersonRepository getSamplePersonRepository() {
return samplePersonRepository;
}
@Inject
public void setSamplePersonRepository(SamplePersonRepository samplePersonRepository) {
this.samplePersonRepository = samplePersonRepository;
}
}
@@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.repositories.cdi;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import org.apache.webbeans.cditest.CdiTestContainer;
@@ -33,6 +34,7 @@ public class CdiRepositoryTests {
private static CdiTestContainer cdiContainer;
private CdiProductRepository repository;
private SamplePersonRepository personRepository;
@BeforeClass
public static void init() throws Exception {
@@ -51,6 +53,7 @@ public class CdiRepositoryTests {
public void setUp() {
CdiRepositoryClient client = cdiContainer.getInstance(CdiRepositoryClient.class);
repository = client.getRepository();
personRepository = client.getSamplePersonRepository();
}
@Test
@@ -80,4 +83,13 @@ public class CdiRepositoryTests {
retrieved = repository.findOne(bean.getId());
assertNull(retrieved);
}
/**
* @see DATAES-113
*/
@Test
public void returnOneFromCustomImpl() {
assertThat(personRepository.returnOne(), is(1));
}
}
@@ -0,0 +1,27 @@
/*
* 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.repositories.cdi;
import org.springframework.data.elasticsearch.entities.Person;
import org.springframework.data.repository.Repository;
/**
* @author Mark Paluch
* @see DATAES-113
*/
public interface SamplePersonRepository extends Repository<Person, Long>, SamplePersonRepositoryCustom {
}
@@ -0,0 +1,26 @@
/*
* 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.repositories.cdi;
/**
* @see DATAES-113
* @author Mark Paluch
*/
interface SamplePersonRepositoryCustom {
int returnOne();
}
@@ -0,0 +1,29 @@
/*
* 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.repositories.cdi;
/**
* @see DATAES-113
* @author Mark Paluch
*/
class SamplePersonRepositoryImpl implements SamplePersonRepositoryCustom {
@Override
public int returnOne() {
return 1;
}
}
@@ -16,16 +16,23 @@
package org.springframework.data.elasticsearch.repositories.custom;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
*/
public interface SampleCustomMethodRepository extends ElasticsearchRepository<SampleEntity, String> {
@@ -57,11 +64,69 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
Page<SampleEntity> findByIdIn(List<String> ids, Pageable pageable);
Page<SampleEntity> findByIdNotIn(List<String> messages, Pageable pageable);
Page<SampleEntity> findByIdNotIn(List<String> ids, Pageable pageable);
Page<SampleEntity> findByAvailableTrue(Pageable pageable);
Page<SampleEntity> findByAvailableFalse(Pageable pageable);
Page<SampleEntity> findByMessageOrderByTypeAsc(String message, Pageable pageable);
Page<SampleEntity> findByLocation(GeoPoint point, Pageable pageable);
Page<SampleEntity> findByLocationAndMessage(GeoPoint point, String msg, Pageable pageable);
Page<SampleEntity> findByLocationWithin(GeoPoint point, String distance, Pageable pageable);
Page<SampleEntity> findByLocationWithin(Point point, Distance distance, Pageable pageable);
Page<SampleEntity> findByLocationNear(GeoBox box, Pageable pageable);
Page<SampleEntity> findByLocationNear(Box box, Pageable pageable);
Page<SampleEntity> findByLocationNear(Point point, Distance distance, Pageable pageable);
Page<SampleEntity> findByLocationNear(GeoPoint point, String distance, Pageable pageable);
Stream<SampleEntity> findByType(String type);
long countByType(String type);
long countByTypeNot(String type);
long countByAvailable(boolean available);
long countByRateLessThan(int rate);
long countByRateBefore(int rate);
long countByRateAfter(int rate);
long countByMessageLike(String message);
long countByMessageStartingWith(String message);
long countByMessageEndingWith(String message);
long countByMessageContaining(String message);
long countByIdIn(List<String> ids);
long countByIdNotIn(List<String> ids);
long countByAvailableTrue();
long countByAvailableFalse();
long countByLocationWithin(GeoPoint point, String distance);
long countByLocationWithin(Point point, Distance distance);
long countByLocationNear(GeoBox box);
long countByLocationNear(Box box);
long countByLocationNear(Point point, Distance distance);
long countByLocationNear(GeoPoint point, String distance);
}
@@ -0,0 +1,43 @@
package org.springframework.data.elasticsearch.repositories.query;
import java.util.List;
import org.springframework.data.elasticsearch.entities.Product;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* Created by akonczak on 04/09/15.
*/
public interface ProductRepository extends PagingAndSortingRepository<Product, String> {
public List<Product> findByNameAndText(String name, String text);
public List<Product> findByNameAndPrice(String name, Float price);
public List<Product> findByNameOrText(String name, String text);
public List<Product> findByNameOrPrice(String name, Float price);
public List<Product> findByAvailableTrue();
public List<Product> findByAvailableFalse();
public List<Product> findByPriceIn(List<Float> floats);
public List<Product> findByPriceNotIn(List<Float> floats);
public List<Product> findByPriceNot(float v);
public List<Product> findByPriceBetween(float v, float v1);
public List<Product> findByPriceLessThan(float v);
public List<Product> findByPriceLessThanEqual(float v);
public List<Product> findByPriceGreaterThan(float v);
public List<Product> findByPriceGreaterThanEqual(float v);
public List<Product> findByIdNotIn(List<String> strings);
}
@@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.repositories.sample;
import java.util.List;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
@@ -24,4 +26,9 @@ import org.springframework.data.elasticsearch.repository.ElasticsearchRepository
*/
public interface SampleElasticsearchRepository extends ElasticsearchRepository<SampleEntity, String> {
long deleteById(String id);
List<SampleEntity> deleteByAvailable(boolean available);
List<SampleEntity> deleteByMessage(String message);
void deleteByType(String type);
}
@@ -121,7 +121,7 @@ public class DynamicSettingAndMappingEntityRepositoryTests {
assertThat(mapping, is(notNullValue()));
assertThat(properties, is(notNullValue()));
assertThat(((String) ((Map) properties.get("email")).get("type")), is("string"));
assertThat((String) ((Map)properties.get("email")).get("analyzer"), is("emailAnalyzer"));
assertThat((String) ((Map) properties.get("email")).get("analyzer"), is("emailAnalyzer"));
}
@Test
@@ -146,7 +146,7 @@ public class DynamicSettingAndMappingEntityRepositoryTests {
assertThat(mapping, is(notNullValue()));
assertThat(properties, is(notNullValue()));
assertThat(((String) ((Map) properties.get("email")).get("type")), is("string"));
assertThat((String) ((Map)properties.get("email")).get("analyzer"), is("emailAnalyzer"));
assertThat((String) ((Map) properties.get("email")).get("analyzer"), is("emailAnalyzer"));
}
/*
@@ -162,6 +162,6 @@ public class DynamicSettingAndMappingEntityRepositoryTests {
assertThat(mapping, is(notNullValue()));
assertThat(properties, is(notNullValue()));
assertThat(((String) ((Map) properties.get("email")).get("type")), is("string"));
assertThat((String) ((Map)properties.get("email")).get("analyzer"), is("emailAnalyzer"));
assertThat((String) ((Map) properties.get("email")).get("analyzer"), is("emailAnalyzer"));
}
}
@@ -0,0 +1,29 @@
/*
* 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.repositories.spel;
import org.springframework.data.elasticsearch.entities.SpELEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* SpELRepository
*
* @author Artur Konczak
*
*/
public interface SpELRepository extends ElasticsearchRepository<SpELEntity, String> {
}
@@ -0,0 +1,28 @@
/*
* 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.repositories.synonym;
import org.springframework.data.elasticsearch.entities.SynonymEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
/**
* SynonymRepository
*
* @author Artur Konczak
*/
public interface SynonymRepository extends ElasticsearchCrudRepository<SynonymEntity, String> {
}
@@ -0,0 +1,77 @@
/*
* 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.repositories.synonym;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.entities.SynonymEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* SynonymRepositoryTests
*
* @author Artur Konczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:synonym-test.xml")
public class SynonymRepositoryTests {
@Autowired
private SynonymRepository repository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Before
public void before() {
elasticsearchTemplate.deleteIndex(SynonymEntity.class);
elasticsearchTemplate.createIndex(SynonymEntity.class);
elasticsearchTemplate.putMapping(SynonymEntity.class);
elasticsearchTemplate.refresh(SynonymEntity.class, true);
}
@Test
public void shouldDo() {
//given
SynonymEntity entry1 = new SynonymEntity();
entry1.setText("Elizabeth is the English queen");
SynonymEntity entry2 = new SynonymEntity();
entry2.setText("Other text");
repository.save(entry1);
repository.save(entry2);
//when
//then
assertThat(repository.count(),is(2L));
List<SynonymEntity> synonymEntities = elasticsearchTemplate.queryForList(new NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("text", "british")).build(), SynonymEntity.class);
assertThat(synonymEntities.size(), is(1));
}
}
@@ -0,0 +1,58 @@
/*
* 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.repository.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import static org.mockito.Mockito.doReturn;
/**
* @author Florian Hopf
*/
@RunWith(MockitoJUnitRunner.class)
public class ElasticsearchEntityInformationCreatorImplTest {
@Mock
private MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
@Mock
private ElasticsearchPersistentEntity<String> persistentEntity;
private ElasticsearchEntityInformationCreatorImpl entityInfoCreator;
@Before
public void before() {
entityInfoCreator = new ElasticsearchEntityInformationCreatorImpl(mappingContext);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowIllegalArgumentExceptionOnMissingEntity() {
entityInfoCreator.getEntityInformation(String.class);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowIllegalArgumentExceptionOnMissingIdAnnotation() {
doReturn(persistentEntity).when(mappingContext).getPersistentEntity(String.class);
doReturn(String.class).when(persistentEntity).getType();
doReturn(null).when(persistentEntity).getIdProperty();
entityInfoCreator.getEntityInformation(String.class);
}
}
@@ -0,0 +1,129 @@
package org.springframework.data.elasticsearch.repository.support;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.entities.Product;
import org.springframework.data.elasticsearch.repositories.query.ProductRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Artur Konczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/repository-query-support.xml")
public class QueryKeywordsTest {
@Autowired
private ProductRepository repository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Before
public void before() {
elasticsearchTemplate.deleteIndex(Product.class);
elasticsearchTemplate.createIndex(Product.class);
elasticsearchTemplate.putMapping(Product.class);
elasticsearchTemplate.refresh(Product.class, true);
repository.save(Arrays.asList(
Product.builder().id("1").name("Sugar").text("Cane sugar").price(1.0f).available(false).build()
, Product.builder().id("2").name("Sugar").text("Cane sugar").price(1.2f).available(true).build()
, Product.builder().id("3").name("Sugar").text("Beet sugar").price(1.1f).available(true).build()
, Product.builder().id("4").name("Salt").text("Rock salt").price(1.9f).available(true).build()
, Product.builder().id("5").name("Salt").text("Sea salt").price(2.1f).available(false).build()));
elasticsearchTemplate.refresh(Product.class, true);
}
@Test
public void shouldSupportAND() {
//given
//when
//then
assertThat(repository.findByNameAndText("Sugar", "Cane sugar").size(), is(2));
assertThat(repository.findByNameAndPrice("Sugar", 1.1f).size(), is(1));
}
@Test
public void shouldSupportOR() {
//given
//when
//then
assertThat(repository.findByNameOrPrice("Sugar", 1.9f).size(), is(4));
assertThat(repository.findByNameOrText("Salt", "Beet sugar").size(), is(3));
}
@Test
public void shouldSupportTrueAndFalse() {
//given
//when
//then
assertThat(repository.findByAvailableTrue().size(), is(3));
assertThat(repository.findByAvailableFalse().size(), is(2));
}
@Test
public void shouldSupportInAndNotInAndNot() {
//given
//when
//then
assertThat(repository.findByPriceIn(Arrays.asList(1.2f, 1.1f)).size(), is(2));
assertThat(repository.findByPriceNotIn(Arrays.asList(1.2f, 1.1f)).size(), is(3));
assertThat(repository.findByPriceNot(1.2f).size(), is(4));
}
/*
DATAES-171
*/
@Test
public void shouldWorkWithNotIn() {
//given
//when
//then
assertThat(repository.findByIdNotIn(Arrays.asList("2", "3")).size(), is(3));
}
@Test
public void shouldSupportBetween() {
//given
//when
//then
assertThat(repository.findByPriceBetween(1.0f, 2.0f).size(), is(4));
}
@Test
public void shouldSupportLessThanAndGreaterThan() {
//given
//when
//then
assertThat(repository.findByPriceLessThan(1.1f).size(), is(1));
assertThat(repository.findByPriceLessThanEqual(1.1f).size(), is(2));
assertThat(repository.findByPriceGreaterThan(1.9f).size(), is(1));
assertThat(repository.findByPriceGreaterThanEqual(1.9f).size(), is(2));
}
}
@@ -299,6 +299,118 @@ public class SimpleElasticsearchRepositoryTests {
assertThat(sampleEntities.getTotalElements(), equalTo(0L));
}
@Test
public void shouldDeleteById() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(documentId);
sampleEntity.setMessage("hello world.");
sampleEntity.setVersion(System.currentTimeMillis());
repository.save(sampleEntity);
// when
long result = repository.deleteById(documentId);
// then
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("id", documentId)).build();
Page<SampleEntity> sampleEntities = repository.search(searchQuery);
assertThat(sampleEntities.getTotalElements(), equalTo(0L));
assertThat(result, equalTo(1L));
}
@Test
public void shouldDeleteByMessageAndReturnList() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity1 = new SampleEntity();
sampleEntity1.setId(documentId);
sampleEntity1.setMessage("hello world 1");
sampleEntity1.setAvailable(true);
sampleEntity1.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setMessage("hello world 2");
sampleEntity2.setAvailable(true);
sampleEntity2.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setId(documentId);
sampleEntity3.setMessage("hello world 3");
sampleEntity3.setAvailable(false);
sampleEntity3.setVersion(System.currentTimeMillis());
repository.save(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3));
// when
List<SampleEntity> result = repository.deleteByAvailable(true);
// then
assertThat(result.size(), equalTo(2));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
Page<SampleEntity> sampleEntities = repository.search(searchQuery);
assertThat(sampleEntities.getTotalElements(), equalTo(1L));
}
@Test
public void shouldDeleteByListForMessage() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity1 = new SampleEntity();
sampleEntity1.setId(documentId);
sampleEntity1.setMessage("hello world 1");
sampleEntity1.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setMessage("hello world 2");
sampleEntity2.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setId(documentId);
sampleEntity3.setMessage("hello world 3");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.save(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3));
// when
List<SampleEntity> result = repository.deleteByMessage("hello world 3");
// then
assertThat(result.size(), equalTo(1));
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
Page<SampleEntity> sampleEntities = repository.search(searchQuery);
assertThat(sampleEntities.getTotalElements(), equalTo(2L));
}
@Test
public void shouldDeleteByType() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity1 = new SampleEntity();
sampleEntity1.setId(documentId);
sampleEntity1.setType("book");
sampleEntity1.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setId(documentId);
sampleEntity2.setType("article");
sampleEntity2.setVersion(System.currentTimeMillis());
documentId = randomNumeric(5);
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setId(documentId);
sampleEntity3.setType("image");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.save(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3));
// when
repository.deleteByType("article");
// then
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
Page<SampleEntity> sampleEntities = repository.search(searchQuery);
assertThat(sampleEntities.getTotalElements(), equalTo(2L));
}
@Test
public void shouldDeleteEntity() {
// given
@@ -1,7 +1,10 @@
{
"test-setting-type" : {
"properties" : {
"email" : {"type" : "string", "analyzer" : "emailAnalyzer" }
"test-setting-type": {
"properties": {
"email": {
"type": "string",
"analyzer": "emailAnalyzer"
}
}
}
}
@@ -0,0 +1,18 @@
<?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/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<import resource="infrastructure.xml"/>
<bean name="elasticsearchTemplate"
class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
<constructor-arg name="client" ref="client"/>
</bean>
<elasticsearch:repositories base-package="org.springframework.data.elasticsearch.repositories.query"/>
</beans>
@@ -0,0 +1,19 @@
<?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/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<import resource="infrastructure.xml"/>
<bean name="elasticsearchTemplate"
class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
<constructor-arg name="client" ref="client"/>
</bean>
<elasticsearch:repositories
base-package="org.springframework.data.elasticsearch.repositories.spel"/>
</beans>

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