From f9aa1556d8f8404c30673856c7314a3e4e6ba89e Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Fri, 31 Oct 2014 00:20:18 +0000 Subject: [PATCH] #28 - Added example for Spring Data Elasticsearch. Added basic example to show repository and ElasticsearchOperations usage. --- README.md | 4 + elasticsearch/example/.gitignore | 13 ++ elasticsearch/example/README.md | 14 ++ elasticsearch/example/pom.xml | 19 +++ .../ApplicationConfiguration.java | 63 +++++++++ .../springdata/elasticsearch/Conference.java | 57 ++++++++ .../elasticsearch/ConferenceRepository.java | 24 ++++ .../src/main/resources/application.properties | 3 + .../ElasticsearchOperationsTest.java | 131 ++++++++++++++++++ elasticsearch/pom.xml | 29 ++++ pom.xml | 1 + 11 files changed, 358 insertions(+) create mode 100644 elasticsearch/example/.gitignore create mode 100644 elasticsearch/example/README.md create mode 100644 elasticsearch/example/pom.xml create mode 100644 elasticsearch/example/src/main/java/example/springdata/elasticsearch/ApplicationConfiguration.java create mode 100644 elasticsearch/example/src/main/java/example/springdata/elasticsearch/Conference.java create mode 100644 elasticsearch/example/src/main/java/example/springdata/elasticsearch/ConferenceRepository.java create mode 100644 elasticsearch/example/src/main/resources/application.properties create mode 100644 elasticsearch/example/src/test/java/example/springdata/elasticsearch/ElasticsearchOperationsTest.java create mode 100644 elasticsearch/pom.xml diff --git a/README.md b/README.md index b7068db6..73d94d03 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ We have separate folders for the samples of individual modules: * `example` - Example for basic Sprign Data Redis setup. * `cluster-sentinel` - Example for Redis cluster and Sentinel support. +## Spring Data Elasticsearch + +* `example` - Example how to use basic text search, geo-spatial search and facets. + ## Miscellaneous * `multi-store` - Example project to use both Spring Data MongoDB and Spring Data JPA in one project. \ No newline at end of file diff --git a/elasticsearch/example/.gitignore b/elasticsearch/example/.gitignore new file mode 100644 index 00000000..e50bef9c --- /dev/null +++ b/elasticsearch/example/.gitignore @@ -0,0 +1,13 @@ +.project +.classpath +.springBeans +.settings/ +target/ + +#IntelliJ Stuff +.idea +*.iml + +##ignore local node data files for unit tests +/data +/.DS_Store diff --git a/elasticsearch/example/README.md b/elasticsearch/example/README.md new file mode 100644 index 00000000..01b4c6ff --- /dev/null +++ b/elasticsearch/example/README.md @@ -0,0 +1,14 @@ +# Spring Data Elasticsearch - Examples + +Requirements: + + * [Maven](http://maven.apache.org/download.cgi) - required. + * [Elasticsearch](http://www.elasticsearch.org/overview/elkdownloads) - optional. + + +To use local elasticsearch cluster: + +* install elasticsearch +* uncomment both properties in file 'application.properties' + + \ No newline at end of file diff --git a/elasticsearch/example/pom.xml b/elasticsearch/example/pom.xml new file mode 100644 index 00000000..accbd48d --- /dev/null +++ b/elasticsearch/example/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + org.springframework + spring-data-elasticsearch-example + + Spring Data Elasticsearch - Example + Sample projects for Spring Data Elasticsearch + https://github.com/spring-projects/spring-data-elasticsearch + + + org.springframework.data.examples + spring-data-elasticsearch-examples + 1.0.0.BUILD-SNAPSHOT + + + diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ApplicationConfiguration.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ApplicationConfiguration.java new file mode 100644 index 00000000..656d3264 --- /dev/null +++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ApplicationConfiguration.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014-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 example.springdata.elasticsearch; + +import java.util.Arrays; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; + +/** + * @author Artur Konczak + * @author Oliver Gierke + */ +@Configuration +@EnableAutoConfiguration +class ApplicationConfiguration { + + @Autowired ElasticsearchOperations operations; + @Autowired ConferenceRepository repository; + + @PreDestroy + public void deleteIndex() { + operations.deleteIndex(Conference.class); + } + + @PostConstruct + public void insertDataSample() { + + // Remove all documents + repository.deleteAll(); + operations.refresh(Conference.class, true); + + // Save data sample + repository.save(Conference.builder().date("2014-11-06").name("Spring eXchange 2014 - London") + .keywords(Arrays.asList("java", "spring")).location("51.500152, -0.126236").build()); + repository.save(Conference.builder().date("2014-12-07").name("Scala eXchange 2014 - London") + .keywords(Arrays.asList("scala", "play", "java")).location("51.500152, -0.126236").build()); + repository.save(Conference.builder().date("2014-11-20").name("Elasticsearch 2014 - Berlin") + .keywords(Arrays.asList("java", "elasticsearch", "kibana")).location("52.5234051, 13.4113999").build()); + repository.save(Conference.builder().date("2014-11-12").name("AWS London 2014") + .keywords(Arrays.asList("cloud", "aws")).location("51.500152, -0.126236").build()); + repository.save(Conference.builder().date("2014-10-04").name("JDD14 - Cracow") + .keywords(Arrays.asList("java", "spring")).location("50.0646501, 19.9449799").build()); + } +} diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/Conference.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/Conference.java new file mode 100644 index 00000000..cc443fcb --- /dev/null +++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/Conference.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014-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 example.springdata.elasticsearch; + +import static org.springframework.data.elasticsearch.annotations.FieldType.*; + +import java.util.List; + +import lombok.Data; +import lombok.experimental.Builder; + +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.GeoPointField; + +/** + * @author Artur Konczak + * @author Oliver Gierke + */ +@Data +@Builder +@Document(indexName = "conference-index", shards = 1, replicas = 0, indexStoreType = "memory", refreshInterval = "-1") +public class Conference { + + private @Id String id; + private String name; + private @Field(type = Date) String date; + private @GeoPointField String location; + private List keywords; + + // do not remove it + public Conference() {} + + // do not remove it - work around for lombok generated constructor for all params + public Conference(String id, String name, String date, String location, List keywords) { + + this.id = id; + this.name = name; + this.date = date; + this.location = location; + this.keywords = keywords; + } +} diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ConferenceRepository.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ConferenceRepository.java new file mode 100644 index 00000000..4a56910d --- /dev/null +++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/ConferenceRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014-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 example.springdata.elasticsearch; + +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +/** + * @author Artur Konczak + * @author Oliver Gierke + */ +interface ConferenceRepository extends ElasticsearchRepository {} diff --git a/elasticsearch/example/src/main/resources/application.properties b/elasticsearch/example/src/main/resources/application.properties new file mode 100644 index 00000000..1982f98a --- /dev/null +++ b/elasticsearch/example/src/main/resources/application.properties @@ -0,0 +1,3 @@ +# Uncomment both entries to connect with local elasticsearch cluster +#spring.data.elasticsearch.clusterName=elasticsearch +#spring.data.elasticsearch.clusterNodes=localhost:9300 diff --git a/elasticsearch/example/src/test/java/example/springdata/elasticsearch/ElasticsearchOperationsTest.java b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/ElasticsearchOperationsTest.java new file mode 100644 index 00000000..8a01a5c0 --- /dev/null +++ b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/ElasticsearchOperationsTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2014-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 example.springdata.elasticsearch; + +import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.FacetedPage; +import org.springframework.data.elasticsearch.core.facet.request.HistogramFacetRequestBuilder; +import org.springframework.data.elasticsearch.core.facet.request.TermFacetRequestBuilder; +import org.springframework.data.elasticsearch.core.facet.result.HistogramResult; +import org.springframework.data.elasticsearch.core.facet.result.TermResult; +import org.springframework.data.elasticsearch.core.query.Criteria; +import org.springframework.data.elasticsearch.core.query.CriteriaQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Test case to show Spring Data Elasticsearch functionality. + * + * @author Artur Konczak + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class ElasticsearchOperationsTest { + + private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + @Autowired ElasticsearchOperations operations; + + @Test + public void textSearch() throws ParseException { + + String expectedDate = "2014-10-29"; + String expectedWord = "java"; + CriteriaQuery query = new CriteriaQuery(new Criteria("_all").contains(expectedWord).and( + new Criteria("date").greaterThanEqual(expectedDate))); + + List result = operations.queryForList(query, Conference.class); + + assertThat(result, hasSize(3)); + + for (Conference conference : result) { + assertThat(conference.getKeywords(), hasItem(expectedWord)); + assertThat(format.parse(conference.getDate()), greaterThan(format.parse(expectedDate))); + } + } + + @Test + public void geoSpatialSearch() { + + String startLocation = "50.0646501,19.9449799"; + String range = "330mi"; // or 530km + CriteriaQuery query = new CriteriaQuery(new Criteria("location").within(startLocation, range)); + + List result = operations.queryForList(query, Conference.class); + + assertThat(result, hasSize(2)); + } + + @Test + public void termFacet() { + + String termField = "keywords"; + String facetName = "all-keywords"; + + FacetedPage firstPage = operations.queryForPage(// + new NativeSearchQueryBuilder().// + withQuery(matchAllQuery()).// + withFacet(new TermFacetRequestBuilder(facetName).// + allTerms().// + descCount().// + fields(termField).build()).// + build(), Conference.class); + + TermResult facet = (TermResult) firstPage.getFacet(facetName); + + assertThat(facet.getTerms(), hasSize(8)); + + facet.getTerms().forEach( + term -> { + assertThat(term.getTerm(), + isOneOf("java", "spring", "scala", "play", "elasticsearch", "kibana", "cloud", "aws")); + }); + } + + @Test + public void histogramFacetOnDate() { + + String termField = "date"; + int interval = 30; + String facetName = "by-date"; + + FacetedPage firstPage = operations.queryForPage(new NativeSearchQueryBuilder() // + .withQuery(matchAllQuery()) // + .withFacet(new HistogramFacetRequestBuilder(facetName).// + timeUnit(TimeUnit.DAYS).// + interval(interval). // + field(termField).build()).// + build(), Conference.class); + + HistogramResult facet = (HistogramResult) firstPage.getFacet(facetName); + + assertThat(facet.getIntervalUnit(), hasSize(3)); + } +} diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml new file mode 100644 index 00000000..942fbaea --- /dev/null +++ b/elasticsearch/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + spring-data-elasticsearch-examples + pom + + + org.springframework.data.examples + spring-data-examples + 1.0.0.BUILD-SNAPSHOT + + + Spring Data Elasticsearch - Examples + Sample projects for Spring Data Elasticsearch + https://github.com/spring-projects/spring-data-elasticsearch + + + example + + + + + org.springframework.boot + spring-boot-starter-data-elasticsearch + + + + diff --git a/pom.xml b/pom.xml index bf722499..de7c9529 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ redis solr cassandra + elasticsearch