Sphinx 검색엔진 3부 - 검색 및 설정




검색을 해 봅시다.



이제 모든게 갖추어 졌으니, 검색을 해 보도록 하겠습니다.

아래처럼 sphinx 에 접속합니다.

[und3r@sungwook ~]$ mysql -h0 -P9306



현재 words_kr 테이블에는 abc 가 존재합니다. 검색해 보도록 하겠습니다.

mysql> select * from words_kr where match('internet');

+------+----------+

| id   | word     |

+------+----------+

|   59 | internet |

+------+----------+

1 row in set (0.00 sec)



보시는것 처럼 where match() 형태로 검색을 합니다. 지금의 경우 대상 column 이 word 하나 밖에 없어서 예로 적절치는 않습니다만, 만약 대상 column 이 2개 이상이라면 어떻게 될까요? 어떤 column 에서 찾는걸까요?

위와 같이, 별도로 명시하지 않는 경우, 모든 대상 컬럼(sql_field_*)으로부터 검색을 합니다. 명시적으로 특정 column 에서만 검색하려면 아래처럼 대상컬럼을 적어주어야 합니다.


mysql> select * from words_kr where match('@word internet');

+------+----------+

| id   | word     |

+------+----------+

|   59 | internet |

+------+----------+

1 row in set (0.00 sec)




지금의 경우는 100% 일치할 경우입니다. 아래처럼 비슷한 경우는 검색되지 않습니다.

mysql> select * from words_kr where match('inter');

Empty set (0.00 sec)


mysql> select * from words_kr where match('inter*');

Empty set (0.00 sec)






와일드 케릭터(*) 사용하기



부분 일치되는 것도 검색되게 하려면 어떻게 해야 할까요?

이것이 가능하게 하려면 sphinx.conf 파일을 수정해 주어야 합니다.

각 index 에 아래처럼 min_infix_len 을 넣어줍니다.

index words_kr

{

    min_infix_len   = 3

    source          = words_kr

    path            = /var/lib/sphinx/words_kr

}


min_infix_len 는 '검색할 때 반드시 매칭되어야 하는 최소 문자 수' 정도로 생각하시면 됩니다.

위처럼 값이 3일때는, internet 을 검색하기 위해서 최소 세문자가 일치할 경우에만 검색된다는 것이죠.

아래 예를 보시면 쉽게 이해가 되실겁니다.


참, 반드시 conf 가 변경되면, indexer 를 통해서 다시 indexing 해 주어야 합니다.

[und3r@sungwook ~]$ sudo indexer -c /etc/sphinx/sphinx.conf --rotate --all



mysql> select * from words_kr where match('int*');

+------+----------+

| id   | word     |

+------+----------+

|   59 | internet |

+------+----------+

1 row in set (0.00 sec)


mysql> select * from words_kr where match('in*');

Empty set, 1 warning (0.00 sec)


mysql> select * from words_kr where match('*nte*');

+------+----------+

| id   | word     |

+------+----------+

|   59 | internet |

+------+----------+

1 row in set (0.00 sec)


mysql> select * from words_kr where match('*nt*');

Empty set, 1 warning (0.00 sec)



최소 한글자로 검색하고 싶다면, min_infix_len 을 1 로 세팅하시면 됩니다.




한글(unicode) 검색하기



기존 방식대로 한글 검색을 해 봅시다.


mysql> select * from words_kr where match('소나기');

Empty set (0.00 sec)


왜 검색이 안될까요?

바로 charset_table 이라는 녀석 때문입니다. 이것이 키워드를 뽑아낼 때, 어떤 문자를 허용할건지, 말 것인지를 결정합니다. sphinx 에서 기본값은 latin 과 cyrillic 문자 입니다. 그렇기 때문에 한글은 토큰화 되지 않고, 검색이 불가능 한 것입니다. 다시말해 라틴(latin)과 키릴(cyrillic)문자 이외에는 검색이 불가능하다는 이야깁니다. 특수문자, 일본어, 한자, 한글 등등 검색이 안됩니다.


이 문제를 해결하기 위해서는 charset_table 를 설정해 주면 됩니다. 이 값에 토큰화 시킬 대상의 문자들을 입력해 주면 됩니다, 만약 한글 '가', '나' 를 추가해 주고 싶다면, 아래처럼 해주면 됩니다.

charset_table = U+AC00, U+B098


콤마로 구분해서 하나씩 써 주면 되는데, 위와 같이 유니코드로 적어주어야 합니다. 유니코드는 위키에서 참고하시면 됩니다.

