0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MariaDB の FULLTEXT で「I need a stairlift」を探すと、need だけで当たったページが返ってきます【RAG の参照先がずれた記録】

0
Posted at

自作の WordPress プラグイン Rapls AI Chatbot で、英語の「I need a stairlift」という質問に、昇降機と関係のないページが参照先として出ていました。キーワード検索で実際に効いていた語は need だけでした。MariaDB 10.11 で同じ形を作って測った結果です。

検索語(AGAINST に渡した文字列) 返ってきたページ
need stairlift(修正前のキーワード) Programs / Contact / About Us(3件とも同点 0.0492)
stairlift(修正後のキーワード) 0件
stair lifts Stair Lifts & Vehicle Lifts(3.4199)
you have stair lifts Stair Lifts(3.5783)/ About Us(0.4886)/ Programs(0.1584)
howdoIknow(修正前に「how do I know?」から作られていた語) 0件

(サンプル5ページに対する結果。ページは報告者のサイトではなく、この記事のために作ったもの)

修正前(1.20.2 まで) 修正後(1.20.3)
英語のストップワードは20語 89語。need、you、have などを足した
全部がストップワードの英文は、残りを連結して1語にしていた 英語なら何も検索しない。日本語は従来どおり連結する

先に断っておくと、ここに出てくる仕組みはドキュメントに書いてあることです。MariaDB の Full-Text Index Overview には、InnoDB では3文字未満の語は索引に入らないこと、ストップワードは自然言語モードでは結果に効かないこと、部分一致はしないことが書かれています。半分以上の行に出る語を除く規則は MyISAM/Aria だけ、ともあります。ここで書くのは、それらが重なって、RAG の参照カードが別のページを指したときに何が起きていたかを、手元で測った記録です。

何が起きていたか

報告は WordPress.org のフォーラムに届きました。「I need a stairlift」と聞くと、回答の本文は Stair Lifts & Vehicle Lifts のページから正しく作られる。ところが、回答の下に出る参照カードは、助成制度を説明した Programs というページを指す。何度試しても同じ、という内容です。

参照カードがずれた主因は別にあって、ベクトル検索だけで見つかったページにはカードに出る印が付かない作りでした(1.19.5 で修正)。この記事で扱うのは、その陰で Programs が「キーワードで一致した」扱いになっていたほうです。

プラグインは、質問から語を取り出して、それを MATCH ... AGAINST に渡しています。

$sql = $wpdb->prepare(
    "SELECT post_id, post_type, title, content, url,
            MATCH(title, content) AGAINST(%s IN NATURAL LANGUAGE MODE) as score
     FROM {$table}
     WHERE MATCH(title, content) AGAINST(%s IN NATURAL LANGUAGE MODE)
     ORDER BY score DESC
     LIMIT %d",
    $search_query, $search_query, $limit * 2
);

語の取り出しは、修正前と修正後のコードをそのまま抜き出して、PHP 8.4 で動かしました。

I need a stairlift            old=["need","stairlift"]  new=["stairlift"]
how do I know?                old=["howdoIknow"]        new=[]
Do you have stair lifts?      old=["you","have","stair","lifts"]  new=["stair","lifts"]
What does a stair lift cost?  old=["stair","lift","cost"]  new=["stair","lift","cost"]

修正前のストップワードは what、is、the、how、do、can、tell、me、about、know など20語で、need も you も have も入っていませんでした。

測った環境

MariaDB 10.11.14(Ubuntu 24.04 のパッケージ)
ENGINE=InnoDB / utf8mb4_unicode_520_ci
innodb_ft_min_token_size = 3 / innodb_ft_enable_stopword = ON

テーブルはプラグインの索引テーブルから、検索に関わる列だけを残しました。FULLTEXT は (title, content) です。ページは次の5件です。

post_id title need を含むか
1 Stair Lifts & Vehicle Lifts 含まない
2 Programs 含む
3 Contact 含む(Need help choosing?)
4 About Us 含む
5 Wheelchair Ramps 含まない

need stairlift:stairlift は何も足さない

### AGAINST('need stairlift')
post_id  title     score
3        Contact   0.0492
4        About Us  0.0492
2        Programs  0.0492

AGAINST('need') だけで検索した結果と、3件の顔ぶれもスコアも同じでした。stairlift という語は、どのページにも1語としては存在しないので、スコアに何も足しません。ドキュメントのとおり部分一致はしないので、stair lifts と書かれたページにも当たりません。

そして、need を含む3ページが同点で並びます。

同点なので、先頭は決まっていない

