Spring Frameworkを用いたElasticsearch連携実装手順

Spring FrameworkアプリケーションにElasticsearchを統合する方法について、設定から実装までを解説します。以下の手順で全文検索機能を実装できます。

Maven依存関係の設定

まず、必要なライブラリをpom.xmlに追加します。Spring Data ElasticsearchとElasticsearch本体、および関連ライブラリを定義します。


<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>com.example</groupId>
    <artifactId>spring-es-integration</artifactId>
    <version>1.0.0</version>

    <properties>
        11
        7.17.0
        4.4.0
    </properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>${spring-data-elasticsearch.version}</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        
        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.3</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.17.2</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
        
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.20</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

Spring設定ファイル

次に、Springの設定ファイルを作成します。Elasticsearchクライアントとリポジトリ、テンプレートを定義します。


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/data/elasticsearch
        https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">

    
    
    
    
    
    
    
    <bean id="esTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        
    </bean>

ドキュメントモデルクラス

Elasticsearchに保存するドキュメントを表すクラスを作成します。アノテーションでマッピング情報を定義します。

package com.example.search.model;

import lombok.Data;
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;

@Data
@Document(indexName = "blog_index", type = "_doc", createIndex = false)
public class BlogDocument {
    @Id
    @Field(type = FieldType.Long, store = true)
    private Long identifier;
    
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
    private String subject;
    
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
    private String body;
}

リポジトリインターフェース

Spring Dataのリポジトリインターフェースを定義します。命名規則に従ったメソッドで検索処理を実装できます。

package com.example.search.repository;

import com.example.search.model.BlogDocument;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;

public interface BlogDocumentRepository extends ElasticsearchRepository {
    List<BlogDocument> findBySubject(String keyword);
    
    List<BlogDocument> findBySubjectOrBody(String subjectKeyword, String bodyKeyword);
    
    List<BlogDocument> findBySubjectOrBody(String subjectKeyword, String bodyKeyword, Pageable pageable);
}

テスト実装例

実際の動作を確認するためのテストクラスです。インデックス操作、ドキュメントのCRUD、検索処理を実装しています。

package com.example.search.test;

import com.example.search.model.BlogDocument;
import com.example.search.repository.BlogDocumentRepository;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class SearchIntegrationTest {
    
    @Autowired
    private BlogDocumentRepository documentRepository;
    
    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Test
    public void initializeIndex() {
        Map indexSettings = new HashMap<>();
        Map config = new HashMap<>();
        config.put("number_of_shards", "3");
        config.put("number_of_replicas", "1");
        indexSettings.put("index", config);
        
        esTemplate.createIndex(BlogDocument.class, indexSettings);
        esTemplate.putMapping(BlogDocument.class);
    }

    @Test
    public void persistDocument() {
        BlogDocument doc = new BlogDocument();
        doc.setIdentifier(100L);
        doc.setSubject("Spring Data Elasticsearch入門");
        doc.setBody("ElasticsearchはLuceneをベースにした分散型検索エンジンです。RESTful APIを提供します。");
        documentRepository.save(doc);
    }

    @Test
    public void removeDocument() {
        documentRepository.deleteById(100L);
    }

    @Test
    public void fetchAllDocuments() {
        Iterable<BlogDocument> results = documentRepository.findAll();
        results.forEach(System.out::println);
    }

    @Test
    public void fetchByIdentifier() {
        Optional<BlogDocument> result = documentRepository.findById(200L);
        result.ifPresent(System.out::println);
    }

    @Test
    public void searchBySubjectKeyword() {
        List<BlogDocument> documents = documentRepository.findBySubject("Elasticsearch");
        documents.forEach(System.out::println);
    }

    @Test
    public void nativeQueryExample() {
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("Springフレームワーク").defaultField("subject"))
                .withPageable(PageRequest.of(0, 5))
                .build();
        
        List<BlogDocument> results = esTemplate.queryForList(
            searchQuery, 
            BlogDocument.class, 
            IndexCoordinates.of("blog_index")
        );
        results.forEach(System.out::println);
    }

    @Test
    public void pagedSearchExample() {
        Pageable pageSpec = PageRequest.of(0, 5);
        List<BlogDocument> results = documentRepository.findBySubjectOrBody(
            "Spring", "検索", pageSpec
        );
        results.forEach(System.out::println);
    }
}

クエリメソッド命名規則

Spring Data Elasticsearchのリポジトリインターフェースでは、メソッド名を特定の規則に従って命名することで、実装なしに検索処理を自動生成できます。主な規則は以下の通りです。

  • findBy[fieldName] - 指定フィールドの完全一致検索
  • findBy[field1]Or[field2] - 複数フィールドのOR条件検索
  • findBy[field1]And[field2] - 複数フィールドのAND条件検索
  • findBy[field]Like - 部分一致検索
  • findBy[field]StartsWith - 前方一致検索
  • findBy[field]EndsWith - 後方一致検索
  • findBy[field]Containing - 含む検索

ページングを行う場合は、最後の引数にPageableオブジェクトを指定します。ソートや大文字小文字の区別など、より詳細な指定も可能です。

タグ: Spring Data Elasticsearch Elasticsearch Java 全文検索 Maven

8月4日 19:19 投稿