2014-03-12 146 views
1

我正試圖在一個對一對多關係的兩個表上實現搜索功能。把它想象成與多個標籤。每個標籤在tag表中都有自己的行。MySQL全文搜索:一對多關係

我想找回後,如果所有的搜索詞可以發現無論是)的後文,二)後標籤或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); 

如果我的搜索查詢是「特瑪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在標籤中,我想檢索該文章。

我在這裏錯過了什麼?這甚至可以使用全文搜索?有沒有更好的方法來解決這個問題?

回答

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) 
+0

我試過了。唯一的問題是,如果只有一個條款在帖子文本中,並且沒有任何條款在標籤中,它仍然匹配。我需要它是一個**和**。 – musicnothing

+0

它在MATCH(post.text)中未找到+。測試更新的查詢。 –

+0

現在它只匹配兩個詞都在文本中,並且兩個詞都在標籤中。我需要的是在文本,標籤或兩者中找到所有術語。就像TermA在文本中,但TermB在標籤中。 – musicnothing

1

這是可能的,但我猜你的Tags表中,你有每個帖子每個標籤一行。因此,一行包含標籤'TermA'用於發佈1,另一行包含標籤'TermB',對吧?

全部詞語查詢(與+)只返回搜索字段包含所有指定詞的行。對於標籤表而言,情況絕非如此。

一個可能的解決方案是將所有標籤存儲在帖子表本身的單個字段中。那麼在標籤上進行高級匹配也很容易。

另一種可能性是完全改變標籤的條件。也就是說,對文本使用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中的「標籤」字段是最簡單的方法。從標準化的角度來看,這並不理想,但我認爲這是可以管理的。

+0

這可以工作,如果我知道有多少條款與帖子文本匹配。有沒有一種方法來計算? – musicnothing

+0

因爲這是現在,至少有一個詞必須在帖子文本中,所有的詞必須在標籤中找到。如果我可以從預計在標籤中找到的標籤數量中減去在帖子文本中找到的標籤數量,我可以將其取消。 – musicnothing

+0

也許你可以爲每個搜索到的關鍵字添加一個「MATCH」條件。在布爾模式下,我認爲它返回0或1,在這種情況下,你可以將它們加起來。 – GolezTrol

0

您可以搜索texttags

SELECT * 
    FROM post 
WHERE MATCH(text,tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE) 

爲了得到這個工作,你需要爲兩個列創建一個FULLTEXT索引。

CREATE FULLTEXT INDEX keywords ON pos(text,tags) 

在布爾搜索模式下,這應該做你想做的。

+0

問題在於您無法創建跨多個表格的全文索引。標籤不是一個領域,它是一張桌子。您的建議是,我還在郵寄表中保留了一個標籤中的字段? – musicnothing