[Qdrant] Full Text Filtering
Qdrant๋ ํ
์คํธ ๊ฒ์์ ๋ํ ๊ธฐ๋ฅ๋ ๊ฝค ์ ์ ๊ณตํ๋ค.
๊ธฐ๋ฅ์ ๋ฒ์๊ฐ Elasticsearch๋ณด๋ค๋ ๋ชปํ์ง๋ง, ๊ฐ๋ฒผ์ด ์ฌ์ฉ์ฌ๋ก์์๋ ์ธ๋งํ๋ค.
qdrant์ Full Text Filtering์ Payload Index์ ํ๊ฐ์ง ๋ณ์ข ์ผ๋ก์ ์ง์๋๋ค.
๋จ์ด ๋ถ๋ฆฌ (Tokenizer)
Qdrant๋ Elasticsearch์ ๋ง์ฐฌ๊ฐ์ง๋ก, ํ ์คํธ๋ฅผ ํํ์ ๊ธฐ๋ฐ์ผ๋ก ๋๋ ์ inverted index ๊ตฌ์กฐ ๊ธฐ๋ฐ์ผ๋ก ๋น ๋ฅด๊ณ ํ์ง ๋์ ๊ฒ์์ ์ํํ ์ ์๋ค.

์ด๋ฅผ ์ํด ์ง์๋๋ tokenizer ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
whitespace - ๊ณต๋ฐฑ ๊ธฐ๋ฐ ๋ถ๋ฆฌ
word - ๊ณต๋ฐฑ ๋ฐ ๊ตฌ๋์ , ํน์๋ฌธ์ ๊ธฐ๋ฐ ๋ถ๋ฆฌ
prefix - word์ ๋์ผํ ์กฐ๊ฑด ํ์ ์ ๋์ฌ ๊ฒ์์ฉ ๋ถ๋ฆฌ (red => r, re, red)
multilingual - ์์ฐ์ด์ ๋ฌธ์ฅ ๊ตฌ์ฑ์์๋ฅผ ํ์
ํด์ ํํ์ ๋ถ์ (๋ค๊ตญ์ด ๊ฐ๋ฅ)
์์์ ์๋๋ก ๊ฐ์๋ก ์ฌ์ธํ ํ ํฐํ๊ฐ ๊ฐ๋ฅํ๋ ๊ทธ๋งํผ ์ค๋ฒํค๋๊ฐ ์ฆ๊ฐํ๋ค.
๋จ์ํ ์ฌ์ฉ์ฌ๋ก๋ผ๋ฉด word ์ ๋๋ก ์ถฉ๋ถํ ํ
์ง๋ง, ํ๊ตญ์ด์ ๋ํ ์์ฐ์ด ๋ถ์ ๊ฐ์๊ฑธ ํด์ผํ๋ค๋ฉด multilingual์ ์ฌ์ฉํด์ผ ํ๋ค.
multilingual๋ ๋ด๋ถ์ ์ผ๋ก meilisearch ํ์์ ๋ง๋ ํํ์ ๋ถ์๊ธฐ๋ฅผ ์ฌ์ฉํ๋ค.
์ด๊ฑด ํ์ฌ ๋ผํด๋ฌธ์, ์ค๊ตญ์ด, ํ๊ตญ์ด, ํ๊ตญ์ด, ์ผ๋ณธ์ด, ์๋์ด ๋ฑ์ ์ง์ํ๋ค.
์ง์ ๋ชฉ๋ก์ ๋ํ ์์ธ ๋ด์ฉ์ ์๋ ํ์ด์ง๋ฅผ ์ฐธ์กฐํ๋ฉด ๋๋ค.
https://github.com/meilisearch/charabia (์ผ๋ณธ์ด ์ ์ธ)
https://github.com/daac-tools/vaporetto (์ผ๋ณธ์ด ํ์ )
ํ ์คํธ ๋ฐ์ดํฐ ์ธํ
ํ๋ฒ ์ง์ ๋ฐ์ดํฐ ๊น๊ณ ์ฟผ๋ฆฌ ๋ ๋ ค๋ณด๋ฉด์ ๋์์ ํ์ธํด๋ณด์.
๋ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ๊น์๋ค.
PUT collections/testdb
{
"vectors": {
"size": 4,
"distance": "Euclid"
}
}
PUT collections/testdb/points
{
"points": [
{
"id": 1,
"vector": [1,1,1,1],
"payload": {
"name": "red banana"
}
},
{
"id": 2,
"vector": [1,2,1,1],
"payload": {
"name": "red ramen"
}
},
{
"id": 3,
"vector": [1,2,1,3],
"payload": {
"name": "blue banana"
}
},
{
"id": 4,
"vector": [1,2,2,1],
"payload": {
"name": "blue wine"
}
},{
"id": 5,
"vector": [1,4,2,1],
"payload": {
"name": "red ramen"
}
},
{
"id": 6,
"vector": [3,2,1,1],
"payload": {
"name": "red chip bag"
}
},{
"id": 7,
"vector": [2,2,1,1],
"payload": {
"name": "golden record"
}
},
{
"id": 8,
"vector": [2,2,2,2],
"payload": {
"name": "gucci bag"
}
},{
"id": 9,
"vector": [2,1,1,1],
"payload": {
"name": "awesome chicken noodle"
}
},
{
"id": 10,
"vector": [1,2,3,4],
"payload": {
"name": "red noodle"
}
}
]
}๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
ํ ์คํธ ๊ฒ์์ ์ฐ๋ ค๋ฉด ์ธ๋ฑ์ค๋ฅผ ๋จผ์ ๋ง๋ค์ด์ผ ํ๋ค.
๋ค๋ฅธ ๊ธฐ๋ณธ ํํฐ๋ง๋ค์ ์ธ๋ฑ์ค๊ฐ ์์ด๋ ์ฑ๋ฅ์ด ๋๋ฆด๋ฟ ๋์์ ํ์ง๋ง, text ๊ฒ์์ ์ ์ฉ text ์ธ๋ฑ์ค๊ฐ ๋ฐ๋์ ์กด์ฌํด์ผ ํ๋ค. ์์ฑ ๋ฐฉ์ ์์ฒด๋ ๋ค๋ฅธ payload index๋ค๊ณผ ๋น์ทํ๋, ์ต์ ์ด ๋งค์ฐ ๋ง๋ค๋๊ฒ ์ฐจ์ด์ ์ด๋ค.
PUT /collections/์ปฌ๋ ์
๋ช
/index
{
"field_name": "ํ๋๋ช
",
"field_schema": {
"type": "text",
"tokenizer": "word"
}
}

