본문 바로가기

데비안 리눅스 설치 가이드/Sphinx 검색 엔진

[Sphinx] 검색 엔진 Sphinx 3부 - SphinxQL 사용과 설정

반응형

소개



2부에서는 간단한 예제를 통해 Sphinx를 활용한 검색을 해봤다. (실제로 검색은 아니고 출력일 뿐이었다!!)


이제 SphinxQL을 통해 검색을 하고, 원하는 결과값을 가져오도록 환경설정값을 변경하도록 해보자.

여기서는 2부에서 사용한 test.sql 예제를 가지고 검색해보도록 한다.




간단한 검색



MySQL에서 검색시에는 필드명='찾을값' 형식으로 사용했었다.

하지만 SphinxQL에서는 MATCH() 라는 것을 사용한다.


아래처럼 검색을 하도록 하자.

SELECT * FROM zipcode WHERE MATCH('홍성');





검색한 결과가 나타났다.
이처럼 MATCH()를 사용하면 되는데, 꼭 '' 사이에 검색어를 넣도록 한다.

결과를 보면 20개의 검색결과 값을 가져온 것을 볼 수 있다.
20개 이상의 결과값을 원하면 LIMIT를 사용하면 된다.

SELECT * FROM zipcode WHERE MATCH('홍성') LIMIT 100; 



이제 2개 이상의 단어 검색을 해보자

SELECT * FROM zipcode WHERE MATCH('홍성 갈산'); 




이처럼 여러개를 검색 할 수 있다. 하지만 아래처럼 띄어 쓰기 없이 다시 입력해 보자

SELECT * FROM zipcode WHERE MATCH('홍성갈산'); 





결과값이 동일하다는 것을 알 수 있다.

이것이 바로 n-gram 검색방식의 특징이다. 

'홍성갈산', '갈산홍성', '홍산갈성', '갈홍산성' 등등 글자 순서를 변경해도, 동일한 검색 결과를 가져오는 것을 알게 될 것이다.







구문 검색 (Parse Search)



일단 아래처럼 검색해보자

SELECT * FROM zipcode WHERE MATCH('서산');




검색해보면 서울도 나오고, 전북도 나온다.

전북이 나온 Row를 보면 

전북 전주시 완학동 81 라고 되어있다. 즉 서와 산자가 총 4개 포함되어 있기 때문에 상위 결과값으로 나온것이다. 

1위로 나온 Row는 5자가 포함되어 있음을 학인 할 수 있다. 

이처럼 일치하는 검색이 몇개이고, 얼마나 연관성이 있는지에 따라 weight값이 결정이 되면 가장 높은 weight값 순으로 정렬되어 검색 결과가 나오는 것이다.


그럼 이제 구문으로 검색하기 위해 검색어 양 옆에 따옴표를 붙이도록 하자.

SELECT * FROM zipcode WHERE MATCH('"서산"'); 





원하는 값이 나오는 것을 알 수 있다.
이처럼 쌍따옴표로 검색어를 감싸주면 구문으로 검색이 된다.
응용으로 아래처럼 검색어 2개를 각각 따옴표로 처리해주고, 쉼표 없이 검색해주면 원하는 값을 쉽게 찾을 수 있게된다.

SELECT * FROM zipcode WHERE MATCH('"서산" "고북"'); 







필드 검색 (Field Search)


공주라고 검색해보자

SELECT * FROM zipcode WHERE MATCH('"공주"');




모든 필드에서 공주라는 검색어를 찾게된다.


만약 내가 원하는 것이 suburb에 있는 공주라고 한다면 필드명을 명시해주면 된다.

필드 검색시에는 필드명 앞에 @를 붙이고 한칸 뛰면 된다.

SELECT * FROM zipcode WHERE MATCH('@suburb "공주"'); 




만약 검색할 필드가 2개 이상이라면 아래처럼 가로안에 필드명을 적고 쉼표로 구분해 주면 된다. 주의할점은 쉼표뒤에 공백이 없어야 한다는 점이다.

SELECT * FROM zipcode WHERE MATCH('@(city,suburb) "공주"'); 




필드별 우선 검색


여러 필드를 검색했을때, 검색된 단어가 가장 많은 데이터부터 나오게 된다.

만약 검색시 title에 일치된 값이 다른 필드에서 일치된 값보다 더 중요한 검색 결과값을 가지고 있기 때문에 상위 결과값으로 출력해 줘야 한다면 필드별로 weight값을 주면 된다.


일단 아래처럼 검색해보자

SELECT title, publisher FROM book WHERE MATCH ('"어린이"');





