엘라스틱서치는 인덱스에 저장된 문서를 검색할 수 있도록 다양한 검색기능을 제공한다. 문서는 색인시 설정한 Analyzer에 의해 분석과정을 거쳐 토큰으로 분리되는데, 이러한 Analyzer는 색인 시점 말고도 검색 시점에도 이용된다. 특정 문장이 검색어로 요청되면 분석기를 통해 분석된 토큰의 일치 여부를 판단하여 그 결과에 Score을 매긴다. 이러한 엘라스틱서치에서는 다양한 검색 조건을 주기위하여 Query DSL이라는 특수한 쿼리 문법을 제공한다.

 

1. 검색 API

문장은 색인 시점에 텀으로 분리된다. 검색 시에는 이 텀을 일치시켜야 검색이 가능하다. 엘라스틱서치는 루씬기반이기 때문에 색인 시점에 Analyzer를 통해 분석된 텀을 Term, 출현빈도, 문서번화와 같이 역색인 구조로 만들어 내부적으로 저장한다. 검색 시점에는 Keyword 데이터 타입과 같은 분석이 되지 않는 데이터와 Text 데이터 타입과 같은 분석이 가능한 데이터를 구분해서 분석이 가능할 경우 분석기를 이용해 분석을 수행한다. 이를 통해 검색 시점에도 형태소분석이 되든 되지 않든 텀을 얻을 수 있으며, 해당 텀으로 색인 시점에 생긴 역색인 테이블을 뒤져 문서를 찾고 스코어를 계산하여 결과로 제공한다.

 

1.1. 검색 질의 방식

엘라스틱서치에서 제공하는 검색 API는 기본적으로 Query기반으로 동작한다. 검색 질의에는 검색하고자 하는 각종 Query 조건을 명시할 수 있으며, 동일한 조건을 두 가지 방식으로 표현 가능하다.

 

  1. URI 검색(루씬 스타일)
  2. Request Body 검색

첫번째로 우선 URI방식을 살펴본다. 

 

1.1.1. URI 검색 방식

URI를 이용하는 방식은 HTTP GET 요청을 활용한다. 즉, 쿼리파라미터로 Query를 작성하는 것이다. 간단한 쿼리는 쉽지만 쿼리파라미터의 표현한계로 인해 복잡한 질의는 불가능하다.

 

예시) GET http://localhost:9200/인덱스명/_search?q=필드명:검색값

 

1.1.2. Request Body 검색 방식

Request Body 방식은 HTTP 요청 시 Body에 검색할 칼럼과 검색어를 미리 정의된 JSON 구조로 표현하여 질의하는 방식이다. JSON 구조의 표현을 조금더 효율적으로 하기 위해 엘라스틱서치는 Query DSL이라는 미리 정의된 특별한 문법을 지원한다. URI 검색방식보다 더욱 풍부한 조건의 질의가 가능하다.

 

1
2
3
4
5
6
7
8
9
예시) POST http://localhost:9200/인덱스명/_search
     Header Content-Type:application/json
Request Body {
                "query":{
                   "term":{
                      "typeNm":"장편"
                   }
                }
             }
cs

 

그러면 각 검색 표현법의 장단점은 무엇일까?

 

1.2.1. URI 검색 방식

URI 검색은 Request Body 검색에 비해 단순하고 사용하기 편하지만 복잡한 질의문을 입력하기 힘들다는 치명적인 단점이 있다. 또한 URI 검색을 이용할 경우에는 엘라스틱서치에서 제공하는 모든 검색 API 옵션을 사용할 수 없다. 이유는 쿼리파라미터 표현법에는 표현상 한계가 있기 때문이다. 하지만 간단한 쿼리 혹은 테스트에서는 간편함을 제공하기도 함으로 URI 검색 방식의 주요 파라미터를 알아보자.

 

파라미터 기본값 설명
q - 검색을 수행할 쿼리 문자열 조건을 지정.
df - 쿼리에 검색을 수행할 필드가 지정되지 않았을 경우 기본값으로 검색할 필드를 지정한다.
analyzer 검색 대상 필드에 설정된 형태소 분석기(색인 시점 분석기) 쿼리 문자열을 형태소 분석할때 사용할 분석기 지정
analyze_wildcard false 접두어/와일드카드 검색 활성화 여부 지정
default_operator OR 두 개 이상의 검색 조건이 q에 포함된 경우 검색 조건 연산자를 설정한다.
_source true 검색 결과에 문서 본문 포함 여부를 지정
sort - 정렬 기준 필드 지정
from - 검색 시작 위치 지정
size - 반환 결과 개수 지정

 

q 옵션에는 기본적으로 '필드명:검색어' 형태로 입력할 수 있으며, 여러개의 필드를 검색 할때는 공백을 입력한 후에 추가적인 필드명과 검색어를 입력한다. URI 검색 방식의 q 옵션은 Request Body 검색에서 제공하는 Query String Search 옵션과 동일하게 동작한다.

 

예시) http://localhost:9200/인덱스명/_search?q=필드명1:필드값1 AND 필드명2:필드값2&

        analyze_wildcard=true&from=0&size=5&sort=_score:desc,필드명:asc&

       _source_includes=필드명,필드명,필드명,필드명

 

 

1.2.2. Request Body 검색 방식

