1
0
mirror of synced 2026-09-05 07:29:38 +00:00

Compare commits

..

85 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
68 changed files with 2869 additions and 369 deletions
+5 -5
View File
@@ -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>
@@ -53,9 +53,9 @@ the appropriate dependency version.
```
###Note:
Spring data elaticsearch 1.0.0.RELEASE version is on elasticsearch 1.1.1 library which uses java 1.6 or later version.
As latest version of elasticsearch 1.2.x is now supporting java 1.7, future releases of spring data elasticsearch library can be used with Java 7 or later version.
In order to use with java 6 it has to be with elasticsearch 1.1.x.
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.
+17 -10
View File
@@ -4,12 +4,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>1.1.2.RELEASE</version>
<version>1.3.2.RELEASE</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.5.2.RELEASE</version>
<version>1.7.2.RELEASE</version>
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
</parent>
@@ -23,8 +23,8 @@
<commonscollections>3.2.1</commonscollections>
<commonslang>2.6</commonslang>
<elasticsearch>1.3.2</elasticsearch>
<springdata.commons>1.9.2.RELEASE</springdata.commons>
<elasticsearch>1.5.2</elasticsearch>
<springdata.commons>1.11.2.RELEASE</springdata.commons>
</properties>
@@ -121,6 +121,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -133,10 +140,10 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
@@ -151,14 +158,14 @@
<repositories>
<repository>
<id>spring-libs-release</id>
<url>http://repo.spring.io/libs-release</url>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>http://repo.spring.io/plugins-release</url>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
+3 -2
View File
@@ -4,9 +4,9 @@ BioMed Central Development Team
:revdate: {localdate}
:toc:
:toc-placement!:
:spring-data-commons-docs: https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
(C) 2013-2014 The original author(s).
(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.
@@ -32,4 +32,5 @@ include::reference/elasticsearch-misc.adoc[]
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
@@ -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
}
@@ -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,15 +15,16 @@
*/
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
@@ -209,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}
*
@@ -323,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
*
@@ -333,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
*
@@ -424,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
*
@@ -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;
@@ -51,6 +52,8 @@ 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;
@@ -70,6 +73,7 @@ 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;
@@ -89,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;
/**
@@ -106,6 +111,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private ResultsMapper resultsMapper;
private String searchTimeout;
public ElasticsearchTemplate(Client client) {
this(client, null, null);
@@ -130,6 +136,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
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);
@@ -280,7 +290,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
if (query.getFilter() != null) {
request.setPostFilter(query.getFilter());
}
SearchResponse response = request.execute().actionGet();
SearchResponse response = getSearchResponse(request.execute());
return extractIds(response);
}
@@ -302,9 +312,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
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());
}
@@ -315,10 +328,85 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
@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());
@@ -438,6 +526,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
@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");
@@ -445,16 +537,24 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
Assert.notNull(query.getId(), "No Id 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)
.setDoc(query.getUpdateRequest().doc())
if (query.getUpdateRequest().script() == null) {
// doc
if (query.DoUpsert()) {
updateRequestBuilder.setDocAsUpsert(true)
.setDoc(query.getUpdateRequest().doc());
} else {
updateRequestBuilder.setDoc(query.getUpdateRequest().doc());
}
} else {
// or script
updateRequestBuilder
.setScript(query.getUpdateRequest().script(), query.getUpdateRequest().scriptType())
.setScriptParams(query.getUpdateRequest().scriptParams())
.setScriptLang(query.getUpdateRequest().scriptLang());
} else {
updateRequestBuilder.setDoc(query.getUpdateRequest().doc());
}
return updateRequestBuilder.execute().actionGet();
return updateRequestBuilder;
}
@Override
@@ -477,6 +577,26 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
}
@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
);
}
}
@Override
public <T> boolean indexExists(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName());
@@ -542,42 +662,80 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
.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);
}
@@ -640,7 +798,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
requestBuilder.setBoostTerms(query.getBoostTerms());
}
SearchResponse response = requestBuilder.execute().actionGet();
SearchResponse response = getSearchResponse(requestBuilder.execute());
return resultsMapper.mapResults(response, clazz, query.getPageable());
}
@@ -676,8 +834,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
searchRequest.addAggregation(aggregationBuilder);
}
}
return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()).execute());
}
return searchRequest.setQuery(searchQuery.getQuery()).execute().actionGet();
private SearchResponse getSearchResponse(ListenableActionFuture<SearchResponse> response) {
return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout);
}
private <T> boolean createIndexIfNotCreated(Class<T> clazz) {
@@ -737,12 +898,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
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);
}
@@ -904,6 +1060,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
}
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()};
@@ -976,4 +1141,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
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, singleField.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())
@@ -159,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())) {
@@ -289,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;
}
}
@@ -19,5 +19,6 @@ 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;
}
}
@@ -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;
}
}
@@ -95,7 +95,8 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
@Override
public String getIndexType() {
return indexType;
Expression expression = parser.parseExpression(indexType, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
@Override
@@ -35,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 = " ";
@@ -71,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;
}
@@ -304,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;
}
@@ -315,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;
}
@@ -326,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));
}
/**
@@ -346,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}
*
@@ -522,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 {
@@ -542,5 +588,13 @@ public class Criteria {
public Object getValue() {
return value;
}
@Override
public String toString() {
return "CriteriaEntry{" +
"key=" + key +
", value=" + value +
'}';
}
}
}
@@ -46,10 +46,10 @@ public class ElasticsearchRepositoryBean<T> extends CdiRepositoryBean<T> {
* @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}.
* {@link CustomRepositoryImplementationDetector}, can be {@literal null}.
*/
public ElasticsearchRepositoryBean(Bean<ElasticsearchOperations> operations, Set<Annotation> qualifiers,
Class<T> repositoryType, BeanManager beanManager, CustomRepositoryImplementationDetector detector) {
Class<T> repositoryType, BeanManager beanManager, CustomRepositoryImplementationDetector detector) {
super(qualifiers, repositoryType, beanManager, detector);
Assert.notNull(operations, "Cannot create repository with 'null' for ElasticsearchOperations.");
@@ -67,7 +67,7 @@ public class ElasticsearchRepositoryExtension extends CdiRepositoryExtensionSupp
}
private <T> CdiRepositoryBean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers,
BeanManager beanManager) {
BeanManager beanManager) {
Bean<ElasticsearchOperations> elasticsearchOperationsBean = this.elasticsearchOperationsMap.get(qualifiers
.toString());
@@ -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,6 +23,9 @@ 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
@@ -45,13 +49,28 @@ 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());
@@ -59,6 +78,26 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
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();
}
@@ -41,6 +41,7 @@ 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> {
@@ -61,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));
}
@@ -72,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));
}
@@ -89,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();
@@ -99,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:
@@ -112,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:
@@ -125,10 +128,23 @@ 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 = 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);
@@ -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) {
@@ -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) {
@@ -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);
+80
View File
@@ -1,6 +1,86 @@
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.
+1 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 1.1.2
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").
@@ -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()));
@@ -34,19 +34,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* SpELEntityTest
*
* @author Artur Konczak
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/spel-repository-test.xml")
public class SpELEntityTest {
public class SpELEntityTests {
@Autowired private SpELRepository repository;
@Autowired
private SpELRepository repository;
@Autowired
private ElasticsearchTemplate template;
@Autowired private ElasticsearchTemplate template;
@Before
public void init() {
@@ -55,15 +51,31 @@ public class SpELEntityTest {
@Test
public void shouldDo() {
//Given
// Given
repository.save(new SpELEntity());
repository.save(new SpELEntity());
//When
// When
//Then
// 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);
}
}
@@ -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,9 +42,19 @@ 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
@@ -254,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
@@ -523,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
@@ -589,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
@@ -615,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
@@ -665,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
@@ -709,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++) {
@@ -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,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");
}
}
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -22,13 +22,12 @@ import org.springframework.data.elasticsearch.annotations.Document;
* SpELEntity
*
* @author Artur Konczak
*
*/
@Document(indexName = "#{'abz'+'-'+'entity'}", type = "spel", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
@Document(indexName = "#{'abz'+'-'+'entity'}", type = "#{'my'+'Type'}", indexStoreType = "memory", shards = 1,
replicas = 0, refreshInterval = "-1")
public class SpELEntity {
@Id
private String id;
@Id private String id;
public String getId() {
return 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;
@@ -120,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);
@@ -495,6 +497,56 @@ public class CustomMethodRepositoryTests {
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
@@ -591,6 +643,22 @@ public class CustomMethodRepositoryTests {
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
*/
@@ -685,7 +753,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);
@@ -1125,5 +1193,17 @@ public class CustomMethodRepositoryTests {
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;
}
}
@@ -16,6 +16,7 @@
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;
@@ -71,6 +72,10 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
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);
@@ -83,6 +88,8 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
Page<SampleEntity> findByLocationNear(GeoPoint point, String distance, Pageable pageable);
Stream<SampleEntity> findByType(String type);
long countByType(String type);
long countByTypeNot(String type);
@@ -122,5 +129,4 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
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);
}
@@ -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,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
@@ -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>
+18
View File
@@ -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.synonym"/>
</beans>
+13
View File
@@ -0,0 +1,13 @@
{
"synonym-type": {
"_all": {
"enabled": true
},
"properties": {
"text": {
"type": "string",
"analyzer": "synonym_analyzer"
}
}
}
}
@@ -0,0 +1,23 @@
{
"index": {
"number_of_shards": "1",
"number_of_replicas": "0",
"analysis": {
"analyzer": {
"synonym_analyzer": {
"tokenizer": "whitespace",
"filter": [
"synonym_filter"
]
}
},
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms_path": "synonyms.txt",
"ignore_case": "true"
}
}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"index": {
"number_of_shards": "1",
"number_of_replicas": "0",
"analysis": {
"analyzer": {
"synonym_analyzer": {
"tokenizer": "whitespace",
"filter": [
"synonym_filter"
]
}
},
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms": [
"british,english",
"queen,monarch"
],
"ignore_case": "true"
}
}
}
}
}
+3
View File
@@ -0,0 +1,3 @@
british,english
queen,monarch
+2 -2
View File
@@ -11,8 +11,8 @@ Import-Template:
org.apache.commons.collections.*;version="${commonscollections:[=.=.=,+1.0.0)}";resolution:=optional,
org.apache.commons.lang.*;version="${commonslang:[=.=.=,+1.0.0)}",
com.fasterxml.jackson.*;version="${jackson:[=.=.=,+1.0.0)}";resolution:=optional,
org.elasticsearch.*;version="${elasticsearch:[=.=.=,+1.2.1)}",
org.apache.lucene.*;version="4.8.1",
org.elasticsearch.*;version="${elasticsearch:[=.=.=,+1.4.4)}",
org.apache.lucene.*;version="4.10.4",
org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}",
org.slf4j.*;version="${slf4j:[=.=.=,+1.0.0)}",
org.springframework.*;version="${spring:[=.=.=.=,+1.0.0)}",