검색 결과에서 title에 어린이 라는 값이 없음에도 publisher에서 어린이가 있기에 상위로 들어왔다.

책을 찾고 싶은데, 제공사의 책 결과값을 가져오는 것은 검색에 비효율적이다.


아래처럼 weight값을 줘보자.

SELECT title, publisher FROM book WHERE MATCH ('"어린이"') OPTION FIELD_WEIGHTS=(title=2,publisher=1);





필드 title에 2값을 주고, publisher에는 1값의 weight 값을 주었다. (1값은 기본값으로 명시하지 않아도 된다.)

따라서 만약 title에 어린이가 일치하면 weight값이 publisher에서 일치하는 값보다 커지게 되어 검색 결과에 상위로 나온다.


이제 검색시 책 위주로 검색을 하도록 한 것이다.



와일드카드 검색 (Wildcard Search)



만약에 일부분을 검색해야 한다면, 와일드카드를 사용하면 된다.

예를 들면 아래처럼 검색 한다면 오로지 7000값만을 찾게 된다.

SELECT title,price FROM book WHERE MATCH('@price 7000'); 





하지만 와일드카드인 *을 사용해서 아래처럼 검색하면 다음과 같은 결과를 얻을 수 있다.

SELECT title,price FROM book WHERE MATCH('@price *7000'); 





바로 앞에 *을 붙이면 검색어와 일차하는 값과 더불어 *가 붙은 부분에서 일치하는 모든 결과값을 가져오게 된다.

하지만 와일드카드를 사용하려면 sphinx.onf 파일에 설정을 따로 해줘야 한다.

설정을 해주지 않는다면, *값을 인식하지 못하게 된다.


설정은 다음과 같이 한다.


index 인덱스명