위의 URI 검색방식 예시를 그대로 Request Body 검색 방식으로 바꾸어보겠다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST http://localhost:9200/인덱스명/_search
Header Content-Type : application/json
<Request Body>
{
    "query":{
        "query_string":{
            "default_field":"필드명"
            ,"query":"필드명1:필드값1 AND 필드명2:필드값2"
        }
    }
    ,"from":0
    ,"size":5
    ,"sort":[{
        "_score":{
            "order":"desc"
        }
        ,"필드명":{
            "order":"asc"
        }
    }]
    ,"_source":[
        "필드명"
        ,"필드명"
        ,"필드명"
        ,"필드명"
    ]
}
cs

 

간단한 검색 조건인 경우에는 URI 검색방식이 편해보일 수도 있지만, 구조화된 JSON 구조를 파악하면 더욱 깔끔한 질의작성이 가능하다.

 

 

2. Request Body 검색 방식 - Query DSL 

엘라스틱서치로 검색 질의를 요청할때는 Request Body 방식과 URI 방식 모두 _search API를 이용한다. 하지만 Query DSL 검색을 이용하면 여러개의 질의를 조합하거나 질의 결과에 대해 또 검색을 수행하는 등의 기존 URI 방식 보다 강력한 검색이 가능하다. 엘라스틱서치의 Query DSL은 구조화된 JSON 형태를 사용한다.

 

2.1. Query DSL 구조

 

<요청>

1
2
3
4
5
6
7
8
9
10
{
    "size" : "-->반환받는 결과 개수"
    ,"from" : "-->몇번째 문서부터 가져올지 지정. 기본값은 0이다."
    ,"timeout" : "-->검색 요청시 결과를 받는 데까지 걸리는 시간. timeout을 너무 작게하면
                  전체 샤드에서 timeout을 넘기지 않은 문서만 결과로 출력된다. 기본 값은 무한이다."
    ,"_source" : "-->검색시 필요한 필드만 출력하고 싶을 때 사용"
    ,"query" : "-->검색 조건문이 들어가는 공간"
    ,"aggs" : "-->통계 및 집계 데이터를 사용할때 사용하는 공간"
    ,"sort" : "-->문서 결과의 정렬 조건이 들어가는 공간"
}
cs

 

엘라스틱서치로 쿼리가 요청되면 해당 쿼리를 파싱해서 문법에 맞는 요청인지 검사한 후에 파싱에 성공하면 해당 쿼리를 기반으로 검색을 수행하고, 결과를 JSON으로 돌려준다.

 

<응답>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "took" : "-->쿼리 실행 시간"
    ,"timed_out" : "-->쿼리 시간이 초과할 경우를 나타낸다"
    ,"_shards" : {
        "total" : "-->쿼리를 요청한 전체 샤드 개수"
        ,"successful" : "-->검색 요청에 성공적으로 응답한 샤드 개수"
        ,"failed" : "-->검색 요청에 실패한 샤드 개수"
    }
    ,"hits" : {
        "total" : "-->질의에 매칭된 문서의 전체 개수"
        ,"max_score" : "-->일치하는 문서의 스코어 값중 가장 높은 값"
        ,"hits" : [결과 문서 정보와 스코어 값등을 보여줌]
    }
}
cs

 

만약 JSON문법의 오류가 있다면 오류 결과를 JSON으로 넘겨준다.

 

 

2.2. Query DSL 쿼리와 필터

Query DSL을 이용해 검색 질의를 작성할 때 조금만 조건이 복잡해 지더라도 여러개의 질의를 조합해 사용해야 한다. 이때 작성되는 작은 질의들을 두 가지 형태로 나눌 수 있다. 실제 분석기에 의한 전문 분석이 필요한 경우와 단순히 "YES/NO"로 판단할 수 있는 조건 검색의 경우다. 엘라스틱서치에서는 전자를 쿼리 컨텍스트라고 하고, 후자를 필터 컨텍스트라는 용어로 구분한다.

 

  쿼리 컨텍스트 필터 컨텍스트
용도 전문 검색 시 사용 조건 검색 시 사용
특징

1.분석기에 의해 분석이 수행됨.                    2.연관성 관련 점수 계산                              3.루씬 레벨에서 분석 과정을 거처야 하므로 상대적으로 느림.

1.YES/NO로 단순 판별 가능                           2.연관성 관련 점수 계산 안함                         3.엘라스틱서치 레벨에서 처리가 가능하므로 상대적으로 빠름

사용 예 "삼성전자의 본사는 어디있죠?" 같은 문장 분석

"created_year" 필드값이 2018년도인지 여부 "status" 필드에 "user"라는 코드 포함여부

 

대부분의 경우 쿼리 방식과 필터 방식 중 어떤 방식으로 검색 질의를 표현하더라도 같은 결과를 얻을 수도 있다. 하지만 둘의 용도와 성능차이가 있기 때문에 반드시 용도에 맞는 사용을 하는 것이 좋다.

 

2.2.1. 쿼리 컨텍스트

 

  • 문서가 쿼리와 얼마나 유사한지를 점수로 계산
  • 질의가 요청될 때마다 엘라스틱서치에서 내부의 루씬을 이용해 계산을 수행(결과 캐싱 x)
  • 일반적으로 전문 검색에 이용
  • 캐싱되지 않고 디스크 연산을 수행하기에 상대적으로 느림

 

2.2.2. 필터 컨텍스트

 

  • 쿼리의 조건과 문서가 일치하는지를 구분
  • 별도로 점수 계산은 하지 않고 단순 매칭 여부만 검사
  • 자주 사용되는 필터의 결과는 내부적으로 캐싱
  • 기본적으로 메모리 연산이기에 속도 빠름

 

