我正在尝试在具有一对多关系的两个表上实现搜索功能。将其视为具有多个标记的帖子。每个标记在tag
表中都有自己的行。
如果可以在a)发布文字,b)帖子标记或c)中找到所有搜索字词,我想要检索帖子的两个
假设我已经创建了这样的表:
CREATE TABLE post (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
text VARCHAR(100) NOT NULL
);
CREATE TABLE tag (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
post MEDIUMINT NOT NULL
);
我创建了这样的索引:
CREATE FULLTEXT INDEX post_idx ON post(text);
CREATE FULLTEXT INDEX tag_idx ON tag(name);
如果我的搜索查询是“TermA TermB”并想在帖子文本中搜索只是,我会像这样制定我的SQL查询:
SELECT * FROM post WHERE MATCH(text) AGAINST('+TermA +TermB' IN BOOLEAN MODE);
有没有办法在混音中添加标签?我以前的尝试是这样的:
SELECT * FROM post
RIGHT JOIN tag ON tag.post = post.id
WHERE MATCH(post.text) AGAINST('TermA TermB' IN BOOLEAN MODE)
OR MATCH(tag.name) AGAINST('TermA TermB' IN BOOLEAN MODE);
问题是,这只是任何单词查询,而不是所有单词查询。我的意思是,如果TermA在文本中并且TermB在标签中,我想要检索帖子。
我在这里缺少什么?这甚至可以使用全文搜索吗?有没有更好的方法来解决这个问题?
答案 0 :(得分:1)
试试这个:
SELECT post.*
FROM post
INNER JOIN (SELECT post, GROUP_CONCAT(name SEPARATOR ' ') tags FROM tag GROUP BY post) tag ON post.id=tag.post
WHERE MATCH(post.text) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
OR MATCH(tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
这可能也可以获得与内容或标签匹配的结果,但它在MySQL 5.1中不起作用:
SELECT post.*, GROUP_CONCAT(tag.name SEPARATOR ' ') tags
FROM post
LEFT JOIN tag ON post.id=tag.post
GROUP BY post.id
HAVING MATCH(post.text,tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
所以我把它重写为:
SELECT post.*, tags
FROM post
LEFT JOIN (SELECT post, GROUP_CONCAT(tag.name SEPARATOR ' ') tags FROM tag GROUP BY post) tags ON post.id=tags.post
WHERE MATCH(post.text, tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
答案 1 :(得分:1)
这是可能的,但我猜测在你的Tags
表格中,每个帖子的每个标记都有一行。所以一行包含标签' TermA'对于帖子1和带有标签' TermB'的另一条记录,对吧?
所有单词查询(带+
)仅返回搜索字段包含所有指定单词的行。对于标签表,情况绝对不是这样。
一种可能的解决方案是将所有标签存储在posts表本身的单个字段中。那么在标签上进行高级匹配也很容易。
另一种可能性是完全改变标签的条件。也就是说,对文本使用all
查询,对标记使用any
查询。要做到这一点,您必须自己修改搜索查询,幸运的是,从查询中删除加号一样容易。
您还可以查询完全匹配,如下所示:
SELECT * FROM post p
WHERE
MATCH(p.text) AGAINST('TermA TermB' IN BOOLEAN MODE)
AND
/* Number of matching tags .. */
(SELECT COUNT(*) FROM tags t
WHERE
t.post = p.id
AND (t.tag in ('TermA', 'TermB')
= /* .. must be .. */
2 /* .. number of searched tags */ )
在此查询中,我计算匹配标记的数量。在这种情况下,我希望它正好是2,意味着两个标签匹配(假设每个帖子的标签是唯一的)。您还可以检查> = 1以查看是否有任何标签匹配。
但是正如您所看到的,这也需要解析搜索字符串。您将不得不删除这些加号(甚至检查它们的存在,以了解您是否需要'任何'所有')。而且你必须将它拆分以获得搜索到的单词的数量,并自己获得单独的单词。
总而言之,将所有标签添加到“标签”中。 post
中的字段是最简单的方法。从标准化的角度来看,这不是理想的,但我认为这是可管理的。
答案 2 :(得分:0)
您可以同时搜索text
和tags
。
SELECT *
FROM post
WHERE MATCH(text,tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
要使其工作,您需要为两个列一起制作FULLTEXT索引。
CREATE FULLTEXT INDEX keywords ON pos(text,tags)
在布尔搜索模式下,这应该做你想要的。