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

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