2.3.1. Query DSL 주요 파라미터

 

Multi Index 검색

기본적으로 모든 검색 요청은 Multi Index, Multi Type 검색이 가능하다. 즉, 여러 인덱스를 검색할때 한번의 요청으로 검색요청을 보낼 수 있다. 검색 요청시 ","로 다수의 인덱스명을 입력한다.

 

예시) POST http://localhost:9200/인덱스명1,인덱스명2/_search ...

 

두개의 인덱스명이 공통적인 필드만 갖고 있다면 두개가 서로다른 스키마 구조이더라도 한번에 검색할 수 있다. 검색 요청시 인덱스명에 "*" 입력이 가능하기 때문에 더욱 손쉬운 멀티 인덱스 검색이 가능하다. 예를 들어 매일매일 날짜별로 인덱스를 생성하여 데이터를 색인하는 로그 수집으로 엘라스틱 서치를 이용한다고 생각해보면, "log-2019-01-01" 형태라고 가정시 "log-2019-*"로 손쉽게 멀티 인덱스 검색이 가능하다. 솔라 검색엔진과 비교하면 아주 강력한 기능이다. 보통 솔라는 엘라스틱서치와 달리 인덱스로 도메인을 구분하지 않고 인스턴스로 도메인을 구분하기 때문에 멀티테넌시를 제공하기 쉽지 않다. 그 말은 즉, 도메인 인스턴스별로 같은 질문을 여러번 요청을 보내야 엘라스틱서치의 멀티 인덱스 검색과 같은 결과를 얻을 수 있는 것이다.(최소한 필자 사용시는 그랬음...솔라도 비슷한 기능이 있을 수도 있음)

 

쿼리 결과 페이징

페이징을 하기 위해서는 두가지 파라미터를 이용한다. from과 size이다. 각 기본값은 0,5로 설정되어 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
예시) POST http://localhost:9200/인덱스명/_search - 1Page
{
    "from":0,
    "size":5,
    "query":{
        "term":{
            "fieldName":"value"
        }
    }
}
 
예시) POST http://localhost:9200/인덱스명/_search - 2Page
{
    "from":5,
    "size":5,
    "query":{
        "term":{
            "fieldName":"value"
        }
    }
}
cs

 

엘라스틱서치는 관계형 데이터베이스와는 조금 다르게 페이징된 범위의 문서만 가져오는 것이 아니라, 모든 데이터를 읽고 그 중 필터링하여 몇개의 문서를 가져오는 것이기 때문에 페이지 번호가 높아질수록 쿼리 비용은 비례하여 높아진다.

 

 

쿼리 결과 정렬

엘라스틱서치는 기본적으로 점수를 내림차순으로 결과정렬은 한다. 하지만 점수 이외에 정렬 조건을 주고 싶다면 sort 파라미터를 이용한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "term":{
            "fieldName":"value"
        }
    }
    ,"sort":{
        "_score":{
            "order":"desc"
        }
        ,"fieldName":{
            "order":"asc"
        }
    }
}
 
cs

 

위 예제는 점수가 동일할 시에 추가 정렬 조건으로 특정 필드 값으로 오름차순 정렬을 한다.

 

_source 필드 필터링

검색의 결과로 _source의 모든 항목을 결과로 반환한다.(_source의 모든 항목은 문서의 모든 필드를 포함한 결과) 하지만 모든 필드를 결과로 내보낼 필요가 없을 때도 있다. 그때 사용하는 것이 _source 파라미터이다. 필요한 필드만 결과값으로 출력한다면 네트워크 사용량을 줄여 응답 속도도 빨라질 수 있다.

 

1
2
3
4
5
6
7
8
9
10
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "term":{
            "fieldName":"value"
        }
    }
    ,"_source":["fieldName1","fieldName2"]
}
 
cs

 

범위검색

숫자나 날짜 등을 특정한 숫자나 날짜가 아니라 범위를 지정하여 질의 요청을 할 수도 있다.

 

문법 연산자 설명
lt < 피연산자보다 작음
gt > 피연산자보다 큼
lte <= 피연산자보다 작거나 같음
gte >= 피연산자보다 크거나 같음

 

2016년부터 2019년까지의 데이터 조회 예시이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "range":{
            "date":{
                "gte":"2016",
                "lte":"2019"
            }
        }
    }
}
 
cs

 

 

operator 설정

엘라스틱서치는 검색 시 문장이 들어올 경우 기본적으로 OR연산으로 동작한다. 하지만 실무에서 더 정확도 높은 결과를 위하여 AND연산을 사용하여 검색할때도 있다. 그때 사용하는 것이 operator 파라미터이다.(기본값은 OR이다)

 

1
2
3
4
5
6
7
8
9
10
11
12
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "match":{
            "fieldName":{
                "query":"coding start",
                "operator":"and"
            }
        }
    }
}
 
cs

 

coding start가 분석기에 의해 분리되고, 두개의 텀을 and 연산으로 검색을 하게 된다. 즉, 둘다 포함된 문서가 결과로 나올 것이다. 만약 OR 연산이라면 coding과 start라는 단어 둘 중 최소한 하나만 포함이 되어도 결과로 반환될 것이다.

 

minimum_should_match 설정

