وقتی LIKE '%...%' دیگر جواب نمیدهد
جستوجوی «فرش ابریشم کاشان» در عنوان و توضیحات دهها هزار محصول با LIKE یعنی اسکن کامل و بدون رتبهبندی. ایندکس FULLTEXT (در InnoDB از 5.6) یک ایندکس معکوس از «کلمه ← ردیفها» میسازد و نتیجه را بر اساس ارتباط رتبهبندی میکند.
ALTER TABLE products ADD FULLTEXT INDEX ft_title_desc (title, description);
-- حالت زبان طبیعی: رتبهبندی بر اساس ارتباط
SELECT sku, title, MATCH(title, description) AGAINST ('فرش ابریشم کاشان') AS score
FROM products
WHERE MATCH(title, description) AGAINST ('فرش ابریشم کاشان')
ORDER BY score DESC LIMIT 20;
-- حالت بولی: + الزامی، - ممنوع، * پیشوند، "..." عبارت دقیق
SELECT sku, title FROM products
WHERE MATCH(title, description)
AGAINST ('+ابریشم +کاشان -ماشینی "لچک ترنج"' IN BOOLEAN MODE);
ستونهای داخل MATCH باید دقیقاً با ستونهای یک ایندکس FULLTEXT یکی باشند.
مشکلهای parser پیشفرض با فارسی
- کلمات را با فاصله و علائم جدا میکند؛ برای فارسی کار میکند، اما «فرش» و «فرشها» دو کلمهی مستقلاند (ریشهیابی ندارد).
innodb_ft_min_token_sizeپیشفرض 3 است؛ کلمات دوحرفی مثل «گل»، «نخ» و «قم» اصلاً ایندکس نمیشوند و جستوجویشان هیچ نتیجهای نمیدهد.- فهرست stopword پیشفرض انگلیسی است و کلمات پرتکرار فارسی مثل «و» و «از» را نمیشناسد.
[mysqld]
innodb_ft_min_token_size = 2
ngram_token_size = 2
این متغیرها فقط هنگام راهاندازی خوانده میشوند و پس از تغییرشان باید ایندکس FULLTEXT را حذف و دوباره بسازید.
ngram parser
parser داخلی ngram متن را به تکههای n کاراکتری پشتسرهم میشکند؛ با n=2، «کاشان» میشود «کا، اش، شا، ان». در نتیجه جستوجوی «فرش» در «فرشها» و «فرشینه» هم پیدا میشود و به جداکنندهی کلمه وابسته نیست.
ALTER TABLE products ADD FULLTEXT INDEX ft_title_ngram (title) WITH PARSER ngram;
SELECT sku, title FROM products
WHERE MATCH(title) AGAINST ('ابریشم' IN BOOLEAN MODE);
| روش | مزیت | عیب |
|---|---|---|
| parser پیشفرض | ایندکس کوچک، رتبهبندی معقول | بدون ریشهیابی، مشکل کلمات کوتاه |
| ngram | پیدا کردن بخشی از کلمه، مستقل از جداکننده | ایندکس بزرگ، نتایج نامربوط بیشتر |
| Elasticsearch / Meilisearch | تحلیلگر فارسی، تحمل غلط املایی، facet | یک سرویس دیگر برای نگهداری و همگامسازی |
نکتههایی که کمتر کسی میداند
- برای دیدن اینکه متن واقعاً به چه توکنهایی شکسته شده:
SET GLOBAL innodb_ft_aux_table = 'carpet_shop/products';و سپسSELECT word FROM information_schema.innodb_ft_index_cache;— بهترین راه آزمایش رفتار با نیمفاصله و ی/ک. - قبل از ایندکس کردن، متن را نرمال کنید: ی/ک عربی، ارقام و اعراب. یک ستون
search_text(که برنامه هنگام ذخیره پر میکند یا ستون تولیدشدهی STORED با REPLACE) و FULLTEXT روی آن، نتایج را بسیار بهتر میکند. - تغییرات FULLTEXT در InnoDB فقط پس از COMMIT دیده میشوند؛ در تستی که داخل تراکنش داده درج و بلافاصله جستوجو میکند، نتیجه خالی است.
- قاعدهی معروف «کلمهای که در بیش از ۵۰٪ ردیفها باشد نادیده گرفته میشود» فقط مخصوص MyISAM است و در InnoDB وجود ندارد.
- با حذف و ویرایش زیاد، ایندکس FULLTEXT ردیفهای حذفشده را نگه میدارد؛
SET GLOBAL innodb_optimize_fulltext_only = ON; OPTIMIZE TABLE products;فقط ایندکس متنی را بهینه میکند.