web-dev-qa-db-ja.com

MySQLフルテキスト検索の関連性を操作して、あるフィールドを別のフィールドよりも「価値のある」ものにするにはどうすればよいですか?

キーワードとコンテンツの2つの列があるとします。両方にフルテキストインデックスがあります。キーワードにfooが含まれる行に、コンテンツにfooが含まれる行よりも関連性を持たせたい。 MySQLでキーワードの一致をコンテンツの一致よりも重み付けするために何をする必要がありますか?

「一致」構文を使用しています。

解決:

次の方法でこれを機能させることができました:

SELECT *, 
CASE when Keywords like '%watermelon%' then 1 else 0 END as keywordmatch, 
CASE when Content like '%watermelon%' then 1 else 0 END as contentmatch,
MATCH (Title, Keywords, Content) AGAINST ('watermelon') AS relevance 
FROM about_data  
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
HAVING relevance > 0  
ORDER by keywordmatch desc, contentmatch desc, relevance desc 
38
Buzz

実際には、caseステートメントを使用してフラグのペアを作成する方が良い解決策になる場合があります。

select 
...
, case when keyword like '%' + @input + '%' then 1 else 0 end as keywordmatch
, case when content like '%' + @input + '%' then 1 else 0 end as contentmatch
-- or whatever check you use for the matching
from 
   ... 
   and here the rest of your usual matching query
   ... 
order by keywordmatch desc, contentmatch desc

繰り返しになりますが、これは、すべてのキーワードの一致がすべてのコンテンツのみの一致よりもランクが高い場合のみです。また、キーワードとコンテンツの両方での一致が最高ランクであると仮定しました。

20
notnot

3つの全文索引を作成する

  • a)キーワード列に1つ
  • b)コンテンツ列に1つ
  • c)キーワードとコンテンツの両方の列に1つ

次に、クエリ:

SELECT id, keyword, content,
  MATCH (keyword) AGAINST ('watermelon') AS rel1,
  MATCH (content) AGAINST ('watermelon') AS rel2
FROM table
WHERE MATCH (keyword,content) AGAINST ('watermelon')
ORDER BY (rel1*1.5)+(rel2) DESC

ポイントはrel1は、keyword列だけでクエリの関連性を示します(その列にのみインデックスを作成したため)。 rel2も同じですが、content列についてです。これで、これらの2つの関連性スコアを追加して、好きな重みを適用できます。

ただし、実際の検索にはこれら2つのインデックスのどちらも使用していません。そのためには、両方の列にある3番目のインデックスを使用します。

(keyword、content)のインデックスがリコールを制御します。別名、返されたもの。

2つの個別のインデックス(1つはキーワードのみ、もう1つはコンテンツのみ)が関連性を制御します。ここで独自の重み付け基準を適用できます。

さまざまなインデックスをいくつでも使用できることに注意してください(または、他の要因に基づいてクエリ時に使用するインデックスと重み付けを変更する...クエリにストップワードが含まれている場合にのみキーワードで検索する...の重み付けバイアスを減らすクエリに3語を超える場合のキーワード...など)。

各インデックスはディスク領域を消費するため、より多くのインデックス、より多くのディスクが必要です。次に、mysqlのメモリフットプリントが大きくなります。また、更新するインデックスが多いため、挿入には時間がかかります。

状況に応じて、パフォーマンスのベンチマークを行う必要があります(mysqlクエリキャッシュをオフにしてベンチマークを行うように注意してください)。これはグーグルグレードでは効率的ではありませんが、かなり簡単で「すぐに使える」ので、クエリでの「いいね」の使用よりもはるかに優れています。

私はそれが本当にうまくいくと思います。

82
mintywalker

フルテキストインデックスを2つだけ使用したシンプルなバージョン(@mintywalkerから取得したクレジット):

SELECT id, 
   MATCH (`content_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance1,  
   MATCH (`title_ft`) AGAINST ('keyword*' IN BOOLEAN MODE) AS relevance2
FROM search_table
HAVING (relevance1 + relevance2) > 0
ORDER BY (relevance1 * 1.5) + (relevance2) DESC
LIMIT 0, 1000;

これにより、keywordに対して完全にインデックス付けされた列の両方が検索され、一致した関連性が2つの別々の列に選択されます。一致しないアイテム(relevance1とrelevance2は両方ともゼロ)を除外し、content_ft列の重みを増やして結果を並べ替えます。複合フルテキストインデックスは必要ありません。

7
lubosdz

私はこれを数年前に行いましたが、全文索引はありませんでした。便利なコード(以前の雇用主)はありませんが、そのテクニックをよく覚えています。

簡単に言うと、各列から「ウェイト」を選択しました。例えば:

select table.id, keyword_relevance + content_relevance as relevance from table
   left join
      (select id, 1 as keyword_relevance from table_name where keyword match) a
   on table.id = a.id
   left join
      (select id, 0.75 as content_relevance from table_name where content match) b
   on table.id = b.id

ここでお粗末なSQLを許してください。何かを書く必要があったので数年になります。私はこれを頭のてっぺんからやっています...

お役に立てれば!

J.Js

0
Justin James

ブールモードでは、MySQLは ">"および "<"演算子をサポートし、Wordの貢献度を行に割り当てられている関連性の値に変更します。

このようなものがうまくいくのだろうか?

SELECT *, 
MATCH (Keywords) AGAINST ('>watermelon' IN BOOLEAN MODE) AS relStrong, 
MATCH (Title,Keywords,Content) AGAINST ('<watermelon' IN BOOLEAN MODE) AS relWeak 
FROM about_data  
WHERE MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE) 
ORDER by (relStrong+relWeak) desc
0
Tom

私の知る限り、これはMySQL全文検索ではサポートされていませんが、キーワードフィールドでそのWordを何回か繰り返すことで効果を得ることができます。キーワード「foo bar」を使用する代わりに、「foo bar foo bar foo bar」を使用します。これにより、fooとbarの両方がキーワード列内で等しく重要になります。

私たちのサイトでこれを使用すると、機能します。

0
adamJLev