반응형

[상황]

 - rdb를 이용해서 고객관련된 데이터가 구축되어 있음

 - legcy 시스템에서 약 20년 동안 운영하면서 주소 데이터가 엉망이됨

  ( 신주소, 구주소, 회사에서 사용하는 배송을위한 행정구역주소: xxx동 xxx아파트)

 - 주소검색의 필요성  : 간 혹 연락처 맵핑이 안되는 고객이 있음-

     > 조회를 위해서 주소검색을 시도해야 함,

       동일주소에 중복 주문이 발생 할 수 있음 ( 내가 주문하고,  부모님이 주문하고) <-- 중복주문

  

 

@fulltext searching 검색 엔진도입을 고민하게됨

 

[내가 생각했던 검색엔진 후보군]

   1. mariadb에 10.x버전 부터 추가된 plugin mgroonga 

   2. elasticsearch

 

mgroonga 

 - aws에서 free tier 를 이용해서 mariadb서비스를 생성후 테스트 진행 mgroonga plugin지원 안함

 - centos 설치하고 직접 mariadb 설치해서 간단하게 설치 후 테스트

 - 한글 검색 지원 

 - table type -> mgroonga 생성되고  외래키 사용할 수 없음

 - limitation : [참고]mroonga.org/docs/reference/limitations.html#limitations-of-table 

 

elasticsearch

 - 6.4 버전  전 까지만 해도 한글공식지원안됨

 - 7.x 버전 설치해서 테스트 nori elasticsearch에서 공식지원됨

 - uri 방식 or rest 방식으로 curd가능

 - spring data jpa와 연계하여 쉽게 구현 가능

 

 

@ mgroonga vs elasticsearch 사실 비교할 여지가 없음

elasticsearch를 선택한 이유

 - inverted index(역 색인) (매력적임)

 - nori : 한글형태소 분석기 

 - rest api  : 별도로 curd를 구현할 필요 없음 , 연동하기 편함

 - 주소검색 뿐만아니라 다양하게 이용할 가능성이 있음.

 - kibana : visualiztion에 특화된 도구

 등등등 개발자가 해야할 일들이 줄어듬

 

[해결시나리오]

 - 고객의 주소가 변경되면 -> elasicsearch 보내는 scheduler 개발(하루에 2번 정도 실행)

 - elasticsearch로 주소 컬럼에서 검색 후 , 고객코드 리턴

 - 리턴받은 고객코드로 실서비스 rdb에서 조회

 - 주소 컬럼에 데이터를 넣을 떄 주소정보, 건물명(아파트명), 행정구역 , 광역시,시,군,구,동,리 포함

 - 주소 컬럼에  건물명 ngram 2자, 3자 만들어서 추가 , (역 색인용)

   ex) 화정동 힐스테이트 -> 화정 힐스, 화정 힐스테 검색되기위해서

   보통 xxx동 xxx아파트 형태로 검색을 함

 

 이미 개발은 완료 되서 실서비스 하고 있음. 해보고 iessu사항이 있다면 정리해서 공유하도록 하겠습니다.

 

반응형
블로그 이미지

visualp

c#, java

,
반응형

@Document 를 지정후 

interface를 이용해서 쿼리를 하던 중

기본검색은 jpa사용하듯 하면되지만

match를 이용하여 모두 조건이 일치할 떄 데이터가 조회해야되는 이슈가 발생

ex) 화정동 현대, 화정동 힐스, 화정 힐스

모두 만족해야되는 상황이 발생

jpa는 match를 이용하면 elasticsearch에서는 기본 or조건이 걸림

 

[순서]

1- elasticsearch에서 Dev tools를 이용하여 쿼리 작성

GET idx_es_address/_search 
{
  "query": {
     "match": {
        "address": { 
          "query": "화정동 힐스",
          "operator": "and",
          "analyzer": "korean"
        }
      }
  }
}

2 - 작성된 쿼리를 query 부분을 제외함

{
  "match": {
      "title": {
        "query": "화정동 힐스",
        "operator": "and"
      }
    }
}

3 - 제외된 query를 

  - codebeautify.org/jsonminifier 가서 minify시킴

{"match":{"title":{"query":"화정동 힐스","operator":"and"}}}

 

4 -"화정동 힐스" -> ?0 로 변경 <-- 0번째 parameter라는 의미

{"match":{"title":{"query":"?0","operator":"and"}}}

 

5. interface작성<최종>

 @Query("{\"match\":{\"address\":{\"query\":\"?0\",\"operator\":\"and\"}}}")
 List<EsAddressVO> findAllByAddressMatches(String addr, Pageable pageable);

 

반드시 Pageable param을 넣어주는것을 권장

기본 pagesize = 10으로 세팅되어 있음

반응형
블로그 이미지

visualp

c#, java

,
반응형

elasticsearch jpa연동하여 작업하던중 findAll 사용시 pageable 지정해주지 않고 사용하면 

기본  페이지가 10으로 세팅됨 ,

form 0 , default page size 10 , 

데이터를 한번에 가져와야 할 상황 ~ 1000정도로 세팅함