text ๊ฒ์ (๋ฌธ์ฅ ๋จ์ด ๋ฌด์กฐ๊ฑด ํฌํจ)
๋ง๋ค๊ณ ๋๋ฉด, ์ด์ filter ๋ฑ์ ํตํด์ ํ
์คํธ ๊ฒ์์ ์ํํ ์ ์๊ฒ ๋ ๊ฒ์ด๋ค.
์ฌ์ฉ๋ฒ ์์ฒด๋ ๋จ์ํ๋ค. ๊ทธ๋ฅ text ์ ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
POST /collections/testdb/points/query
{
"with_payload": true,
"filter": {
"must": [
{
"key": "name",
"match": {
"text": "red"
}
}
]
}
}
๊ทธ๋ฌ๋ฉด ์์ ๊ฐ์ด ํด๋น token(red)๊ฐ ํฌํจ๋ ๊ฒ๋ค์ ํํฐ๋งํด์ค ๊ฒ์ด๋ค.
๋จ, text ํํฐ๋ ๋จ์ด๊ฐ ๊ฒน์น๋ ๋ชจ๋ ๊ฒ์ ๊ฐ์ ธ์ค์ง ์๋๋ค. ํ์ฌ ๊ฒ์์ด์ ํฌํจ๋ ๋ชจ๋ ๋จ์ด๊ฐ ํฌํจ๋๋ ํญ๋ชฉ์ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์, ๋์จํ ๊ฒ์์ด ๋ถ๊ฐ๋ฅํ๋ค.
text_any ๊ฒ์ (๋ฌธ์ฅ ๋จ์ด 1๊ฐ ์ด์ ํฌํจ)
๋จ์ด์ ๋ํด OR ๊ฒ์์ ํ๋ ค๋ฉด text ๋์ text_any๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ด๊ฑด ๊ฒ์์ด๋ฅผ ํ ํฐ์ผ๋ก ๋ถ๋ฆฌํ ๋ค์์, ๊ฐ ํ ํฐ์ด ํ๋๋ผ๋ ํฌํจ๋๋ค๋ฉด ๋ชจ๋ ๊ฐ์ ธ์จ๋ค.
phrase ๊ฒ์ (๋ฌธ์ฅ ์์ ํฌํจ)
๋ณด๋ค ์๊ฒฉํ ๊ฒ์์ด ํ์ํ๋ค๋ฉด phrase๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์ด๊ฑด ํ์ฌ ๊ฒ์์ด๊ฐ ๋ณํ ์์ด ํฌํจ๋๋ ๊ฒ๋ง ๊ฒ์ฌํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด ์ธ๋ฑ์ค์ ๋จผ์ ์ต์
์ ํ์ฑํํด์ผ ํ๋ค.

