How to implement a MySQL fulltext search across multiple tables?
You should be able to add the tscore
, ascore
, and cscore
values in the ORDER BY
clause.
Try this:
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
@Ike Walker's solution is great, however in my case I wanted to roll up the one-to-many results into a single row per search result. Riffing on @Ike Walker's solution here's how I got the job done:
Schema:
T1: Articles
T2: Comments (many comments to one article)
Indexes:
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
Note: If you want to add weights to each field you could do something like.
SUM((MATCH(articles.title) AGAINST('$q')*3) +
(MATCH(articles.body) AGAINST('$q')*2) +
MATCH(comments.comment) AGAINST('$q')) as relevance
In this case title would have 3x, body 2x the value of a match in comments.