[참고]

www.elastic.co/guide/en/elasticsearch/reference/6.8/search-request-from-size.html

 

GET /_search
{
    "from" : 0, "size" : 10,
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

Pageable pageable = PageRequest.of(0,1000);

esAddressRepo.findAllByOaddrMatches(addr,pageable);

이런식으로 사용함.

반응형
블로그 이미지

visualp

c#, java

,
반응형

@ 십질 끝에 정리 

 - Setting or Mapping 은 최초 index (table) 생성시 적용이 됩니다.

 - 중간에 끼어 넣기 안됨 , 최초 생성하면서 설정 해줘야함 .

 - spring data @Document 객체에 @Setting, @Mapping annotation을 통해서 간단하게 연결 할 수 있습니다.

 - SettingMapping 에는 "setting" or "mapping" 노드를 포함하지 않는다. <-- 포함시 세팅 안됨

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(indexName = "idx_es_address", createIndex = true)
@Setting(settingPath = "/elasticsearch/settings/settings.json")
@Mapping(mappingPath = "/elasticsearch/mappings/mappings.json")
public class EsAddressVO {

    //고객코드
    @Id
    private String cuscode;

    private String areacode;

    //신주소
    private String naddr;

    //구주소
    private String oaddr;
    
}

@Setting /resources/elasticsearch/settings/settings.json

 - tokenizer와 analyzer 세팅함

 

{
  "analysis": {
    "tokenizer": {
      "nori_none": {
        "type": "nori_tokenizer",
        "decompound_mode": "none"
      },
      "nori_discard": {
        "type": "nori_tokenizer",
        "decompound_mode": "discard"
      },
      "nori_mixed": {
        "type": "nori_tokenizer",
        "decompound_mode": "mixed"
      }
    },
    "analyzer": {
      "korean": {
        "type": "nori",
        "stopwords": "_korean_"
      }
    }
  }
}

위 tokenizer 에 대한 자세한 부분들은 기술문서 참고 할것

 

 

@Mapping /resources/elasticsearch/mappings/mappings.json

기본적으로 spring에서 mapping을 지정하지 않더라도 기본 mapping을 해주지만

nori 한글 검색을 사용하기 위해서 oaddr 필드에 setting.jsoin에서 등록해둔 korean <-- analyzer를  지정

{
    "properties": {
      "oaddr": {
        "type": "text",
        "analyzer": "korean"
      }
    }
}

 

kibana console에서 확인하는 방법

 

 

GET idx_es_addres/_settings

{
  "idx_es_address" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "1",
        "provided_name" : "idx_es_address",
        "creation_date" : "1606194598534",
        "analysis" : {
          "analyzer" : {
            "korean" : {
              "type" : "nori",
              "stopwords" : "_korean_"
            }
          },
          "tokenizer" : {
            "nori_discard" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "discard"
            },
            "nori_mixed" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "mixed"
            },
            "nori_none" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "none"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "mxGr5zJOTrW5Gp9-n4HjHQ",
        "version" : {
          "created" : "7100099"
        }
      }
    }
  }
}

 

GET idx_es_address/_mapping

{
  "idx_es_address" : {
    "mappings" : {
      "properties" : {
        "_class" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "areacode" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "cuscode" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "naddr" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "oaddr" : {
          "type" : "text",
          "analyzer" : "korean"
        }
      }
    }
  }
}

 

반응형
블로그 이미지

visualp

c#, java

,
반응형

@ 테스트 후 정리 

 - Setting or Mapping 은 최초 index (table) 생성시 적용이 됩니다.

 - 중간에 끼어 넣기 안됨 , 최초 생성하면서 설정 해줘야함 .

 - spring data @Document 객체에 @Setting, @Mapping annotation을 통해서 간단하게 연결 할 수 있습니다.

 - SettingMapping 에는 "setting" or "mapping" 노드를 포함하지 않는다. <-- 포함시 세팅 안됨

 

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(indexName = "idx_es_address", createIndex = true)
@Setting(settingPath = "/elasticsearch/settings/settings.json")
@Mapping(mappingPath = "/elasticsearch/mappings/mappings.json")
public class EsAddressVO {

    //고객코드
    @Id
    private String cuscode;

    private String areacode;

    //신주소
    private String naddr;

    //구주소
    private String oaddr;
    
}

@Setting /resources/elasticsearch/settings/settings.json

 - tokenizer와 analyzer 세팅함

 

{
  "analysis": {
    "tokenizer": {
      "nori_none": {
        "type": "nori_tokenizer",
        "decompound_mode": "none"
      },
      "nori_discard": {
        "type": "nori_tokenizer",
        "decompound_mode": "discard"
      },
      "nori_mixed": {
        "type": "nori_tokenizer",
        "decompound_mode": "mixed"
      }
    },
    "analyzer": {
      "korean": {
        "type": "nori",
        "stopwords": "_korean_"
      }
    }
  }
}

위 tokenizer 에 대한 자세한 부분들은 기술문서 참고 할것

 

 

@Mapping /resources/elasticsearch/mappings/mappings.json

