Подтвердить что ты не робот

Как я могу манипулировать значимостью поиска полнотекстового поиска MySQL, чтобы сделать одно поле более "ценным", чем другое?

Предположим, у меня есть два столбца, ключевые слова и контент. У меня есть полный текст для обоих. Я хочу, чтобы строка с foo в ключевых словах имела большую релевантность, чем строка с foo в контенте. Что мне нужно сделать, чтобы заставить MySQL весить совпадения в ключевых словах выше, чем в содержании?

Я использую синтаксис "match to".

РЕШЕНИЕ:

Было возможно сделать эту работу следующим образом:

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 
4b9b3361

Ответ 1

Фактически, использование аргумента 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

Опять же, это происходит только в том случае, если все совпадения ключевых слов более высокие, чем все совпадения только для содержания. Я также сделал предположение, что совпадение как в ключевом слове, так и в содержании - это самый высокий ранг.

Ответ 2

Создайте три полных текстовых индекса

  • a) один в столбце ключевых слов
  • b) один в столбце содержимого
  • c) один для столбца с ключевым словом и содержанием

Затем ваш запрос:

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)

Дело в том, что rel1 дает вам релевантность вашего запроса только в столбце keyword (потому что вы создали индекс только в этом столбце). rel2 делает то же самое, но для столбца content. Теперь вы можете добавить эти два значения релевантности, применяя любое взвешивание, которое вам нравится.

Однако вы не используете ни один из этих двух индексов для фактического поиска. Для этого вы используете свой третий индекс, который находится в обоих столбцах.

Указатель на (ключевое слово, содержимое) управляет вашим отзывом. Ака, что возвращается.

Два отдельных индекса (один по ключевому слову, только по содержанию) контролируют вашу релевантность. И здесь вы можете применить свои собственные критерии взвешивания.

Обратите внимание, что вы можете использовать любое количество разных индексов (или изменять индексы и весы, которые вы используете во время запроса, на основе других факторов, возможно... только поиск по ключевому слову, если запрос содержит слово-стоп... уменьшить взвешивание смещения для ключевых слов, если запрос содержит более трех слов... и т.д.).

Каждый индекс использует дисковое пространство, поэтому больше индексов, больше диска. И, в свою очередь, более высокий объем памяти для mysql. Кроме того, вставки будут занимать больше времени, так как у вас есть больше индексов для обновления.

Вы должны проверить производительность (старайтесь отключить кеш запросов mysql для сравнения, иначе ваши результаты будут искажены) для вашей ситуации. Это не эффективный уровень google, но он довольно прост и "из коробки", и это почти наверняка намного лучше, чем использование "похожих" в запросах.

Я считаю, что это работает очень хорошо.

Ответ 3

Насколько я знаю, это не поддерживается полнотекстовым поиском MySQL, но вы можете добиться эффекта, как-то повторяя это слово несколько раз в поле ключевых слов. Вместо того, чтобы иметь ключевые слова "foo bar", есть "foo bar foo bar foo bar", таким образом, как foo, так и bar одинаково важны в столбце ключевых слов, и поскольку они появляются несколько раз, они становятся более релевантными для mysql.

Мы используем это на нашем сайте, и оно работает.

Ответ 4

Я сделал это несколько лет назад, но без полного текстового индекса. У меня нет подходящего кода (бывший работодатель), но я хорошо помню эту технику.

В двух словах я выбрал "вес" из каждого столбца. Например:

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

Ответ 5

В булевом режиме MySQL поддерживает " > " и "<" оператора, чтобы изменить вклад слова в значение релевантности, присвоенное строке.

Интересно, будет ли что-то подобное работать?

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

Ответ 6

Мне нужно что-то подобное и использовало решение OP, но я заметил, что полный текст не соответствует частичным словам. Поэтому, если "арбуз" находится в ключевых словах или содержании как часть слова (например, watermelonsalesmanager), он не имеет отношения к MATCH и не включается в результаты из-за WHERE MATCH. Поэтому я немного обманул и подстроил запрос OP:

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 (Keywords LIKE '%watermelon%' OR 
  Title LIKE '%watermelon%' OR 
  MATCH(Title, Keywords, Content) AGAINST ('watermelon' IN BOOLEAN MODE)) 
HAVING (keywordmatch > 0 OR contentmatch > 0 OR relevance > 0)  
ORDER BY keywordmatch DESC, contentmatch DESC, relevance DESC

Надеюсь, что это поможет.

Ответ 7

Упрощенная версия, использующая только 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;

Ответ 8

Ну, это зависит от того, что вы точно имеете в виду:

Я хочу строку с foo в ключевых словах иметь большую актуальность, чем строка с foo в содержании.

Если вы имеете в виду, что строка с foo в ключевых словах должна появиться до любой строки с foo в контенте, тогда я сделаю два отдельных запроса, один для ключевых слов и затем (возможно, лениво, только если он запросил) другой в контенте.

Ответ 9

Если метрика только в том, что все совпадения ключевых слов являются более "ценными", чем все совпадения содержимого, вы можете просто использовать объединение с количеством строк. Что-то в этом роде.

select *
from (
   select row_number() over(order by blahblah) as row, t.*
   from thetable t
   where keyword match

   union

   select row_number() over(order by blahblah) + @@rowcount + 1 as row, t.*
   from thetable t
   where content match
)
order by row

Для чего-то более сложного, если вы хотите применить фактический вес к каждой строке, я не знаю, как помочь.