๋ค๋ฅธ text ๊ฒ์์ ์์์ ์๊ด์์ด ๊ฐ๋ณ ๋จ์ด์ ์ผ์น ์ฌ๋ถ๋ง ๋ณด์ง๋ง

phrase๋ ์์ฒ๋ผ ์์๋ฅผ ๊ผฌ๋ ๊ฒ์ด ํตํ์ง ์๋๋ค. ๊ฒ์์ด์ ๋ฌธ์ฅ์ด ์ ํํ๊ฒ ํฌํจ๋์ด์ผ ํ๋ค.
Text ์ธ๋ฑ์ค ์ต์ ๋ค
ํ ์คํธ ๊ฒ์์ ๋ํ ์ธ๋ถ์ ์ธ ํ๋์, ์ธ๋ฑ์ค๋ฅผ ๋ง๋๋ ์์ ์ ์ค์ ํด์ผ ํ๋ค.
์ง์ํ๋ ์ ์ฒด ์ต์ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
lowercase๋ ๋์๋ฌธ์ ๊ตฌ๋ถ์ ๋ํ ์ต์
์ด๋ค.
๊ธฐ๋ณธ๊ฐ์ true๊ณ , ๋ชจ๋ ํ ํฐ์ ์๋ฌธ์๋ก ์ ์ฅํด์ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์๋ ๊ฒ์ด ๊ธฐ๋ณธ ๋์์ด๋ผ๋ ๋ป์ด๋ค.
ascii_folding์ ๋ผํด ๋ณํ ๋ฌธ์๋ค์ ๋ญ์น๋ ๊ฒ์ ๋ํ ์ต์
์ด๋ค.
๊ธฐ๋ณธ๊ฐ์ false๋ค. ํ์ฑํํ๋ค๋ฉด ๋ผํด ๋ฌธ์์์ ์๋ผ์ฐํธ๋ ์
์ผํธ ๋ฑ์ ์ ๊ฑฐํ ์ฑ๋ก ์ ์ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ stemmer๋ ํ
์คํธ๋ฅผ ํ ํฐํํ ๋ ์ด๊ทผ ๋จ์๋ก ๊ฐ๊ณตํ ์ง๋ฅผ ์ ํํ๋ค.
์๋ฅผ ๋ค์ด, shirts๋ผ๋ ํ ํฐ์ด ๋์ค๋ฉด ๊ทธ๊ฑธ shirt๋ผ๊ณ ์ ์ฅํ๋ ๊ฒ์ด๋ค.
๊ธฐ๋ณธ ์ค์ ์์๋ ์ด๊ทผ ์ถ์ถ์ด ๊บผ์ ธ์๊ธฐ ๋๋ฌธ์, bags๋ฅผ ๋ฃ๋๋ผ๋ bag๊ณผ ์ผ์นํ์ง๋ ์์ ๊ฒ์๋์ง ์๋๋ค.

