[PostgreSQL] tsvector์ Full Text Search
PostgreSQL์์ ์ ๋ฌธ๊ฒ์(Full Text Search)์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ณธ๋ค.
๋จ์ ํฌํจ ๊ฒ์์ด๋ผ๋ฉด Like์ pg_trgm์ ์จ๋ ์ถฉ๋ถํ์ง๋ง, ๋ ์์ฐ์ค๋ฌ์ด ๋จ์ด ์์ค ์์ฐ์ด ๊ฒ์์ ์ํ๋ค๋ฉด ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ผ ํ๋ค.
์ด ๋ฐฉ๋ฉด์์ PostgreSQL์ Elasticseach/Lucene์ ๋นํ ๋ฐ๋ ์๋์ง๋ง, ์ ๋นํ ์์ค์์๋ ์ธ๋งํ ํ ์คํธ ๋ถ์&๊ฒ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
๋จ, ๋ค๊ตญ์ด์ ๋ํ ์๋งํ ์ฒ๋ฆฌ๋ ์์ฒญ๋ ๊ณ ์ฑ๋ฅ ์ธ๋ฑ์ฑ์ ๊ธฐ๋ํด์๋ ์๋๋ค.
์๊ณ ๊ฐ๋จํ๊ฒ ์์ํ๊ธฐ์ ์ข์ ์ ๋๋ผ์, ์๊ตฌ์ฌํญ์ด ์ด๋ ์ ๋์ธ์ง๋ฅผ ํ์
ํ๊ณ ์ ํํด์ผ ํ๋ค.
ํ ์ด๋ธ & ๋ฐ์ดํฐ ์ ์
๋จผ์ ํ
์ด๋ธ์ ์ ๋นํ ์ ์ํ์.
์ผ๋จ ํ์ํ ๊ฒ์ ํ
์คํธ๋ค. ํ
์คํธ๊ฐ ์์ด์ผ ์ชผ๊ฐ์ ์ธ ์ ์์ ๊ฒ์ด๋ค.
CREATE TABLE search_set (
id bigserial PRIMARY KEY,
user_id bigint NOT NULL,
original_text text NOT NULL,
search_vector tsvector
);
์ ๋นํ ๊น์๋ค.
์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ tsvector๋ผ๋ ์ ์ฉ ํ์ ์ด๋ค. ์ ๊ฑธ ๊ฒ์ ์ต์ ํ์ฉ ์ปฌ๋ผ์ผ๋ก ๋ณ๋ ์ค์ ์ ํด์ผ ํ๋ค.
ํ ํฐํ ๋จ์ (tsvector)
๊ทธ ๋ค์์ ํด์ผํ ๊ฒ์ ํ
์คํธ๋ฅผ ๋จ์ด ๋จ์๋ก ์ชผ๊ฐ๋ ๊ฒ์ด๋ค.
elasticsearch ๋ฑ์์ tokenizer๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ ์ด๊ฑด๋ฐ, ํ ํฐ ๋จ์๊ฐ tsvector๋ผ๋ ํ์
์ ํตํด ํํ๋๋ค.
to_tsvector๋ฅผ ์ฌ์ฉํ๋ฉด ํ
์คํธ๋ฅผ ๊ทธ ์ฆ์ ๋จ์ด์ ๋ฒกํฐ๋ก ๋ณํํ ์ ์๋ค.
์ฌ๊ธฐ์๋ simple tokenizer๋ฅผ ์ฌ์ฉํด์ ๋ฃจํ๋ฅผ ๋๋ ค๋ณด๊ฒ ๋ค.
-- SELECT to_tsvector('ํ ํฐํ ๋ฐฉ์', 'ํ
์คํธ');
SELECT to_tsvector('simple', 'He is good dog');
๊ทธ๋ผ ์ด๋ฐ ์์ผ๋ก, ๋จ์ด๋ฅผ ์ชผ๊ฐ ๋ค์์ ๋จ์ด๋ณ ๊ฐ์๋ฅผ ํ์ํด์ค๋ค.
์๋ฌดํผ ์ ๋นํ ํ์ํ ํ ํฐํ ๋ฐฉ์์ ์ ํํด์, ๋ฒกํฐ๋ฅผ ์ด๊ธฐํํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ค์ํ๋ค.
update search_set
set search_vector = to_tsvector('simple', original_Text)
๋์ถฉ ๋๋ ค๋ง ๋ณด๋ ค๋ฉด ์ด๋ ๊ฒ ํ๋ฉด ๋๊ธด ํ๋๋ฐ, ๋ค๊ตญ์ด์ฒ๋ฆฌ๋ ์ด๋ฐ์ ๋ฐ ์๊ตฌ์ฌํญ์ด ์๋ค๋ฉด ํ ํฐํ๋ฅผ DB ๋ฐ์์ ํ๊ณ tsvector ํํ์๋ง ๋ฃ์ด์ฃผ๋๊ฒ ์ข๋ค.
๋ง์ฝ ์ง์ ํ ํฐํ๋ฅผ ํ๋ค๋ฉด ์ด๋ฐ ์์ผ๋ก ๋ฃ์ด์ค ์ ์๋ค.
select '''openai'':1,3 ''gpt'':2'::tsvector
ํค:์์น1,์์น2, ... ์์ด๋ค.
tsquery๋ฅผ ํตํ ์ฟผ๋ฆฌ ํํ
๋ฐ์ดํฐ๋ฅผ ๋ค ์ธํ
ํ์ผ๋ฉด, ์ด์ ๊ฒ์์ ํ ์ฐจ๋ก๋ค.
์ด๊ฑด ๊ฒ์ ํํ์ ์์ฒด๋ฅผ ๋ tsquery๋ผ๋ ํ๋์ ํ์
์ผ๋ก ์ ๊ณตํ๋ค.
๊ฐ์ฅ ๊ฐ๋จํ ์ฌ์ฉ๋ฒ์, to_tsquery๋ผ๋ ํ ์คํธ ๊ธฐ๋ฐ ์์ฑ ํจ์๋ฅผ ์ฐ๋ ๊ฒ์ด๋ค.
SELECT to_tsquery('simple', 'openai & gpt');
์ด๊ฑด openai์ gpt๊ฐ ๋ชจ๋ ํฌํจ๋ ํ
์คํธ๋ฅผ ์ฐพ๋ ํํ์์ด ๋๋ค.
์ฌ๊ธฐ์๋ ํ ํฐํ ๋งค์ปค๋์ฆ์ด ํฌ๊ฒ ์ค์ํ์ง ์๋ค. ์ ๊ฒ์์ด๋ฅผ ํ ํฐ์ผ๋ก ์ชผ๊ฐ๋ ๊ธฐ์ค์ด ๋ ๋ฟ์ด๋ค.
์ํ๋ค๋ฉด ๋ฆฌํฐ๋ด์ ์ด๋ฐ ์์ผ๋ก ํํ ๊ฐ๋ฅํ๋ค.
๊ฒ์ํ๊ธฐ: ๋จ์ด ํํฐ๋ง ๊ฒ์
๊ฒ์์ ํ ๋๋ @@ ์ฐ์ฐ์๋ก ๋์ ๋ฒกํฐ์ tsquery๋ฅผ ์ ๋ฌํ๋ฉด ๋๋ค.
๋ฒกํฐํ๋ @@ ์ฟผ๋ฆฌ

A. AND ๊ฒ์: &
AND ์ ๊ฐ์ ๊ฒ๋ ์ํ๋๋๋ก ๋ถ์ฌํ ์ ์๋ค.
&๋ฅผ ๋ถ์ด๋ฉด ๋จ์ด๊ฐ ์ ๋ถ ์กด์ฌํ๋ ๊ฒ๋ง ์ฐพ๋๋ค.
์๋ฌ๋ฉด ์ด์ ์นํจ๊ณผ ํธ๋ ๋๊ฐ ์ ๋ถ ํฌํจ๋ ๋ฒกํฐ๋ง ํํฐ๋ง์ด ๋๋ค. &๊ฐ AND ์ฐ์ฐ์๋ค.
B. OR ๊ฒ์: |
| ๋ฅผ ๋ฃ์ผ๋ฉด OR ๊ฒ์๋ ๋๋ค. ์ด๊ฒ๋ ์ธ๋ฑ์ค๋ฅผ ๊ฑธ๋ฉด ์ ํ๋ค.

C. Phrase ๊ฒ์: <->
phrase๋ ํน์ ๋จ์ด ๋ญ์น๊ฐ ์ฐ์์ ์ผ๋ก ๋ฑ์ฅํ๋ ๊ฒ์ ์ํ ์กฐ๊ฑด์ด๋ค.
๊ทธ๋ฌ๋๊น, A ๋จ์ด ๋ค์ ๋ฐ๋ก B ๋จ์ด๊ฐ ์ด์ด์ง๋ ๊ฒ๋ง ๋ณด๊ณ ์ถ๋ค๋ฉด A <-> B์ ํํ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋๋ฆด ์ ์๋ค.
D. Distance ๊ฒ์:
๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ ๊ฒ์์ด๋ ๊ฒ์ด ์๋ค. ์ด๊ฑด phrase๋ณด๋ค ํ์ฅ๋ ๊ตฌ์กฐ์ธ๋ฐ, ๋จ์ด ์ฌ์ด์ ๊ฑฐ๋ฆฌ๊ฐ ์ข ์์ด๋ ํฌํจ์ด ๋๊ฒ ํ๋ ๊ฒ์ด๋ค. ๊ทธ ๊ฑฐ๋ฆฌ๋ฅผ ์ง์ ํ ์ ์๋ค.
select
to_tsvector('simple', '๋นจ๊ฐ์ ๋
ธ๋์') @@ to_tsquery('simple', '๋นจ๊ฐ์ <2> ๋
ธ๋์') as _0
, to_tsvector('simple', '๋นจ๊ฐ์ ๋ก ๋
ธ๋์') @@ to_tsquery('simple', '๋นจ๊ฐ์ <2> ๋
ธ๋์') as _1
, to_tsvector('simple', '๋นจ๊ฐ์ ๋ก ๋ก ๋
ธ๋์') @@ to_tsquery('simple', '๋นจ๊ฐ์ <2> ๋
ธ๋์') as _2

E. Prefix ๊ฒ์: :*
๊ธฐ๋ณธ์ ์ผ๋ก ๊ฑฐ์ ๋ชจ๋ ๊ฒ์์ ๋จ์ด(ํ ํฐ) ์์ค์ Exact match๋ค.
ํ์ง๋ง tsvector๋ Prefix ํฌํจ ๊ฒ์ ๋ํ ์ง์ํ๋ค. ๊ทธ๋ฅ "์นํจ"๋ง ๋ฃ๋๋ค๋ฉด ์นํจ ๋จ์ด๊ฐ ์๋ ๊ฒ๋ง ๋งค์นญ๋์ง๋ง, :*๋ฅผ ๋ถ์ด๋ฉด ์นํจ์ผ๋ก ์์ํ๋ ๋จ์ด๋ค๋ ํจ๊ป ๋งค์นญ๋๋ค.

Score๋ก ์ ๋ ฌํ๊ธฐ
๋จ์ด์ ํฌํจ ๊ฐ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ญํน์ ํ๊ฑฐ๋ ํ ์๋ ์๋ค.
ํต์ฌ์ to_rank์ to_rank_cd๋ผ๋ ํจ์๋ค.
์ด๊ฑด ์ฟผ๋ฆฌ ํํ์์ ๊ธฐ๋ฐ์ผ๋ก, ์ผ๋ง๋ ๋จ์ด๊ฐ ๋งค์นญ๋๋์ง๋ฅผ ๊ณ์ฐํ๋ค.
ts_rank(tsvector, tsquery)

๋น์ฐํ ๋จ์ด๊ฐ ๋ง์ด ๋งค์นญ๋ ์๋ก ์ ์๊ฐ ๋์์ง๊ณ , ์๋ค๋ฉด 0์ ์ด ๋์จ๋ค.
to_rank_cd๋ to_rank์ ๋น์ทํ๊ธด ํ๋ฐ, ๊ฒ์์ด๋ค๋ผ๋ฆฌ ๊ฐ๊น์ธ์๋ก ๋ ํฐ ์ ์๊ฐ ๋์จ๋ค.
๊ทธ๋ฌ๋๊น, "์ผ์ฑ์ ์"์ "ai"๋ฅผ AND ์กฐ๊ฑด์ผ๋ก ๋ฃ์์๋, ๋ฐ์ดํฐ์ ๊ทธ 2๊ฐ ์ฌ์ด์ ๋จ์ด๊ฐ ์ ์์๋ก ์ ์๊ฐ ์ปค์ง๊ณ , ๋จ์ด๊ฐ ๋ง์์ ๋ฉ์ด์ง์๋ก ์ ์๊ฐ ์์์ง๋ค.
์ค์ ์ฌ์ฉ๋ก์์๋ ์ผ๋ฐ ๋ฒ์ ๊ณผ cd ๋ฒ์ ๋ชจ๋ ์ ์ฉํ๋ค.
์๋ฌดํฐ ์ ๊ฑธ ์ด์ ์ ์ ํ ORDER BY ์ ์๋ง ๋ถ์ด๋ฉด ์ ๋ ฌ์ด ๋๋ค.

๊ทผ๋ฐ, ์ฑ๋ฅ์ ์ผ๋ก ์ฃผ์ํด์ผ ํ๋ ๋ถ๋ถ์ด ์๋ค.
ts_rank๋ก ์ ๋ ฌ์ ๊ฑฐ๋๊ฑด ํ์ ํ tsvector ์ธ๋ฑ์ฑ์ ๋์์ด ์๋๋ค. ์ด์ฉ ์ ์๋ค. tsvector์ฉ ์ธ๋ฑ์ค๋ ํํฐ๋ง์ฉ์ด์ง, ์ ๋ ฌ์ด๋ ๊ฐ์ค์น๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ์ค์ ๋ก ์ธ๋๋ ๋จผ์ ํํฐ๋ง์ ํ๊ณ ํ์ฒ๋ฆฌ๋ก ์ ๋ ฌ์ ํ๋ ํธ์ด ์ข๋ค. ์๊ทธ๋ฌ๋ฉด 0์ ์ง๋ฆฌ๋ ์ ๋ถ ๊ณ์ฐํ๊ณ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค์ ์ฐ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ผ ์ด๋ ๊ฒ ๋น ๋ฅด๊ฒ ํ๋ค. ์นํจ์ด ํฌํจ๋ ๊ฒ๋ง ๋ถ๋ถ์ ์ผ๋ก ๋ก๋ํ ๋ค์์ ์ ๋ ฌ์ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
Gin ์ธ๋ฑ์ค
tsvector ํ์
์ ์ ์ฉ ์ธ๋ฑ์ค ํ์์ ์ง์ํ๋ค. GIN๊ณผ Gist 2๊ฐ์ง๊ฐ ๊ฐ๋ฅํ๋ฐ, ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ์๋ ์์ค์ด ์๋ GIN์ ๊ถ์ฅํ๋ค.
์ด๊ฑด ์ผ๋ฐ ์ปฌ๋ผ ์กฐ๊ฑด๊ณผ ํผํฉํด์ ์ปค๋ฒ๋ง์ผ๋ก ์ธ ์๋ ์๋ค.
CREATE INDEX idx_search_set_vector
ON search_set
USING gin (search_vector);
์ด๋ ๊ฒ ๋ง๋ค๋ฉด ๋๋ค. ์ด๊ฒ ํ
์คํธ๊ฐ ํฌ๋ฉด ๋น๋ํ์์ด ๊ฝค ๋ฌด๊ฒ๋ค.
์์ญ๋ง ํ ์ ๋์ธ๋ฐ๋ ๊ณ ์ฑ๋ฅ ์ฅ๋น์์ ๋ช๋ถ์ด ๊ฑธ๋ ธ๋ค.
๊ทธ๋ผ ์๋ ์ด ๋จ์๋ก๋ ๊ฑธ๋ฆฌ๋ ๊ฒ์ด,
(์ฌ๋งํด์๋) ๋ฐ๋ฆฌ์ด ๋จ์๋ก ์ฒ๋ฆฌ๊ฐ ๋ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ฑด tsvector @@ tsquery ํํ์ ๋จ์ ํํฐ๋ง ์กฐ๊ฑด์๋ง ์ธ๋ฑ์ฑ์ด ๋๋ค. ์์์ ์ธ๊ธํ rank ๊ธฐ๋ฐ ์ ๋ ฌ ๊ฐ์๊ฑด ์ต์ ํ์ ๋์์ด ์๋๋ค.
์ฐธ๊ณ ๋ก ์ธ๋ฑ์ค ํฌ๊ธฐ๊ฐ ์ข ํฌ๋ค.
๋์คํฌ ์๋ฐ์ด๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ๋ฅผ ๊ณ ๋ คํ๊ธด ํด์ผ ํ๋ค.
Gist ์ธ๋ฑ์ค (๋น์ถ์ฒ)
tsvector๋ Gist ๊ธฐ๋ฐ์ ์ธ๋ฑ์ค๋ ์ง์ํ๋ค.
์ด๊ฑด tradeoff๊ฐ ํ์คํ๊ฒ ์๋ค.
์ฅ์ : ์ธ๋ฑ์ค ๋น๋๊ฐ ๋น ๋ฅด๊ณ ์ธ๋ฑ์ค ํฌ๊ธฐ๊ฐ ์์ ๋์
๋จ์ : ์๋๊ฐ ๋๋ฆฌ๊ณ , ์ธ๋ฑ์ฑ ๋จ๊ณ์์ ์์ค์ด ๋ฐ์ํ๋ค.
์ด๊ฒ ์ข ๋ฏธ๋ฌํ๋ฐ, ์ธ๋ฑ์ฑ์ ํ ๋ "์นํจ" ๊ฒ์์ ํด๋ "์นํจ"์ด ์๋ ํ์ด ๋ฐํ๋ ์ ์๋ค๋ ๊ฒ์ด๋ค. ๋ฌผ๋ก ๊ทธ๊ฒ ์ฌ์ฉ์์๊ฒ๊น์ง ๊ฐ์ง ์๊ณ , DB ๋ด๋ถ์ ์ผ๋ก ์ฒดํฌ๋ฅผ ํด์ ๊ต์ ์ ํ๋ค.
๋ค๋ง ๋ถํ์ํ ์ค์บ์ด๋ ๋ฐ๋ณต์ด ๋ฐ์ํด์ ๋นํจ์จ์ ์ผ๋ก ๋์ํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ ํ์ฌํญ ๋ฐ ํ๊ณ
tsquery&tsvector๋ ๊ธฐ๋ฅ์ ์ธ ์ ํ์ฌํญ์ด๋ ํ๊ณ๊ฐ ์ข ์๋ค.
1. ํ ํฐ๋น ๊ธธ์ด์ ํ
tsvector๋ฅผ ์์ฑํ ๋, ํ ๋จ์ด(ํ ํฐ) ๋น ๊ธธ์ด ์ ํ์ด ์๋ค.
๋ณดํต 2048์๊ฐ ์ ํ์ด๊ณ , 2048์ ์ด์์ ํ ํฐ์ tsvector๋ก ๋ณํ๋์ง ์๊ณ ๋ฒ๋ ค์ง๋ค. ์ค๋ฅ๋ ์๋จ๊ณ ๊ฒฝ๊ณ ๋ง ๋ฌ๋ค.

2. ๋ณต์กํ ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์ ๋ถ๊ฐ๋ฅ
Elasticsearch ๊ฐ์ ์ ๋ฌธ ์์ง์์๋ BM25 ๊ฐ์ ์ฌ๋ฌ๊ฐ์ง ๋ณ์น์ ์ธ ๊ฐ๋ ์ ํตํด ์ธ์ฌํ ์ต์ ํ๋ฅผ ์ ๊ณตํ๋๋ฐ, PostgreSQL์๋ ๊ทธ ์ ๋๊น์ง๋ ์๋ค. ๋๋ฌด ๋ง์ด ๋ฐ๋ผ๋ฉด ์๋๋ค.
3. ์ ๋ ฌ ์ต์ ํ ๋ถ๊ฐ๋ฅ
์์ ์ธ๊ตฝํ๋ฏ์ด, ์ค์ฝ์ด ์ ๋ ฌ์ ์ธ๋ฑ์ค๋ฅผ ์ต์ ์ผ๋ก ํ์ฉํ ์ ์๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ค์บํ๊ณ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฐ ๋ค์ ์ ๋ ฌํ๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๋์ํ๋ค.
4. ๋ณตํฉ์ ์ธ ํํฐ๋ง ์ต์ ํ ๋ถ๊ฐ๋ฅ
Elasticsearch ๊ฐ์ ๊ฒฝ์ฐ์๋ ํ
์คํธ ๊ฒ์์ ๋ํด์ ์ฌ๋ฌ๊ฐ์ง ๋ค์ํ ์ปฌ๋ผ์ผ๋ก ์กฐ๊ฑด์ ๊ฑธ์ด๋ ๊ธฐ๊น๋๊ฒ ์ต์ ํ๋ฅผ ์ ํด์ฃผ๋๋ฐ, PostgreSQL์์๋ ๊ทธ ์ ๋๋ฅผ ๊ธฐ๋ํ๊ธด ์ด๋ ต๋ค.
BTree ๋ฐ๋ก ๋ง๋ค์ด๋๋ฉด Bitmap ์ต์ ํ๋ฅผ ํ๊ธด ํ๋๋ฐ, Elasticsearch์ ๋นํ๋ฉด ์ฑ๋ฅ ์์ค์ด ๋ง์ด ์ณ์ง๋ค.
5. Tokenizer ์ง์ ๋ถ์ค
์์ด ์ ๋๊น์ง๋ ๋ด์ฅ ์์ง์ด๋ ๊ธฐ๋ณธ ํ์ฅ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ฐ, ๊ทธ ์ด์์ ์๋๋ค. ํ๊ตญ์ด๊ฐ์๊ฑด ํฌ๊ธฐํ๋๊ฒ ๋ง๋ค. ์์ ๋ถ๊ฐ๋ฅํ๊ฑด ์๋๊ณ , ๋ค๊ตญ์ด์ ๋ํ ์ง์์ด ํ์ํ๋ค๋ฉด ๋ฐ๊นฅ์์ ๋ถํดํด์ ์ง์ ๋ฃ์ผ๋ฉด ๋๊ธด ํ๋ค.
6. ๋จ์ด ๋ฑ์ฅ ํ์๋ ์ฒ๋ฆฌ ๋ถ๊ฐ
tsquery๋ ๊ธฐ๋ฅ์ด ์ข ์ ํ์ ์ด๋ค. ํน์ ๋จ์ด๊ฐ ํฌํจ๋๋์ง ์ฌ๋ถ๋, ๋์จํ ์ค์ฝ์ด ์ ๋๋ ๊ณ์ฐํ ์ ์์ด๋ ํน์ ๋จ์ด๊ฐ ๋ช๋ฒ ๋ฑ์ฅํ๋์ง๋ฅผ ์กฐํํ๊ฑฐ๋ ์กฐ๊ฑด์ผ๋ก ๊ฑฐ๋๊ฑด ์ด๋ ต๋ค.
์ฐธ์กฐ
https://www.postgresql.org/docs/current/textsearch.html
https://www.postgresql.org/docs/current/textsearch-controls.html
https://www.postgresql.org/docs/current/textsearch-tables.html
https://www.postgresql.org/docs/current/textsearch-dictionaries.html
https://www.postgresql.org/docs/current/textsearch-indexes.html
https://www.postgresql.org/docs/current/datatype-textsearch.html
https://www.postgresql.org/docs/current/textsearch-intro.html