바로 위에서 보았던 operator 설정중 OR연산을 사용하면 너무 많은 결과가 나오게 된다. 이 경우 텀의 개수가 몇 개 이상 매칭될 때만 검색 결과로 나오게 할 수 있는데 이것이 바로 minimum_should_match 파라미터를 이용하는 것이다. 즉, OR연산자로 AND연산과 비슷한 효과를 낼 수 있게 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "match":{
            "fieldName":{
                "query":"this is coding start",
                "minimum_should_match" : 3
            }
        }
    }
}
 
cs

 

위의 쿼리의 뜻은 OR연산을 사용하되 4개의 텀중 최소한 3개가 일치해야 결과로 내보낸다라는 쿼리이다.

 

 

fuzziness 설정

fuzziness 파라미터를 이용하면 완벽히 동일한 텀으로 문서를 찾는 Match Query를 유사한 텀의 형태로 검색을 하는 Fuzzy Query로 변경할 수 있다. 이는 레벤슈타인 편집 거리 알고리즘을 기반으로 문서의 필드 값을 여러번 변경하는 방식으로 동작한다. 유사한 검색 결과를 찾기 위해 허용 범위의 텀으로 변경해 가며 문서를 찾아 결과로 출력한다. 예를 들어, 편집 거리수를 2로 설정하면 오차범위가 두 글자 이하인 검색 결과까지 포함해서 결과를 준다. 하지만 한국어에는 적용하기 쉽지 않다.(만약 키워드성 질의가 많은 검색이라면 어느정도는 한국어도 가능하기는 한것같다.) 오차범위 값으로 0,1,2,AUTO 총 4가지 값을 사용가능하다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "match":{
            "movieNm":{
                "query":"곤지엠",
                "fuzziness":1
            }
        }
    }
}
 
 
cs

 

 

위와 같이 질의를 날리면 "곤지암"이라는 영화 제목을 결과값으로 반환받을 수 있다.

 

 

boost 설정

boost 설정으로 관련성이 높은 필드나 키워드에 가중치를 더 줄 수 있게 해준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "multi_match":{
            "query":"Fly",
            "fields":["field1^3","field2"]
        }
    }
}
 
 
 
cs

 

위의 쿼리는 Fly라는 단어가 field1에 있을 경우 해당 문서의 가중치에 곱하기 3을 하여 더 높은 점수를 얻게 된다.

 

 

2.3.2. Query DSL 주요 쿼리

 

Match All Query

match_all 파라미터를 사용하여 색인의 모든 문서를 검색하는 쿼리이다.

 

1
2
3
4
5
6
7
8
9
10
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "match_all":{}
    }
}
 
 
 
 
cs

 

Match Query

Match Query는 텍스트, 숫자, 날짜 등이 포함된 문장을 형태소 분석을 통해 텀으로 분리한 후 이 텀들을 이용해 검색 질의를 수행한다. 즉, 검색어가 분리돼야 할 경우에 사용해야한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "match":{
            "fieldName":"coding start"
        }
    }
}
 
 
 
 
 
cs

 

위의 쿼리는 형태소분석에 의해 coding, start라는 두개의 텀으로 분리되고 별도의 operator가 없기에 OR연산으로 fieldName이라는 필드에 coding,start 텀을 OR연산으로 검색한다.

 

 

Multi Match Query

multi_match 파라미터를 이용하여 하나의 필드가 아니라 여러개의 필드에 대해서 검색을 수행할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "multi_match":{
            "query":"가족",
            "fields":["fieldName1","fieldName2"]
        }
    }
}
 
 
 
 
 
 
cs

 

위에서도 다루어봤지만 multi_match를 사용하면서 특정 필드에 가중치를 부여할 수도 있다.

 

 

Term Query

텍스트 형태의 값을 검색하기 위해 엘라스틱서치는 두 가지 매핑 유형을 지원한다.

 

타입 설명
Text 타입 필드에 데이터가 저장되기 전에 데이터가 Analyzer에 의해 분석되어 역색인 구조로 저장됨.
Keyword 타입 데이터가 분석되지 않고 문장이라도 하나의 텀(분석되지 않은)으로 저장됨

 

바로 이전에 다룬 Match Query는 검색어 매칭 전에 검색 텍스트에 대해 형태소 분석이 들어간다. 하지만 Term Query는 별도의 형태소 분석없이 검색 문자 그대로 해당 문자와 동일한 문서가 있는지 찾는다. 즉, Keyword 데이터 타입을 사용하는 필드를 검색하기 위해서는 Term Query를 사용한다. 그리고 정확히 일치하지 않으면 검색 결과로 나오지 않는다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "term":{
            "fieldName":"코미디"
        }
    }
}
 
 
 
 
 
 
 
cs

 

fieldsName이라는 필드에 코미디라는 정확한 단어가 포함된 문서가 있다면 결과로 돌려준다.

 

 

Bool Query

RDB에서는 다양하게 AND,OR,NOT 등의 조건을 여러개 WHERE절에 사용할 수 있다. 이처럼 엘라스틱서치도 여러개의 조건 절을 연결하여 더욱 정확한 검색결과를 얻을 수 있는 쿼리가 있는데, 바로 boot query이다. 

 

엘라스틱서치 SQL 설명
must : [] AND 칼럼=조건 반드시 조건에 만족하는 문서만 검색
must_not : [] AND 칼럼!=조건 조건을 만족하지 않는 문서가 검색
should : [] OR 칼럼=조건 여러 조건 중 하나 이상을 만족하는 문서 검색
filter : [] 칼럼 IN (조건) 조건을 포함하고 있는 문서를 출력. 해당 파라미터를 사용하면 점수별로 정렬하지 않는다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "bool":{
            "must":[
                {
                    "term":{
                        "repGenreNm":"코미디"    
                    }
                },
                {
                    "match":{
                        "repNationNm":"한국"
                    }
                }
            ],
            "must_not":[
                {
                    "match":{
                        "typeNm":"단편"
                    }
                }
            ]
        }
    }
}
 
 
 
 
 
 
 
 
cs

 

