[PostgreSQL] 검색을 위한 Basic Text Matching

Basic Text Matching

PostgreSQL에서 Full Text Search는 @@ 이라는 일치 연산자를 씁니다. tsvector(Document)가 tsquery(Query)와 일치하면 true를 반환하죠.

여기서 어떤 데이터 타입이 먼저 쓰이는지는 중요하지 않습니다. 예시로 보시죠.

SELECT 'legends never die'::tsvector @@ 'legends'::tsquery;
?column?
--------
t

SELECT 'legends'::tsquery @@ 'legends never die'::tsvector;
?column?
--------
t

-- 순서가 바뀌어도 결과는 같습니다.

위의 예시를 보면, tsquerytsvector는 단순히 텍스트가 아니라는 걸 알 수 있습니다.

tsquery는 검색어를 나타내고, 이미 정규화된 렉셈을 포함하고 있습니다. 그리고 AND, OR, NOT, FOLLOWED BY 같은 연산자를 사용해서 복합적인 검색을 수행할 수 있습니다.

tsvector는 검색할 문서를 나타내며, 렉셈의 리스트로 구성되어 있습니다. 이 렉셈은 검색어로부터 파싱된 것이며, 검색어의 위치(Position)를 포함하고 있습니다.

다음 예시는 Tresh와 Lantern이라는 두 단어가 포함된 문서를 검색하는 예시입니다.

SELECT to_tsvector('english', 'Thresh Lantern is a skill of Thresh.') @@ to_tsquery('Thresh & Lantern');
?column?
--------
t

조금 더 복잡한 예시를 보겠습니다.

-- AND, OR, NOT 연산자를 사용한 예시
SELECT to_tsvector('english', 'Thresh Lantern is a skill of Thresh.') @@ to_tsquery('Thresh & (Lantern | Hook) & !Shield');
?column?
--------
t

위 예시는 “Thresh“와 “Lantern” 또는 “Hook“을 포함하지만 “Shield“는 포함하지 않는 문서를 찾습니다.

만약 “Shield“가 위 예시에 포함돼있다면 어떨까요? 결과는 false가 될 것입니다.

SELECT to_tsvector('english', 'Thresh Lantern is a skill of Thresh. Lantern grants a shield to nearby champions.') @@ to_tsquery('Thresh & (Lantern | Hook) & !Shield');
?column?
--------
f

또한 @@ 연산자는 텍스트 입력도 지원해서 간단히 텍스트 자체를 tsvector 또는 tsquery로 명시적으로 변환해서 그냥 건너뛸 수도 있습니다.

tsvector @@ tsquery  -- 전처리된 Document와 전처리된 Query를 비교
tsquery  @@ tsvector  -- 전처리된 Query와 전처리된 Document를 비교
text @@ tsquery     -- text를 자동으로 tsvector로 변환해서 비교
text @@ text     -- 두 텍스트를 각각 tsvector와 plainto_tsquery로 변환해서 비교

FOLLOWED BY 연산자(<->)는 두 단어가 순서대로 인접하게 나오는지 확인합니다.

(<N>) 연산자는 두 단어 사이의 거리를 나타냅니다.

-- "Thresh Hook"이라는 문구가 인접하게 나오나 확인하는 예시
SELECT to_tsvector('Thresh Hook is a powerful skill.') @@ to_tsquery('Thresh <-> Hook');
?column?
--------
t

위 예시는 “Thresh“와 “Hook“이 인접하게 나오는 문서를 찾습니다.

반대의 예시로 보여드리면 이렇게 false가 나옵니다.

SELECT to_tsvector('Hook is used by Thresh.') @@ to_tsquery('Thresh <-> Hook');
?column?
--------
f

추가로 <N> 연산자를 사용해서 두 단어 사이의 거리를 지정할 수 있습니다.

-- 다른 단어가 1개 껴있는 경우
SELECT to_tsvector('Thresh uses his Hook skill.') @@ to_tsquery('Thresh <2> Hook');
?column?
--------
t

phraseto_tsquery

phraseto_tsquery 함수는 여러 단어 사이에 불용어(Stopword)가 포함된 문장을 검색할 때 쓸 수 있습니다.

SELECT phraseto_tsquery('Thresh uses Hook');
    phraseto_tsquery
-----------------------
'thresh' <-> 'use' <-> 'hook'

SELECT phraseto_tsquery('Thresh uses his Hook skill');
    phraseto_tsquery
-----------------------
'thresh' <-> 'use' <2> 'hook' <-> 'skill'

-- <2>: uses와 hook 사이에 정확히 1개의 단어가 껴있다. -> "uses his Hook"

참고자료