http://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C

그렇다면 모든 한글을 다 일일이 이렇게 입력해 주어야 하느냐... 인데, 아닙니다. 쉽게 쓸수 있도록 나름의 문법을 지원합니다.

해당 링크나 아래 그림을 참고하시기 바랍니다.





그럼 한글을 지원하고 싶다면, 어떻게 할까요? 한글의 유니코드 범위를 모두 작성해 주면 됩니다.

자음/모음/조합된 한글의 유니코드의 범위는 아래와 같습니다.

U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9


여기에 숫자, 알파벳을 추가하면 아래와 같이 됩니다.

charset_table = 0..9, A..Z->a..z, a..z, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9


일본어, 중국어 이외에 특수문자 등도 검색이 가능하게 하려면, 이곳 charset_table 에 추가해 주면 됩니다.


+추가내용

위에 나와있듯이, 검색대상을 직접 지정해 주어야 합니다. A..Z->a..z 의 의미는 A부터 Z 까지를 a부터 z 와 동일하게 취급하겠다는 이야깁니다. 다시말해서 대문자를 소문자로 처리하겠다는 것이죠. 이렇게 하면 대소문자 구분없이 검색이 가능해 집니다. 내부적으로는 대문자를 소문자로 변환해서 저장(?) 한다고 생각하시면 됩니다. 그런데 주의할점이 있습니다. A..Z->a..z 는 대문자를 소문자로 취급하겠다는 것이 전부입니다. 검색 대상은 a..z 처럼 따로 등록해 주어야 합니다. 대문자를 소문자로 mapping 만 할뿐, 변환된 소문자를 검색 대상으로 '등록' 해 주는 작업은 별개의 일이라는 말입니다.




U+FF01..U+FF5E->U+0021..U+007E, U+0021..U+0040, U+0041..U+005A->U+0061..U+007A, U+0061..U+007A, U+005B..U+0060, U+007B..U+007E


위 내용을 분석해 보죠. U+FF01..U+FF5E 는 아래 영역의 문자입니다.



단순하게 ASCII 문자들 처럼 보이지만 다릅니다. ! 의 차이점입니다. 우리가 쓰는 ASCII 는 아래 영역입니다.



분명 모양은 같은데, 실제로는 다른 유니코드지요. 즉, 검색이 안된다는 이야깁니다. 이때 필요한 기능이 mapping 입니다. U+FF01..U+FF5E->U+0021..U+007E 이것이 바로 그 역할을 합니다. 앞서 말씀드린대로 이것은 mapping 하는 것 뿐이고, U+0021..U+007E 를 검색 대상으로 등록은 별도로 해 주어야 합니다.




U+FF01..U+FF5E->U+0021..U+007E, U+0021..U+0040, U+0041..U+005A->U+0061..U+007A, U+0061..U+007A, U+005B..U+0060, U+007B..U+007E


위 코드를 다시 한번 살펴 봅시다. U+FF01..U+FF5E 의 영역을 U+0021..U+007E 과 동일하게 취급하겠다는 이야깁니다. 그리고 U+0021..U+0040 영역인 특수문자 + 숫자 영역을 검색대상으로 등록하겠다는 이야기고요. U+0041..U+005A->U+0061..U+007A 는 대문자를 소문자와 동일하게 취급하겠다는 이야깁니다. 그 이후 U+0061..U+007A 인 소문자를 검색 대상에 추가하겠다는 이야깁니다. U+005B..U+0060 와 U+007B..U+007E 는 해당 문자를 검색 영역에 등록하는 작업이 되겠습니다.


주의할점은 U+0020 처럼 등록되지 않은 문자를 추가해서는 안됩니다. 이런 문자가 UNICODE TABLE 에 보면 여러개 존재하는데 충돌납니다.




돌아와서 이렇게 indexer 로 다시 indexing 하고나면, 아래처럼 한글 검색이 잘 되는것을 볼 수 있습니다.

mysql> select * from words_kr where match('소나기');

+------+-----------+

| id   | word      |

+------+-----------+

|    4 | 소나기    |

+------+-----------+

1 row in set (0.00 sec)


mysql> select * from words_kr where match('소나*');

+------+-----------+

| id   | word      |

+------+-----------+

|    4 | 소나기    |

+------+-----------+

1 row in set (0.00 sec)


mysql> select * from words_kr where match('*나기');

+------+-----------+