해당 쿼리는 장르가 코미디이면서 제작사 나라가 한국이라는 단어를 포함하고 있으며, 종류가 단편이 아닌 영화를 조회하는 쿼리이다.

 

 

Prefix Query

Prefix Query는 해당 접두어가 있는 모든 문서를 검색하는데 사용한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "prefix":{
            "movieNm":"자전"
        }
    }
}
 
 
 
 
 
 
 
 
 
cs

 

영화제목에 자전이라고 시작하는 영화 제목을 검색 결과로 모두 가져온다.

 

 

Exists Query

필드 값이 null 인  데이터를 제외하고 검색한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "exists":{
            "field":"movieNm"
        }
    }
}
 
 
 
 
 
 
 
 
 
cs

 

Wildcard Query

검색어가 와일드카드와 일치하는 구문을 찾는다. 이때 입력된 검색어는 형태소 분석이 이뤄지지 않는다.

 

와일드카드 옵션 설명
* 문자의 길이와 상관없이 와일드카드와 일치하는 모든 문서를 찾는다
? 지정된 위치의 한 글자가 다른 경우의 문서를 찾는다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
예시) POST http://localhost:9200/인덱스명/_search
{
    "query":{
        "wildcard":{
            "fieldName":"여?"
        }
    }
}
 
 
 
 
 
 
 
 
 
cs

 

여기까지 간단한 엘라스틱서치 검색 API에 대해 다루어보았다. 다음 포스팅에서는 더 다루지 못한 검색 API 나머지 부분을 다룰 예정이다. 혹시 잘못된 점이나 모르는 점 등이 있다면 댓글을 꼭 부탁드린다..

posted by 여성게
:
Middleware/Zookeeper 2018. 12. 31. 23:31

Zookeeper란?,분산코디네이터



분산 처리 환경에서 필수로 언급되는 ZooKeeper란 무엇일까? 한 마디로 정의하면 "분산 처리 환경에서 사용 가능한 데이터 저장소"라고 말할 수 있겠다. 기능은 매우 단순하지만 분산 서버 환경에서는 활용 분야가 넓다. 예를 들어 분산 서버 간의 정보 공유, 서버 투입/제거 시 이벤트 처리, 서버 모니터링, 시스템 관리, 분산 락 처리, 장애 상황 판단 등 다양한 분야에서 활용할 수 있다.

'어떻게 다양한 활용이 가능할까?'라는 의문을 해결하기 위해서 기능에 대해서 한 마디로 설명하면 다음과 같이 설명할 수 있다.

ZooKeeper는 데이터를 디렉터리 구조로 저장하고, 데이터가 변경되면 클라이언트에게 어떤 노드가 변경됐는지 콜백을 통해서 알려준다. 데이터를 저장할 때 해당 세션이 유효한 동안 데이터가 저장되는 Ephemeral Node라는 것이 존재하고, 데이터를 저장하는 순서에 따라 자동으로 일련번호(sequence number)가 붙는 Sequence Node라는 것도 존재한다. 조금 과장하면 이러한 기능이 ZooKeeper 기능의 전부다. 이런 심플한 기능을 가지고 자신의 입맛에 맞게 확장해서 사용하면 된다.



기능을 하나씩 자세히 살펴보자. 우선 ZooKeeper가 데이터를 저장하는 방식인 데이타 모델에 대해서 살펴보자.

ZooKeeper는 <그림 1>과 같은 디렉터리 구조로 데이터를 저장한다. 특징을 살펴보면 Persistent를 유지하기 위해서 트랜잭션 로그와 스냅샷 파일이 디스크에 저장되어 시스템을 재시작해도 데이터가 유지된다. 각각의 디렉터리 노드를 znode라고 명명하며, 한 번 설정한 이름은 변경할 수 없고 스페이스를 포함할 수도 없다.

zookeeper1

그림 1 ZooKeeper의 데이터 모델(이미지 출처)

데이터를 저장하는 기본 단위인 노드에는 Persistent Node와 Ephemeral Node, Sequence Node, 3가지가 있다.

첫 번째로 Persistent Node는 한 번 저장되고 나면 세션이 종료되어도 삭제되지 않고 유지되는 노드다. 즉, 명시적으로 삭제하지 않는 한 해당 데이터는 삭제 및 변경되지 않는다.

두 번째로 Ephemeral Node는 특정 노드를 생성한 세션이 유효한 동안 그 노드의 데이터가 유효한 노드다. 좀 더 자세히 설명하면 ZooKeeper Server에 접속한 클라이언트가 특정 노드를 Ephermeral Node로 생성했다면 그 클라이언트와 서버 간의 세션이 끊어지면, 즉 클라이언트와 서버 간의 Ping을 제대로 처리하지 못한다면 해당 노드는 자동으로 삭제된다. 이 기능을 통해 클라이언트가 동작하는지 여부를 쉽게 판단할 수 있다.

세 번째로 Sequence Node는 노드 생성 시 sequence number가 자동으로 붙는 노드다. 이 기능을 활용해 분산 락 등을 구현할 수 있다.

다음으로 ZooKeeper 서버 구성도를 살펴보자.

