Mysql fulltext для нескольких таблиц
Я пытаюсь выполнить полнотекстовый поиск в трех отдельных таблицах и сортировать результаты по релевантности. Во время поиска ответа я узнал, что не могу использовать полнотекстовый поиск в нескольких таблицах. Поэтому я добавил отдельный индекс полного текста для каждого столбца, который я хочу найти. Теперь проблема в том, что я могу выполнить поиск, но я не могу сортировать, как хотелось бы.
Здесь мои таблицы
CREATE TABLE books (
bookID int(11) NOT NULL AUTO_INCREMENT,
title varchar(300) NOT NULL,
authorID int(11) NOT NULL,
FULLTEXT KEY title (title)
)
CREATE TABLE IF NOT EXISTS authors (
authorID int(11) NOT NULL AUTO_INCREMENT,
authorNamevarchar(200) NOT NULL,
FULLTEXT KEY authorName(authorName)
);
CREATE TABLE IF NOT EXISTS chapters (
chapterID int(11) NOT NULL AUTO_INCREMENT,
bookID int(11) NOT NULL,
content longtext NOT NULL,
FULLTEXT KEY content (content)
);
И мой запрос sql. Вот где я застрял.
SELECT *,
MATCH(books.title) AGAINST('$q') as tscore,
MATCH(authors.authorName) AGAINST('$q') as ascore
MATCH(chapters.content) AGAINST('$q') as cscore
FROM books
LEFT JOIN authors ON books.authorID = authors.authorID
LEFT JOIN chapters ON books.bookID = chapters.bookID
WHERE
MATCH(books.title) AGAINST('$q')
OR MATCH(authors.authorName) AGAINST('$q')
OR MATCH(chapters.content) AGAINST('$q')
ORDER BY ???? DESC
Теперь с этим запросом я могу сортировать по названиям, авторам или содержимому. Я хочу сделать это, получить релевантность для всех трех столбцов и упорядочить результаты.
И да, я знаю другие поисковые системы, такие как lucene или sphinx, но я не планирую использовать их сейчас.
Спасибо заранее.
Ответы
Ответ 1
Вы должны иметь возможность добавлять значения tscore, ascore и cscore в предложении ORDER BY.
Попробуйте следующее:
SELECT *,
MATCH(books.title) AGAINST('$q') as tscore,
MATCH(authors.authorName) AGAINST('$q') as ascore,
MATCH(chapters.content) AGAINST('$q') as cscore
FROM books
LEFT JOIN authors ON books.authorID = authors.authorID
LEFT JOIN chapters ON books.bookID = chapters.bookID
WHERE
MATCH(books.title) AGAINST('$q')
OR MATCH(authors.authorName) AGAINST('$q')
OR MATCH(chapters.content) AGAINST('$q')
ORDER BY (tscore + ascore + cscore) DESC
Ответ 2
Решение @Ike Walker велико, однако в моем случае я хотел собрать результаты "один-ко-многим" в одну строку для каждого результата поиска. Riffing на решении @Ike Walker здесь, как я получил работу:
Схема:
T1: Articles
T2: Comments (many comments to one article)
Индексы:
ALTER TABLE articles ADD FULLTEXT title_index (title)
ALTER TABLE articles ADD FULLTEXT body_index (body)
ALTER TABLE comments ADD FULLTEXT comment_index (comment)
SQL:
SELECT
articles.title,
SUM(MATCH(articles.title) AGAINST('$q') +
MATCH(articles.body) AGAINST('$q') +
MATCH(comments.comment) AGAINST('$q')) as relevance
FROM
articles
LEFT JOIN
comments ON articles.id = comments.article_id
WHERE
MATCH(articles.title) AGAINST('$q')
OR MATCH(articles.body) AGAINST('$q')
OR MATCH(comments.comment) AGAINST('$q')
GROUP BY
articles.id
ORDER BY
relevance DESC
Примечание. Если вы хотите добавить вес к каждому полю, вы можете сделать что-то вроде.
SUM((MATCH(articles.title) AGAINST('$q')*3) +
(MATCH(articles.body) AGAINST('$q')*2) +
MATCH(comments.comment) AGAINST('$q')) as relevance
В этом случае заголовок имеет 3x, тело 2x - значение совпадения в комментариях.
Ответ 3
Это зависит от того, что вы хотите отсортировать. Вы можете сортировать по автору, затем заголовок, затем содержимое раздела с этим
ORDER BY MATCH(authors.authorName) DESC ,MATCH(books.title) DESC ,MATCH(chapters.content) DESC
идея состоит в том, что когда вы находите имя автора, это более актуально, чем когда оно найдено в названии, что, в свою очередь, более актуально, чем найти его в тексте главы. Вы также можете сортировать по общей релевантности с помощью
ORDER BY MATCH(authors.authorName) + MATCH(books.title) + MATCH(chapters.content) DESC
но это может привести к нечетным результатам, поскольку текст, отображаемый только в содержании главы, может отображаться перед заголовком.