| id   | word      |

+------+-----------+

|    4 | 소나기    |

+------+-----------+

1 row in set (0.00 sec)





중국어(한자), 일본어 등 검색하기



한국어와 마찬가지로, 중국어 일본어들도 charset_table 에 추가해 주어야 합니다. 유니코드 범위를 찾아서 등록해 주면 되는데, 비어있는(reserved) 유니코드는 charset_table 에 포함되지 않도록 주의해야 합니다.


한 가지 중요한게 있습니다. 중복되는 문자에 대해서 동일하게 인식되도록 해 주어야 합니다. 이해가 되시나요?

문자 '7'(U+0037) 이 있습니다. 그런데 '7'(U+FF17) 도 있습니다. 문자 'a'(U+0061) 가 있습니다. 그런데 ''(U+FF41)도 있습니다. 엄밀히 보면 두 문자는 다릅니다. 그런데 사람이 볼때는 같다고 보는게 맞을 것입니다.

이렇게 사람이 볼때는 같은 문자인데, 유니코드에서는 따로 존재하는 문자들이 꽤 많습니다. 특히 한자에도 이런게 많이 있습니다. U+4E00..U+9FFF 범위는 CJK Unified Ideographs 입니다. 즉 한중일 언어 입니다. 그런데 U+F900..U+FAFF 범위는 CJK Compatibility Ideographs 입니다. 이 두 유니코드 영역에 문자들을 살펴보면 동일한게 많이 있습니다. 제대로 검색을 하려면, 일일이 같은 문자를 찾아서 동일한 처리가 되도록 해야 합니다. 엄청나게 귀찮고 복잡한 일인데, 어쩔수가 없습니다.

일반적으로 이것은 유니코드 노멀라이즈(Unicode Normalize) 라고 합니다. 


이런것들을 고려해서 작성하면, 아래처럼 됩니다. 기본적인 문자 + 한글 + 일본어 + 한자에 대한 charset_table 입니다.


charset_table

더보기






한글(unicode) 좀 더 유연하게 하기



검색 방법에는 많은 방식이 있습니다. 그중 대표적인것으로 형태소 분석, 스태밍, n-gram 등이 있습니다. sphinx 는 n-gram 방식을 지원합니다. 각 방식에 대해서 찾아보시기 바랍니다.


n-gram 방식에 대해서만 설명을 드리자면, 몇 글자로 keyword 를 나누어 indexing 할 것인가? 입니다. 예를 들어보죠.


'동해물과 백두산' 이라는 문장이 있습니다. 이것을 n-gram 방식의 2-gram(ngram_len = 2) 으로 indexing 해 봅시다.

'동해', '해물', '물과', '과 ', ' 백', '백두', '두산'

이렇게 2문자씩 묶이게 됩니다. n 값이 2 이기 때문이죠. 3이라면 아래와 같이 됩니다.
'동해물', '해물과', '물과 ', '과 백', ' 백두', '백두산'


검색 키워드가 '해물' 인 경우 '동해물과 백두산' 이 검색되길 바라지 않을 것입니다. 일반적인 자연어 검색이라면 말이죠. 즉, 이 경우 2-gram(bigram)은 부적절합니다. 3-gram 이라면 '해물'로 검색하더라도 해당 문장이 검색되지 않을 것입니다.


이때 이 값을 변경할 수 있는 옵션이 바로 'ngram_len' 입니다. 함께 쓰이는 옵션으로 'ngram_chars' 가 있습니다. 어떤 문자에 대해서 ngram 을 적용할 것인지 정하는 옵션입니다. 지정 방식은 charset_table 과 동일합니다.



자연어에 대한 검색인 경우에는 ngram 을 사용하는게 나을 것입니다. 그러나 일반적인 검색(like)은 'keyword*' 형태로 검색하는 것이 좋다고 생각합니다.




이외의 옵션들



이외에도 상당히 많은 옵션들이 존재합니다. html 태그를 srip 하는것 부터 시작해서, 앞 문자 몇 개부터 index 할건지 등등 매우 많습니다. 관련된 옵션들을 한번 훑어보시는것을 권장합니다. :)


아래 주소에서 확인 가능합니다.

http://sphinxsearch.com/docs/current/confgroup-index.html



마지막으로 최종적인 '한글, 일본어(히라가나, 카타카나), 특수문자' 를 지원하는 sphinx.conf 는 아래와 같습니다.

ngram 은 사용하지 않습니다.


더보기







신고