저번 포스팅까지 elasticsearch 를 사용해 봤다.
이번에는 Spring boot로 elasticsearch를 사용해보자

spring-data-elasticsearch를 사용할 예정이고 ElasticsearchRepository와 ElasticsearchTemplate 두개를 사용해서 해볼 것이다.

ElasticsearchRepository

@Document(indexName = "account", type = "account", shards = 1, replicas = 0, refreshInterval = "-1")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {

  @Id
  private String id;

  private String name;

  private String email;

  public Account(String name, String email) {
    this.name = name;
    this.email = email;
  }
}

type(table)과 매핑되는 엔티티(?)들이다. @Document에 index명과 type명을 지정해 줄수 있다. 여기서는 account와 account로 지정해놨다 조금 이상하게 지정해 놨다. 아무튼

public interface AccountRepository extends ElasticsearchRepository<Account, Long> {

  List<Account> findByname(String name);

  Account findByemail(String email);
}

그리고 spring data 프로젝트인 만큼 ElasticsearchRepository 상속받아서 사용했다. 이것은 우리가 흔히 쓰던 JPA구조와 동일하다. 참 간편하고 확장성있게 잘 만든 듯하다.

각각을 테스트를 해보자

 @Test
  public void saveTest() {
    accountRepository.save(new Account("wid", "wid.com"));
  }

  @Test
  public void findAllTest() {
    accountRepository.findAll().forEach(System.out::println);
  }

  @Test
  public void findOneTest(){
    List<Account> wonwoo = accountRepository.findByname("wonwoo");
    System.out.println(wonwoo);
  }

잘들어가고 잘 나온다. 우리가 흔히 썼기 때문에 넘어가자

ElasticsearchTemplate

다음은 ElasticsearchTemplate을 활용해 보자.
스프링부트를 사용하면 ElasticsearchTemplate 는 자동으로 빈으로 생성되기 때문에 빈설정을 하지 않아도 된다.

  @Autowired
  private ElasticsearchTemplate elasticsearchTemplate;

  @Test
  public void saveTemplateTest() {
    Account account = new Account("kk", "kk@test.com");
    IndexQuery indexQuery = new IndexQuery();
    indexQuery.setObject(account);
    String index = elasticsearchTemplate.index(indexQuery);
    System.out.println(index);
  }

  @Test
  public void updateTemplateTest() {
    IndexRequest indexRequest = new IndexRequest();
    indexRequest.source("name", "wowowowo");
    UpdateQuery updateQuery = new UpdateQueryBuilder().withId("AVSAOJvzW3yAedZaYx2w")
      .withClass(Account.class).withIndexRequest(indexRequest).build();
    UpdateResponse update = elasticsearchTemplate.update(updateQuery);
    System.out.println(update);
  }

  @Test
  public void deleteTemplateTest() {
    String delete = elasticsearchTemplate.delete(Account.class, "AVSEzpSE4HLWkOEZq-DT");
    System.out.println(delete);
  }

간단하게 R 을 제외하고 CUD과정이다.
딱히 설명은 필요 없는 듯하다. 설명하지 않아도 알 듯싶으니.. 엘라스틱서치는 검색이 훨씬 방대하기 때문에 이거 또한 그냥 넘어가자.

검색을 보자. 다 설명하지 못하지만 필자가 테스트 해본것은 최대한 많이 설명 해볼 것이다.

  @Test
  public void getAccount() {
    GetQuery getQuery = new GetQuery();
    getQuery.setId("AVSAOJvzW3yAedZaYx2w");
    Account account = elasticsearchTemplate.queryForObject(getQuery, Account.class);
    System.out.println(account);
  }

위는 Id 기준으로 검색을 하는 쿼리이다. ID는 엘라스틱 서치에서 만들어주는 아이디다. 물론 지정해 줄수도 있다.

  @Test
  public void getAccountTemplateIndexAndTypeTest() {
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
      .withQuery(matchAllQuery())
      .withIndices("account")
      .withTypes("account")
      .build();
    List<Account> accounts = elasticsearchTemplate.queryForList(searchQuery, Account.class);
    System.out.println(accounts);
  }