{

...

...

charset_table = 0..9..........

...


enable_star=1

min_infix_len=2

#min_prefix_len=2


...

...


enable_star는 와일드카드를 사용한다는 것으로 1값이면 사용한다는 뜻이다.

min_infix_len과 min_prefix_len의 길이는 최소 몇개의 값으로 쪼갤 것인가이다.

예를 들면 test를 2로 쪼개면, te, es, st, tes, est, test 등으로 최소 크기인 2부터 인덱싱을 해두는 것이다.

따라서 만약 *st 라고 검색하면 test가 검색되는 것이다.

infix와 prefix의 차이점은 다음 사이트를 참고한다. 

http://sphinxsearch.com/docs/manual-2.0.8.html#conf-min-prefix-len


참고로, enable_star=1값으로 하면 모든 필드에 적용된다. 만약, 특정한 필드만 와일드카드를 사용한다면 아래처럼 설정 할 수 있다.


infix_fields = price, 등등 필드명

prefix_fields = content, 등등 필드명 


주의할 점으로는 min_infix_len와 min_prefix_len은 동시에 사용 할 수 없으므로 둘중 하나만 선택해야 한다. 

(단, infix_fields 처럼 필드별 설정을 한다면 동시사용 가능)



정렬



MySQL에서 처럼 정렬 기능이 필요 할때 ORDER BY 구문을 사용하면 된다.

하지만 조금 다른점은 반드시 ASC나 DESC 둘중 하나를 반드시 써줘야 한다. 보통 MySQL에서는 ASC를 생략하고 쓰는데, Sphinx에서는 생략을 할 수 없다는 것이다.


이제 아래처럼 정렬해보자.

SELECT * FROM shop ORDER BY price DESC;





가격이 높은 순으로 잘 정렬되어 있다.

그럼 이제 정렬할 필드를 s_price로 바꿔 다시 검색해보자.

SELECT * FROM shop ORDER BY s_price DESC;





분명 price와 s_price는 필드명만 다를뿐 같은 값을 가진 필드이지만, 정렬된 결과값은 서로 다르다.

이제 이런 이유를 잠시 설명해보기 위해 sphinx.conf에 설정된 값을 볼 필요가 있다.



source shop

{

...

...


         sql_query_pre = SET NAMES utf8

         sql_query = SELECT sn, price, price AS s_price, pack_count, item_name, npc_name FROM shop WHERE sphinx_yn='Y'


         sql_attr_bigint         = price

         sql_field_string        = s_price

         sql_field_string        = pack_count

         sql_field_string        = pack_count

         sql_field_string        = item_name

         sql_field_string        = npc_name


...

...

}


Sphinx에서는 크게 2개의 타입이 있는데 Attribute와 Field다.

Attribute : Full-Text 기능 없음. 정렬, 필터 기능 있음.
Field : Full-Text 기능을 지원.

설정을 보면 2개의 타입 sql_arttr_bigint와 sql_field_string이 각각 price와 s_price의 타입으로 선어되어 있다.
price는 Attribute로, s_price는 Field로 선언된 것이다.

따라서 정렬을 할때는 sql_attr_bigint 타입으로 선언된 price로 해야 되는 것이다.
그리고 아래처럼 price로 검색을 한다면 없는 필드라고 에러가 출력된다.

SELECT * FROM shop WHERE MATCH('@price 1000000'); 





하지만, Full-Text를 통한 검색이 안되는 것뿐이다. 즉, MATCH() 함수를 통한 검색이 안되는 것이다.

아래 명령어처럼 일반적인 MySQL 쿼리문처럼 검색을 하면, 가능하다.
따라서 구간 검색이라던지, 날짜 등의 정렬 등이 필요한 경우에 Attribute 타입을 사용하는 것이다.

SELECT * FROM shop WHERE price>= 72 and price <= 200; 





이 방법을 사용함으로서, 특정 구간의 값들이나, 정렬등을 통해 결과값을 구할 수 있다.
따라서 검색할 대상이 어느 필드인지, 검색하지않고 결과값에만 나와도 되는 필드인지, 검색시 어떤 유형의 검색을 허용 할지에 따라, 타입을 잘 골라야 할 것이다.

예를 들면 날짜 데이터의 경우 검색할 필드가 아니고, 결과값에만 출력이 되는 값이라면, 굳이 Attribute 타입이 아닌 Field 타입으로 해도 될 것이다.
하지만, 정렬, 구간 등의 검색이 필요하다면, sql_attr_timestamp로 선언해야 될 것이다.



이상으로 간단한 SphinxQL의 사용법을 사용해 봤다.

더 다양한 옵션과 방법은 Sphinx 홈페이지의 문서를 통해 참조하면 되겠다.


xQL Reference : http://sphinxsearch.com/docs/manual-2.0.8.html#sphinxql-reference

Full-Text Query : http://sphinxsearch.com/docs/manual-2.0.8.html#extended-syntax


참고로 여기서 다룬 xQL은 SphinxAPI를 사용하는 것보다 빠르다고 한다.

다음 4부에서는 인덱싱을 하는 방법을 다루겠다.











-------------------------------------------------------
안녕하세요 님을 위한 공간

-------------------------------------------------------


해당 DB가 없기때문에 직접 테스트는 하지 못하였으며, 정확한 상황을 알 수 없기 때문에 추측의 글을 올립니다.

현재 블로그에 있는 conf파일과 sql문을 이용했을때 검색이 잘 되는 것으로 보아, 이 파일들을 통해 서버에 적용된 값과 비교해보셔야 될것 같습니다.

예를 들면 블로그에 올려진 test.sql에서 shop 테이블의 내용을 "아이폰5 차량용 케이블", "아이폰5 케이블"로 고치고 블로그의 conf파일을 이용해서 테스트한 결과 제대로 검색되어 나오는 것을 확인했습니다. (아래 이미지 참조)






참고로 아래의 글을 남기셨는데요...
---------------------------------------------
스핑크스 검색엔진 db에 붙어서 쿼리를 날려보면 
아이폰5 차량용 케이블,아이폰5 케이블 이런 문자열을 가지는 레코드는 틀림없이 존재하는데 말이죠. 
---------------------------------------------
이렇게 쓰신 뜻은 단순히 select * from xxx으로 데이터가 있는지 확인 하신 거라면 앞서 말씀드린 것처럼 xQL을 사용해서 위 이미지 처럼 정확한 쿼리문을 통해 결과값이 잘 나오는지 먼저 확인해야 합니다.

만약 서버에 적용되있는 값이 위와 같은 쿼리문으로 검색이 안되다면 분명, Sphinx 설정이 잘 못 된 것입니다.
일단 블로그에 있는 기본적인 conf값과 sql로 테스트를 해보시고, 서버에 적용된 값으로 conf값을 하나씩 변경, 다시 인덱싱 하시면서 원인을 찾으시는게 빠를 듯 합니다.
하지만 쿼리문이 결과값을 잘 가져온다면, API에서 문제가 생긴다는 것인데 API에 보내는 쿼리문을 로고로 찍어서 다시 한번 확인하시는게 좋겠습니다.


PS. xQL이 아닌 API 방식을 도입하신것 같은데, API 부분은 경험이 없어 도움을 못드리겠네요. xQL로 오시는 것이 어떠신지 ^^;; 


반응형