zookeeper2

그림 2 ZooKeeper의 서버 구성도(이미지 출처)

ZooKeeper 서버는 일반적으로 3대 이상을 사용하며 서버 수는 주로 홀수로 구성한다. 서버 간의 데이터 불일치가 발생하면 데이터 보정이 필요한데 이때 과반수의 룰을 적용하기 때문에 서버를 홀수로 구성하는 것이 데이터 정합성 측면에서 유리하다. 그리고 ZooKeeper 서버는 Leader와 Follower로 구성되어 있다. 서버들끼리 자동으로 Leader를 선정하며 모든 데이터 저장을 주도한다.



클라이언트에서 Server(Follower)로 데이터 저장을 시도할 때 Server(Follower) -> Server(Leader) -> 나머지 Server(Follower)로 데이터를 전달하는 구조이고, 모든 서버에 동일한 데이터가 저장된 후 클라이언트에게 성공/실패 여부를 알려주는 동기 방식으로 작동한다. 즉, 모든 서버는 동일한 데이터를 각각 가지고 있고, 클라이언트가 어떤 서버에 연결되어 데이터를 가져가더라도 동일한 데이터를 가져가게 된다.

사용/운영 시 몇 가지 주의 사항이 있다. 개발에 급급한 나머지 ZooKeeper를 캐시 용도로 사용해 서비스를 오픈했다가 장애가 발생한 사례도 종종 있다고 한다. 데이터의 변경이 자주 발생하는 서비스에서 ZooKeeper를 데이터 저장소로 사용하는 것은 추천하지 않는다. ZooKeeper에서 추천하는 Read : Write 비율은 10 : 1 이상이다.

그리고 ZooKeeper 서버가 제대로 실행되지 않을 때가 있는데, 대부분 서버간의 데이터 불일치로 인한 데이터 동기화 실패가 그 원인이다. 주로 베타 테스트 후 운영 직전에 ZooKeeper 서버를 증설해서 사용하는데, 이럴 때 기존에 테스트했던 서버와 신규로 투입한 서버의 데이터 차이로 인해 이런 현상이 종종 발생한다. 이때는 데이터를 초기화한 후 서버를 실행하면 된다.

그리고 마지막으로, zoo.cfg라는 설정(configuration) 파일의 ZooKeeper 서버 목록을 꼼꼼히 확인해야 한다. 서버 목록이 정확히 맞지 않아도 서버가 실행되긴 하지만, 로그 파일을 확인하면 ZooKeeper 서버가 지속적으로 재시작할 때도 있고 데이터를 엉뚱한 곳에 저장하기도 한다.


출처:https://d2.naver.com/helloworld/294797

posted by 여성게
:

Solr 7.4.x version의 Tagger Handler를 이용한 NER(Named-Entity Recognition)


NER이란 자연어에서 뜻이 있는 단어를 뽑아내는 것이다. 챗봇처럼 자연어와 관련있는 기술에서 사용되는 기능인데, 예를 들어 "피자 주문할게요" 라는 질문이 있다. 이 문장에서 유추해 볼 수 있는 것은 "피자 주문" 이것을 더 보편화시켜 보면 "메뉴 주문"이라는 사용자질의 "의도"를 알 수 있다. 만약 "햄버거 주문할게요"라는 질의가 있으면 이 또한 "메뉴 주문"이라는 의도라는 것을 유추할 수 있다. 그럼 사용자가 어떠한 메뉴를 주문한다는 것은 알겠는데 그럼 그 메뉴가 무엇인가? 이러한 것이 챗봇에서는 NER이라는 기술로 추출해 낼 수 있는 단어라는 것이다. "피자 주문할게요"라는 질의의 의도는 "메뉴 주문" 개체명(Named-Entity)는 "피자"가 되는 것이다. 이렇게 의미있는 단어를 뽑아내는 것은 아주 중요한 챗봇의 기술이며 앞으로 더욱 발전해야할 기술이기도 하다. 이 NER 기술을 solr 7.4의 Tagger Handler를 이용하여 구현해 볼 것이다.


모든 구현은 Mac OS 환경에서 구현하였습니다.


우선 구현에 앞서 선행되어야 하는 것이, 바로 이전의 글인 Solr와 Zookeeper 연동입니다. 모든 환경은 solr cloud 환경에서 구현하였기에 중복되는 설명은 배제하였습니다.



$SOLR_HOME/server/solr 폴더 밑에 configset이 존재할 것입니다. 그 폴더를 보면 디폴트 환경설정 파일이 있는데, 그 안의 schema.xml과 solrconfig.xml 파일을 만질 것입니다. 이전에 따로 사용중이셨던 설정파일을 이용하셔도 무관합니다.




<schema.xml>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<field name="DOCID" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="NER_VALUE" type="text_general" indexed="true"/>
<field name="NER_KEY" type="string" indexed="true"/>
<field name="NER_TAG" type="tag" stored="false"/>
<copyField source="NER_VALUE" dest="NER_TAG"/>
<uniqueKey>DOCID</uniqueKey>
<fieldtype name="tag" class="solr.TextField" positionIncrementGap="100" postingsFormat="FST50"
        omitTermFreqAndPositions="true" omitNorms="true">
    <analyzer type="index">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.ASCIIFoldingFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
      <filter class="solr.ConcatenateGraphFilterFactory" preservePositionIncrements="false"/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.ASCIIFoldingFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>  
</fieldtype>
cs