위의 코드는 index는 account type도 account에서 모든 데이터를 가져오는 쿼리이다.

  @Test
  public void getAccountTemplateCountTest() {
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
      .withQuery(matchAllQuery())
      .withIndices("account")
      .withTypes("account")
      .build();
    long count = elasticsearchTemplate.count(searchQuery, Account.class);
    System.out.println(count);
  }

위의 코드는 count를 세는 코드이다 쿼리는 이 전과 동일하다.

  @Test
  public void getAccountTemplateIndexAndTypeFieldsTest() {
    SearchQuery searchQuery = new NativeSearchQueryBuilder()
      .withQuery(matchAllQuery())
      .withIndices("account")
      .withTypes("account")
      .withFields("name")
      .build();
    List<Account> accounts = elasticsearchTemplate.queryForList(searchQuery, Account.class);
    System.out.println(accounts);
  }

위의 코드는 이전과 동일하지만 필드를 name만 가져오는 쿼리이다. 실제 해보면 email은 null이 출력된다.

  @Test
  public void getAccountTemplatePage() {
    SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices("account")
      .withTypes("account").withQuery(matchAllQuery())
      .withSort(new FieldSortBuilder("name").order(SortOrder.DESC))
      .withPageable(new PageRequest(0, 5)).build();
    Page<Account> accounts = elasticsearchTemplate.queryForPage(searchQuery, Account.class);

    String str = accounts.getContent().stream()
      .map(i -> i.toString())
      .collect(joining("\n"));
    System.out.println(str);
    System.out.println(accounts.getContent().size());
  }

엘라스틱 서치의 페이징을 할 수 있는 쿼리다. 페이지는 0페이지이고 5개만 출력하며 name 기준으로 desc한 쿼리이다.

  @Test
  public void getAccountTemplateSearchTest() {

    SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("name", "wonwoo")).build();
    List<Account> accounts = elasticsearchTemplate.queryForList(searchQuery, Account.class);

    String str = accounts.stream()
      .map(i -> i.toString())
      .collect(joining("\n"));
    System.out.println(str);
    System.out.println(accounts.size());
  }

이번에는 name 필드에 wonwoo라는 단어가 있으면 모두 출력한다. wonwoo123이라는 단어가 있다면 출력 되지 않는다.
만약 wonwoo 123 이면 출력된다.

  @Test
  public void getAccountTemplateSearchWildcardQueryTest() {

    SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(wildcardQuery("name", "*wonwoo*")).build();

    List<Account> accounts = elasticsearchTemplate.queryForList(searchQuery, Account.class);
    String str = accounts.stream()
      .map(i -> i.toString())
      .collect(joining("\n"));
    System.out.println(str);
    System.out.println(accounts.size());
  }

이번에는 와일드카드쿼리를 사용한 것이다. 만약 이번에는 wonwoo123이 있다면 출력된다.

  @Test
  public void getAccountTemplateSearchQueryStringQueryTest() {

    SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery("*wonwoo*")).build();

    List<Account> accounts = elasticsearchTemplate.queryForList(searchQuery, Account.class);
    String str = accounts.stream()
      .map(i -> i.toString())
      .collect(joining("\n"));
    System.out.println(str);
    System.out.println(accounts.size());
  }

이번 쿼리는 name email 어떤 필드건 상관없이 wonwoo라는 문자가 있으면 모두 출력한다. 단어와 상관없이 문자가 있으면 모두 출력된다.

이것으로 조금 엘라스틱서치에 대해 알것도 같다. 하지만 이거 또한 세발에 피인듯 싶다. 완전히 사용하려면 아직 멀었다.
일단 해당 소스는 github에 올려놔야 겠다.