ํ์ง๋ง ์ด๊ทผ ์ค์ ์ ์ผ ๋ค๋ฉด

ํํ์์ ์ํ์ ๊ธฐ๋ฐ์ผ๋ก ๊ฒ์ํ๊ธฐ์ ์ข ๋ ์ ์ฐํ ๊ฒ์์ด ๊ฐ๋ฅํด์ง๋ค.
ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์ (Scoring)
text ํํฐ๋ง ๋ํ scoring์ ํฌํจํ๋ ๊ฒ์ด ๊ฐ๋ฅ์ ํ๋ ์์ฃผ ๋งค๋๋ฝ๊ฒ ๋์ง๋ ์๋๋ค.
์ด ๋ฒกํฐ ๊ฒ์์ ํ
์คํธ ๊ฒ์ ๊ธฐ๋ฐ์ score๋ฅผ ๋ํ๋ค๊ณ ํ๋ฉด
์ ๋๊ธฐ๋ ๋๋ค.
์ด ๊ฒฝ์ฐ์๋ ํ
์คํธ ํํฐ๋ง์ ๊ฑธ๋ฆฐ ํญ๋ชฉ์ ๊ธฐ๋ณธ 1 score๊ฐ ๋ํด์ง ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ฌธ์ ๋, text, text_any ๋ฑ์ด ๋จ์ด์ ์ผ์น ์ ๋์ ๋น๋กํ score๋ฅผ ๋ฐํํ์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ์กฐ๊ฑด์ด ํ๋๋ผ๋ ๋ง์ผ๋ฉด 1์ ๋ฐํํ๊ณ , ๋ง์ง ์์ผ๋ฉด ์์ 0์ ๋ฐํํ๋ค.
๊ทธ๋์ ์์ ์ผ์น ํ
์คํธ๋ฅผ ๋ฃ๋ , ๋ถ๋ถ ์ผ์น ํ
์คํธ๋ฅผ ๋ฃ๋ ์์ฒ๋ผ ๋ฌด์กฐ๊ฑด ๊ณ ์ ์ ์ 1์ด ๋ฐํ๋๊ณ , ์ ์ฌ๋๋ฅผ ์ ์ ํ ์กฐ์จํ๋ ๊ฒ์ด ์ด๋ ต๋ค.
์ด๊ฑด ํ์ฌ ์์คํ
๊ธฐ๋ฅ์ ํ๊ณ๋ค. ๋ฉ์ธํ
์ด๋ํํ
๋ฌผ์ด๋ณด๋๊น score๊น์ง ์ ์ดํ๋๊ฑด ํ์ฌ๋ก์๋ ๋ถ๊ฐ๋ฅํ ์๊ตฌ์ฌํญ์ด๋ผ๊ณ ํ๋๋ผ.
ํธ๋ฒ์ ์ฐ๋ฉด ๋ฌ์ฑํ ์๋ ์๋๋ฐ, ์ง์ ํ ํฐํ๋ฅผ ํ๋ค์์ ๊ทธ ํํ์์ ์ข
ํฉํด์ ์ฐ์ฐํ๋๋ก ๋ง๋๋ ๊ฒ์ด๋ค.
์ด๋ฐ ์์ผ๋ก ๋ง์ด๋ค.
์ด์์ ์ธ ๋ฐฉ๋ฒ์ ์๋์ง๋ง, ํ์ฌ๋ก์๋ ์ด๊ฒ ๋ฟ์ด๋ค.
์ฐธ์กฐ
https://qdrant.tech/documentation/concepts/indexing/#full-text-index
https://qdrant.tech/documentation/guides/text-search/