적당한 위치에 선언해줍니다. NER과 관련된 필드들과 필드 타입을 선언한 설정입니다.


<solrconfig.xml>

1
2
3
4
5
<requestHandler name="/tag" class="solr.TaggerRequestHandler">
    <lst name="defaults">
      <str name="field">NER_TAG</str>
    </lst>
</requestHandler>
cs


적당한 위치에 선언해줍니다. 개체명을 추출하기 위한 Tagger Handler를 등록하는 설정입니다.


만약 디폴트 설정파일을 이용하지 않고 새로 정의하셨다면 해당 설정 폴더를 SolrConifg/conf/ 밑으로 넣어줍니다.(SolrConfg는 맘대로정의 가능하지만 conf는 맞춰주셔야합니다.)


solr 디렉토리의 적당한 곳에 위치시켜줍니다. 그리고 난 후에 zookeeper에 upconfig를 합니다.(solr collection들이 사용할 설정파일을 zookeeper에 업로드하여 중앙에서 관리해줍니다.)

저는 앞으로 자주 사용할 가능성이 있다 판단이 되어서 쉘스크립트 실행파일을 만들어서 upconfig 했습니다.


<uploadSolrConfig.sh>

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
 
# usage uploadSolrConfig.sh zipConfigPath configName zkServers
 
CONFIG_PATH=$1
CONFIG_NAME=$2
ZK_SERVERS=$3
 
# upload config info
 
$HOME/solr/bin/solr zk upconfig -n $CONFIG_NAME -d $CONFIG_PATH -z $ZK_SERVERS
 
echo $?
cs


~$ ./uploadSolrConfig $SOLR_HOME/SolrConfig TaggerHandlerConf localhost:2181,localhost:2182,localhost:2183 으로 실행시켜줍니다.

이후 solr admin 페이지를 들어가서 cloud>file>confg에 생성한 이름으로 conf file이 생성되었는지 확인합니다. 그리고 Collection탭에 들어가서 생성한 conf file을 이용하여 컬렉션을 생성해줍니다.(collectionName = TaggerHandler)




<indexing file>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<add>
    <doc>
        <field name="DOCID">NER1</field>
        <field name="NER_VALUE">햄버거</field>
        <field name="NER_KEY">메뉴</field>
    </doc>    
    <doc>
        <field name="DOCID">NER2</field>
        <field name="NER_VALUE">피자</field>
        <field name="NER_KEY">메뉴</field>
    </doc>
    <doc>
        <field name="DOCID">NER3</field>
        <field name="NER_VALUE">치킨</field>
        <field name="NER_KEY">메뉴</field>
    </doc>    
    <doc>
        <field name="DOCID">NER</field>
        <field name="NER_VALUE">파스타</field>
        <field name="NER_KEY">메뉴</field>
    </doc>
</add>
cs


그리고 색인할 파일을 만듭니다.


$SOLR_HOME/bin/post -c TaggerHandler $SOLR_HOME/menu.xml 로 색인해줍니다.


이제 모든 작업이 끝이 났고, 적당한 request를 보내서 결과를 확인하면 됩니다.


요청은 POST 방식이고, 요청헤더에 Content-Type text/plain 요청바디에 "햄버거, 피자,피자 주문할게요"라는 질의를 넣고 밑의 요청을 보내면 (url 테스트를 위하여 저는 간편한 postman이라는 툴을 사용하였습니다. curl을 사용해도 무관합니다.)


http://localhost:8983/solr/TaggerHandler/tag?overlaps=NO_SUB&tagsLimit=5000&fl=DOCID,NER_VALUE,NER_KEY&wt=json&intent=on


{ "responseHeader": { "status": 0, "QTime": 35 }, "tagsCount": 3, "tags": [ [ "startOffset", 0, "endOffset", 3, "ids", [ "NER1" ] ], [ "startOffset", 5, "endOffset", 7, "ids", [ "NER2" ] ], [ "startOffset", 8, "endOffset", 10, "ids", [ "NER2" ] ] ], "response": { "numFound": 2, "start": 0, "docs": [ { "DOCID": "NER1", "NER_VALUE": "햄버거", "NER_KEY": "메뉴" }, { "DOCID": "NER2", "NER_VALUE": "피자", "NER_KEY": "메뉴" } ] } }


=>이런 결과가 나옵니다!!!


posted by 여성게
:

Solr&Zookeeper(솔라&주키퍼) cloud 환경 구성



Mac OS 환경에서 작성되었습니다.


solr와 zookeeper를 연동하여 cloud 환경구성하기 입니다.


우선 진행하기 전에 수정 혹은 생성 되어야할 설정 목록입니다.


1)solr.xml : solr cloud를 이루는 solr instance에 관한 설정파일입니다.

2)zoo.cfg : zookeeper 관련 설정파일입니다.

3)collection config file : solr collection들이 가지게 될 schema.xml,solrConfig.xml 등의 파일이 들어가는 config file입니다. 이 파일은 zookeeper에 upconfig하여 모든 solr                                         instance들이 공유하게 됩니다.

4)zooServer Dir : zookeeper들은 파일로써 data snapshot을 저장합니다. 그러한 snapshot파일과 zookeeper 식별 파일들이 들어가는 디렉토리입니다.




<solr.xml>

solr의 폴더의 위치를 편하게 $SOLR_HOME이라고 표현합니다.


$SOLR_HOME/server/solr

$SOLR_HOME/server/solr2

$SOLR_HOME/server/solr3