기본적으로 spring에서 mapping을 지정하지 않더라도 기본 mapping을 해주지만

nori 한글 검색을 사용하기 위해서 oaddr 필드에 setting.jsoin에서 등록해둔 korean <-- analyzer를  지정

{
    "properties": {
      "oaddr": {
        "type": "text",
        "analyzer": "korean"
      }
    }
}

 

kibana console에서 확인하는 방법

 

 

GET idx_es_addres/_settings

{
  "idx_es_address" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "1",
        "provided_name" : "idx_es_address",
        "creation_date" : "1606194598534",
        "analysis" : {
          "analyzer" : {
            "korean" : {
              "type" : "nori",
              "stopwords" : "_korean_"
            }
          },
          "tokenizer" : {
            "nori_discard" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "discard"
            },
            "nori_mixed" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "mixed"
            },
            "nori_none" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "none"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "mxGr5zJOTrW5Gp9-n4HjHQ",
        "version" : {
          "created" : "7100099"
        }
      }
    }
  }
}

 

GET idx_es_address/_mapping

{
  "idx_es_address" : {
    "mappings" : {
      "properties" : {
        "_class" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "areacode" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "cuscode" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "naddr" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "oaddr" : {
          "type" : "text",
          "analyzer" : "korean"
        }
      }
    }
  }
}
반응형
블로그 이미지

visualp

c#, java

,
반응형

application.properties 에

elasticsearch.host=192.168.0.50
elasticsearch.cluster_name=master <-- node name
elasticsearch.port=9200 <-- port 기본 9200
elasticsearch.user_name=elastic  <-- username
elasticsearch.user_password=설정한 비밀번호

 

추가 한다.

 

AbstractElasticsearchConfiguration 확장해서 연결 설정 할 수 있음.

 - RestHighLevelClient <-- 사용하는 것을 6.4 이상부터 권장 하고 있음

 

@Configuration
@EnableElasticsearchRepositories(basePackages = {"com.fourfree.elasticsearch"})
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {
    @Value("${elasticsearch.host}")
    private String host;

    @Value("${elasticsearch.port}")
    private String port;
    
    @Value("${elasticsearch.cluster_name}")
    private String clusterName;

    @Value("${elasticsearch.user_name}")
    private String userName;

    @Value("${elasticsearch.user_password}")
    private String userPassword;


    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo(host+":" + port)
                .withBasicAuth(userName,userPassword)
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
    
    @Bean
    public ElasticsearchOperations elasticsearchOperations(){
        return new ElasticsearchRestTemplate(elasticsearchClient());
    }

}
반응형
블로그 이미지

visualp

c#, java

,
반응형

[참조] docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#core.extensions

       

spring data release train

spring data elasticsearch

Elasticsearch

Spring boot

2020.0.0[1]

4.1.x[1]

7.9.3

2.3.x[1]

Neumann

4.0.x

7.6.2

2.3.x

Moore

3.2.x

6.8.12

2.2.x

Lovelace

3.1.x

6.2.2

2.1.x

Kay[2]

3.0.x[2]

5.5.0

2.0.x[2]

Ingalls[2]

2.1.x[2]

2.4.0

1.5.x[2]

spring boot 기반에서 세팅하려고 함으로 2.3.x 버전을 설치 해야함

pom.xml<--  추가

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
	<version>2.3.2.RELEASE</version>
</dependency>

 

 

반응형
블로그 이미지

visualp

c#, java

,
반응형

7.x 버전 부터 비밀번호 설정 할 수 있도록 xpack 에추가 되었다고 함.

vi /etc/elasticsearch/elasticsearchelasticsearch.yml

아래 2줄 추가

xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

 

비밀번호 변경을 위해서 아래 명령어 실행

 

/user/share/elasticsearch/bin/elasticsearch-setup-passwords  interactive 

 

패스워드 설정 하는 화면이 나오는데 Y/N <-- Y 선택 후 패스워드 설정함.

 

Enternter password for [elastic]:
Reenter password for [elastic]:
Enter password for [apm_system]:
Reenter password for [apm_system]:
Enter password for [kibana_system]:
Reenter password for [kibana_system]:
Enter password for [logstash_system]:
Reenter password for [logstash_system]:
Enter password for [beats_system]:
Reenter password for [beats_system]:
Enter password for [remote_monitoring_user]:
Reenter password for [remote_monitoring_user]:

 

위 순서로 패스워드가 설정됨

설정후 elasticsearch 재시작

elasicsearch ip:9200 접속하면 로그인 하라고 하는데

elastic / 설정한 비밀번호 입력하면됩

 

다음은 키바나 설정방법

vi /etc/kibana/kibana.yml

elasticsearch.username: "elastic"
elasticsearch.password: "password" <-- 설정한 비밀번호

 

kibana 재시작 -> 후 접속

아이피:5601

접속하면 로그인 아이디 비밀번호 form 보임

역시 elasticsearch와 동일하게 

elastic/비밀번호 입력해줌

 

반응형
블로그 이미지

visualp

c#, java

,