プラグインは上位から使います。同じクエリを LIMIT 1 で3回続けて投げると、こうなりました。

Programs
Programs
Contact

ORDER BY score DESC だけでは、同点の並びは保証されません。報告で Programs が毎回出ていたのは、その環境でたまたまそうだった、というところまでしか言えません。どのページが先頭に来ても、need を含むだけのページであることは変わりません。

LIKE にも進まない

修正前の search_index() は、FULLTEXT が1件でも返せば、そこで戻ります。

$fulltext_results = $this->fulltext_search($table, $query, $limit);
if (!empty($fulltext_results)) {
    return $fulltext_results;
}
return $this->like_search($table, $keywords, $limit);

need で3件返ってくるので、LIKE による補助の検索は走りません。もっとも、LIKE '%stairlift%' も手元では0件だったので、走っても結果は変わりませんでした。

InnoDB のストップワードを数えた

InnoDB の既定のストップワードを数えると36語でした。

a about an are as at be by com de en for from how i in is it la of on or
that the this to was what when where who will with und the www

need も you も have も入っていません。「Do you have stair lifts?」を修正前の取り出し方で投げると、you と have で About Us と Programs が後ろに付いてきます(冒頭の表の4行目)。この質問では Stair Lifts が大差で先頭なので害は小さいのですが、質問の中心の語が索引に無いときは、need のときと同じ形になります。

DB 側の一覧に頼れないので、プラグインは質問から語を取り出す時点で落とすことにしました。1.20.3 で足したのは、代名詞、前置詞、助動詞、そして need、want、looking のような質問の書き出しに来る動詞です。help や support、cost、price、service のように、実際のサイトで意味を持つ語は、あえて残しています。

how do I know? を連結した1語

修正前は、語が全部ストップワードで消えると、残りの文字を空白抜きで連結して1語にしていました。「how do I know?」は howdoIknow になります。

### AGAINST('howdoIknow')
(0件)

LIKE でも0件です。日本語は分かち書きしないので、残った文字をつなげると意味のある語に戻ります。その処理が英語にもかかっていました。1.20.3 では、連結した結果にひらがなかカタカナか漢字が含まれるときだけ連結し、英語は何も検索せずにベクトル検索に任せています。

修正前と修正後で、どこまで変わったか

修正後の stairlift は0件です。キーワード検索では、正しいページを見つけられるようにはなっていません。変わったのは、関係のないページを「キーワードで一致した」として返さなくなったことです。正しいページを見つけるのはベクトル検索の役目で、それが参照カードに出られるようにしたのが 1.19.5 でした。

stair lifts と2語で聞かれれば、修正前も修正後も FULLTEXT で当たります。1語の stairlift と2語の stair lifts の違いを埋めるには、語の正規化か類義語の辞書が要ります。そこまではまだやっていません。

あなたの全文検索では、質問の中心の語が索引に無いとき、何が返ってきますか。

手元で試すコマンド

MariaDB に流すと、冒頭の表と同じ結果が出るはずです。

CREATE DATABASE ft CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
USE ft;
CREATE TABLE wp_raplsaich_index (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  post_id BIGINT UNSIGNED NOT NULL,
  title VARCHAR(255) NOT NULL,
  content LONGTEXT NOT NULL,
  PRIMARY KEY (id),
  FULLTEXT KEY content_fulltext (title, content)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;

INSERT INTO wp_raplsaich_index (post_id, title, content) VALUES
(1,'Stair Lifts & Vehicle Lifts','We install stair lifts for straight and curved staircases, and vehicle lifts for vans and cars. A stair lift carries you safely between floors.'),
(2,'Programs','If you need help paying for equipment, Medicaid waiver programs and local grants may cover part of the cost.'),
(3,'Contact','Need help choosing? Call or email us and we will reply within one business day.'),
(4,'About Us','We have served families who need safe access to their homes since 1998.'),
(5,'Wheelchair Ramps','Modular aluminum ramps for porches, steps and entrances.');

SELECT post_id, title,
       ROUND(MATCH(title, content) AGAINST('need stairlift' IN NATURAL LANGUAGE MODE), 4) AS score
FROM wp_raplsaich_index
WHERE MATCH(title, content) AGAINST('need stairlift' IN NATURAL LANGUAGE MODE)
ORDER BY score DESC;

SELECT value FROM information_schema.INNODB_FT_DEFAULT_STOPWORD;

ふだんはraplsworks.comで、WordPressプラグイン開発やClaude Codeまわりのことを書いています。
https://raplsworks.com/

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?