이렇게 3개의 폴더를 생성해줍니다.(solr는 원래 존재하는 폴더) 그리고 solr folder 밑에 solr.xml파일을 복사하여 solr2/solr3 folder 밑에 복사해줍니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8983}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8984}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8985}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs


총 3개의 solr.xml이 각각의 solr,solr2,solr3 폴더에 위치하게 됩니다. 보시는 것과 같이 service port 만 달라지게 됩니다. 이렇게 solr instance가 3개가 뜨게 되며, 각각 8983,8984,8985 port로 요청을 받게 됩니다. 여기서 인스턴스를 홀수개로 띄우는 이유는 리더선출에 관련되어 있습니다. 반드시 클라우드 환경에서는 홀수개의 인스턴스를 띄워줘야 합니다.


그리고 $SOLR_HOME에 적절한 위치에 zookeeper에 upconfig할 config file folder를 위치시켜줍니다. 본인은 server folder와 같은 위치에 solr-config 라는 folder로 만듬. 그리고 이 solr-config 밑에는 conf라는 폴더가 위치해야하고 conf라는 폴더 밑에 upconfig를 위한 설정파일들이 위치해야함.







<zoo.cfg>

zookeeper가 위치한 폴더경로를 편하게 $ZK_HOME이라고 표현하겠습니다.


1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/1
clientPort=2181
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs


1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/2
clientPort=2182
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs

1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/3
clientPort=2183
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs


$ZK_HOME/conf 밑에 총 3개의 zoo.cfg,zoo2.cfg,zoo3.cfg 파일들을 생성해줍니다. 여기서 dataDir는 zookeeper의 식별자파일 및 data snapshot들이 저장되는 경로입니다. 3개의 zookeeper 인스턴스는 각각의 zoo.cfg를 갖습니다.(zookeeper도 동일한 이유로 홀수개의 인스턴스를 띄움). clientPort는 말그대로 zookeeper가 사용할 포트입니다. 그 밑에 server.1=~는 총개의 zookeeper의 인스턴스를 뜻하며 2개의 포트는 zookeeper들끼리 통신할 포트와 리더선출을 위한 포트로 사용됩니다.



<dataDir>



zoo.cfg에 작성한 dataDir와 동일한 위치에 디렉토리 구조를 잡아줍니다. 그리고 그 디렉토리 밑에 확장자가 없는 myid파일들을 각각 만들어줍니다.(~$vi myid) 그리고 각 내용은 상위 폴더와 같이 1/myid는 1이라는 숫자하나 2/myid는 2라는 숫자하나 3/myid는 3이라는 숫자하나만 넣어줍니다.(version-2 폴더는 필자가 cloud환경을 운영하다 생긴 폴더입니다. zookeeper의 data snapshot 파일등이 들어가있습니다. 별도로 생성할 필요 x)


여기까지 모두 세팅이 완료되었습니다.


zookeeper 실행

bin/zkServer.sh start conf/zoo.cfg

bin/zkServer.sh start conf/zoo2.cfg

bin/zkServer.sh start conf/zoo3.cfg 


zookeeper 종료

bin/zkServer.sh stop conf/zoo.cfg

bin/zkServer.sh stop conf/zoo2.cfg

bin/zkServer.sh stop conf/zoo3.cfg


solr 실행

bin/solr start -c -s server/solr -p 8983 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

bin/solr start -c -s server/solr2 -p 8984 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

bin/solr start -c -s server/solr3 -p 8985 -z localhost:2181,localhost:2182,localhost:2183 -noprompt


solr 종료
bin/solr stop -c -s server/solr -p 8983 -z localhost:2181,localhost:2182,localhost:2183 -noprompt
bin/solr stop -c -s server/solr2 -p 8984 -z localhost:2181,localhost:2182,localhost:2183 -noprompt
bin/solr stop -c -s server/solr3 -p 8985 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

이렇게 하면 zookeeper 3개 solr 3개가 올라가는 cloud환경을 구성할 수 있습니다. 지금은 로컬환경이라 한 서버에 3개를 모두 올렸지만 나중에 서버환경에서는 각각 다른 서버에 올리시면 됩니다. 여기서 중요한 것은 만약 다른 서비스 프로젝트에서 solr와 연동되어 사용될때, solr 요청 ip가 localhost가 아니라 server_ip로 요청(즉, 로컬환경이 아니라 서버배포환경)이 간다면 처음에 solr.xml에 설정된 <str name="host">server_ip</str>로 변경해주셔야합니다. 다른 서비스프로젝트에서는 server_ip로 요청을 보내고 cloud환경의 solr는 localhost로 띄운다면 정상적으로 동작하지 않을 것입니다.

여기까지입니다.

여기서 생략된 부분이 몇가지 있다면 solr의 zk upconfig 명령어를 이용하여 solr-config에 있는 config file을 zookeeper에 업로드하는 명령어와 기타 색인 및 쿼리 작업들은 생략되었습니다. 이 부분들은 대부분 구글링 하면 많은 것이라 생각하기에...생략하였습니다. 하지만 중요한 부분입니다. collection을 생성할때에는 반드시 config파일의 이름을 물고 생성이 되기때문에 해당 config파일이 upconfig되어있어야지만 collection이 생성됩니다.(default config파일도 있지만 사용자들이 커스터마이징한 config file을 사용한다는 전제하에) 혹시라도 이 부분에 대해 확실한 답을 찾지 못하셨거나 궁금하신 점이 있으면 댓글 달아주시면 개인적으로 메일드리겠습니다.

posted by 여성게
: