Merge pull request #351 from zinch84/spring_data_elasticsearch
Initial code for Spring Data Elasticsearch article
This commit is contained in:
commit
a4b3920547
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>spring-data-elasticsearch</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.springframework.ide.eclipse.core.springbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.springframework.ide.eclipse.core.springnature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,12 @@
|
|||
## Spring Data Elasticsearch
|
||||
|
||||
### Build the Project with Tests Running
|
||||
```
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
### Run Tests Directly
|
||||
```
|
||||
mvn test
|
||||
```
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.baeldung</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>spring-data-elasticsearch</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<org.springframework.data.version>1.3.2.RELEASE</org.springframework.data.version>
|
||||
<org.springframework.version>4.2.2.RELEASE</org.springframework.version>
|
||||
<junit.version>4.11</junit.version>
|
||||
<org.slf4j.version>1.7.12</org.slf4j.version>
|
||||
<logback.version>1.1.3</logback.version>
|
||||
<elasticsearch.version>1.3.2.RELEASE</elasticsearch.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>${elasticsearch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,53 @@
|
|||
package com.baeldung.config;
|
||||
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.node.NodeBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
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.repository.config.EnableElasticsearchRepositories;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = "com.baeldung.repository")
|
||||
@ComponentScan(basePackages = {"com.baeldung.service"})
|
||||
public class Config {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(Config.class);
|
||||
|
||||
@Bean
|
||||
public NodeBuilder nodeBuilder() {
|
||||
return new NodeBuilder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() {
|
||||
|
||||
try {
|
||||
Path tmpDir = Files.createTempDirectory(Paths.get(System.getProperty("java.io.tmpdir")), "elasticsearch_data");
|
||||
|
||||
ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
|
||||
.put("http.enabled", "false")
|
||||
.put("path.data", tmpDir.toAbsolutePath().toString());
|
||||
|
||||
logger.debug(tmpDir.toAbsolutePath().toString());
|
||||
|
||||
return new ElasticsearchTemplate(nodeBuilder()
|
||||
.local(true)
|
||||
.settings(elasticsearchSettings.build())
|
||||
.node()
|
||||
.client());
|
||||
} catch (IOException ioex) {
|
||||
logger.error("Cannot create temp dir", ioex);
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.baeldung.dao;
|
||||
|
||||
import com.baeldung.model.Article;
|
||||
import com.baeldung.model.Article;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
|
||||
public interface ArticleRepository extends ElasticsearchRepository<Article, String> {
|
||||
|
||||
Page<Article> findByAuthorsName(String name, Pageable pageable);
|
||||
|
||||
@Query("{\"bool\": {\"must\": [{\"match\": {\"authors.name\": \"?0\"}}]}}")
|
||||
Page<Article> findByAuthorsNameUsingCustomQuery(String name, Pageable pageable);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.baeldung.model;
|
||||
|
||||
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.FieldIndex;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Document(indexName = "blog", type = "article")
|
||||
public class Article {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
@Field(type = FieldType.String, index = FieldIndex.not_analyzed)
|
||||
private String title;
|
||||
@Field(type = FieldType.Nested)
|
||||
private List<Author> authors;
|
||||
|
||||
public Article() {
|
||||
}
|
||||
|
||||
public Article(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public List<Author> getAuthors() {
|
||||
return authors;
|
||||
}
|
||||
|
||||
public void setAuthors(List<Author> authors) {
|
||||
this.authors = authors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Article{" +
|
||||
"id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", authors=" + authors +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.baeldung.model;
|
||||
|
||||
public class Author {
|
||||
|
||||
private String name;
|
||||
|
||||
public Author() {
|
||||
}
|
||||
|
||||
public Author(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Author{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.repository;
|
||||
|
||||
import com.baeldung.model.Article;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
|
||||
public interface ArticleRepository extends ElasticsearchRepository<Article, String> {
|
||||
|
||||
Page<Article> findByAuthorsName(String name, Pageable pageable);
|
||||
|
||||
@Query("{\"bool\": {\"must\": [{\"match\": {\"authors.name\": \"?0\"}}]}}")
|
||||
Page<Article> findByAuthorsNameUsingCustomQuery(String name, Pageable pageable);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.service;
|
||||
|
||||
import com.baeldung.model.Article;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
public interface ArticleService {
|
||||
Article save(Article article);
|
||||
Article findOne(String id);
|
||||
Iterable<Article> findAll();
|
||||
Page<Article> findByAuthorName(String name, Pageable pageable);
|
||||
Page<Article> findByAuthorNameUsingCustomQuery(String name, Pageable pageable);
|
||||
long count();
|
||||
void delete(Article article);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.baeldung.service;
|
||||
|
||||
import com.baeldung.repository.ArticleRepository;
|
||||
import com.baeldung.model.Article;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ArticleServiceImpl implements ArticleService {
|
||||
|
||||
private ArticleRepository articleRepository;
|
||||
|
||||
@Autowired
|
||||
public void setArticleRepository(ArticleRepository articleRepository) {
|
||||
this.articleRepository = articleRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Article save(Article article) {
|
||||
return articleRepository.save(article);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Article findOne(String id) {
|
||||
return articleRepository.findOne(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Article> findAll() {
|
||||
return articleRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Article> findByAuthorName(String name, Pageable pageable) {
|
||||
return articleRepository.findByAuthorsName(name, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Article> findByAuthorNameUsingCustomQuery(String name, Pageable pageable) {
|
||||
return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return articleRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Article article) {
|
||||
articleRepository.delete(article);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>elasticsearch - %date [%thread] %-5level %logger{36} - %message%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="com.baeldung.config" level="DEBUG" />
|
||||
|
||||
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
|
||||
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -0,0 +1,130 @@
|
|||
package com.baeldung;
|
||||
|
||||
import com.baeldung.config.Config;
|
||||
import com.baeldung.model.Article;
|
||||
import com.baeldung.model.Author;
|
||||
import com.baeldung.service.ArticleService;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.elasticsearch.index.query.FilterBuilders.regexpFilter;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = {Config.class}, loader = AnnotationConfigContextLoader.class)
|
||||
public class ElasticSearchTest {
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
@Autowired
|
||||
private ArticleService articleService;
|
||||
|
||||
private final Author johnSmith = new Author("John Smith");
|
||||
private final Author johnDoe = new Author("John Doe");
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
elasticsearchTemplate.deleteIndex(Article.class);
|
||||
elasticsearchTemplate.createIndex(Article.class);
|
||||
|
||||
Article article = new Article("Spring Data Elasticsearch");
|
||||
article.setAuthors(asList(johnSmith, johnDoe));
|
||||
articleService.save(article);
|
||||
|
||||
article = new Article("Search engines");
|
||||
article.setAuthors(asList(johnDoe));
|
||||
articleService.save(article);
|
||||
|
||||
article = new Article("Second Article About Elasticsearch");
|
||||
article.setAuthors(asList(johnSmith));
|
||||
articleService.save(article);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenArticleService_whenSaveArticle_thenIdIsAssigned() {
|
||||
List<Author> authors = asList(
|
||||
new Author("John Smith"), johnDoe);
|
||||
|
||||
Article article = new Article("Making Search Elastic");
|
||||
article.setAuthors(authors);
|
||||
|
||||
article = articleService.save(article);
|
||||
assertNotNull(article.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPersistedArticles_whenSearchByAuthorsName_thenRightFound() {
|
||||
|
||||
Page<Article> articleByAuthorName = articleService.findByAuthorName(johnSmith.getName(), new PageRequest(0, 10));
|
||||
assertEquals(2L, articleByAuthorName.getTotalElements());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() {
|
||||
|
||||
Page<Article> articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10));
|
||||
assertEquals(3L, articleByAuthorName.getTotalElements());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() {
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withFilter(regexpFilter("title", ".*data.*"))
|
||||
.build();
|
||||
List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
||||
|
||||
assertEquals(1, articles.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSavedDoc_whenTitleUpdated_thenCouldFindByUpdatedTitle() {
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(fuzzyQuery("title", "serch"))
|
||||
.build();
|
||||
List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
||||
|
||||
assertEquals(1, articles.size());
|
||||
|
||||
Article article = articles.get(0);
|
||||
final String newTitle = "Getting started with Search Engines";
|
||||
article.setTitle(newTitle);
|
||||
articleService.save(article);
|
||||
|
||||
assertEquals(newTitle, articleService.findOne(article.getId()).getTitle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSavedDoc_whenDelete_thenRemovedFromIndex() {
|
||||
|
||||
final String articleTitle = "Spring Data Elasticsearch";
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%"))
|
||||
.build();
|
||||
List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
||||
assertEquals(1, articles.size());
|
||||
final long count = articleService.count();
|
||||
|
||||
articleService.delete(articles.get(0));
|
||||
|
||||
assertEquals(count - 1, articleService.count());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue