2013-10-17 31 views
1

我正在爲任意文本/短語實現類似autosuggest的功能。我想向用戶提供一些絕對的相關性,而不僅僅是發現項目相對於彼此的排名。 即,如果數據庫中的某些行按確切的形式和順序包含輸入的文本,則需要「1」左右的排序。我可以得到MySQL FULLTEXT [0; 1]範圍?

例如,讓我們對「我喜歡吃水果」搜索:

  • 與內容‘當然我喜歡吃水果和vegitables’我希望排秩是還是有點減。
  • 與內容行「我喜歡新鮮水果」我預計結果小於1,但還是相當高的,像0.7

這可以用MySQL和FULLTEXT完成嗎? 而不是[0,1]我得到2.7或1.2或完全匹配的值爲0.6。它出什麼問題了?

這是我的測試表:

表數據:

id text 
1 Lorem ipsum dolor 
2 You can search an index, and organize and present search results. 
3 The Search API can index any number of documents. 
4 Each field has a name and a type. 
5 Each field is required. 
7 Cras dapibus. Vivamus elementum semper nisi. 
8 Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. 
9 Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. 
10 Nam eget dui. Etiam rhoncus. 

查詢1:

SET @str := 'Lorem ipsum dolor'; 
SELECT id, TEXT, MATCH (TEXT) AGAINST (@str) rank FROM test WHERE MATCH (TEXT) AGAINST (@str); 

- >

id text rank 
1 Lorem ipsum dolor 1.280059814453125 

'1.28'是什麼意思?在這裏有'1'會很好。

查詢2:

SET @str := 'Each field is required.'; 
SELECT id, TEXT, MATCH (TEXT) AGAINST (@str) rank FROM test WHERE MATCH (TEXT) AGAINST (@str); 

- >

id text rank 
5 Each field is required. 1.7639520168304443 
4 Each field has a name and a type. 0.8533731698989868 

一個更完整的比賽,我希望 '1' 這裏。

問題3:

SET @str := 'Aenean leo ligula, porttitor eu'; 
SELECT id, TEXT, MATCH (TEXT) AGAINST (@str) rank FROM test WHERE MATCH (TEXT) AGAINST (@str); 

- >

id text rank 
8 Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. 3.5851094722747803 
9 Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi.  0.4266865849494934 

此外全場比賽,和misterious '3.58' 被返回。

我不需要精確的數字,只是[0,1]之間的數字,以便我可以確定是否有完整(或幾乎完整)的匹配。如果結果完全包含搜索字符串,我需要0.8..1左右的內容。或者,也許這只是一個錯誤的工具? FULLTEXT實際上並不是全文,因爲我無法弄清楚它是否完全匹配。

回答

3

MySQL使用an n-dimensional vector product with some fudge factors爲全文匹配生成相關值,這意味着這些值不能以超出給定數據集和查詢的一般方式歸一化。 (然後,爲什麼你需要它們呢?對同一個數據集的類似查詢已經產生了類似的相關性值,並且不同查詢的結果之間的規範化不會產生任何幫助。)

也就是說,沒有什麼阻止你歸等級值自己,結果集合中查詢返回的,用每行一個簡單的計算:

row_normalized_rank = row_returned_rank/highest_returned_rank 

這將需要結果集的兩次掃描,我們能夠確定的最高返回等級值,另一個用於將每行的排名值與返回的最高值進行歸一化;你可以用一個足夠強大的嵌套查詢來完成,但你最好在代碼中完成它。

您還可以自己添加一個模糊因子,以提高精確匹配的排名;考慮一個絕對的等級調整,如:

SELECT id, text, (MATCH (text) AGAINST (@str) + 
        IF(text LIKE CONCAT("%", @str, "%"), 1, -1)) AS rank 
FROM test 
WHERE MATCH (text) AGAINST (@str); 

或幅度的擴大,如:

SET @fudge := 2; 
SELECT id, text, (MATCH (text) AGAINST (@str) * 
        IF(text LIKE CONCAT("%", @str, "%"), @fudge, 1/@fudge)) AS rank 
FROM test 
WHERE MATCH (text) AGAINST (@str); 

調整的味道,當然,不過這應該有助於給你更多喜歡的東西你」重新尋找排名行爲。

+0

非常感謝,並且在單個查詢中使用了'@ fedge'方法!它不能解決我的問題,但可以確保我不會錯過最相關的結果。 我需要[0,1]範圍,因爲:1)我不想顯示結果的相關性很低2)我想向用戶提供其他相關結果的提示/突出顯示/推薦標記(0.7到1例如),而不只是大多數排名第一。用戶體驗是我的首要任務。 – vladimir83

+0

@ vladimir83當然 - 我不會打折你的目的,而是試圖指出如何通過一些小小的欺騙和後期處理來達到你需要的結果。該解決方案如何改變以滿足您的要求? (我認爲我必須在你的解釋中誤讀某些東西,因爲我還不清楚缺少什麼。) –

3

看起來像使用MySQL將等級標準化爲[0,1]是不可能的。通過規範化爲[0,1],我的意思是爲(幾乎)完全匹配的行獲取值接近1,而不僅僅是最高級別的行。例如,如果我搜索"one apple, two oranges",我可能會得到一行,如"one two three"。使用公式rank=row_rank/highest_rank在這種情況下,我將得到rank = 1。但它並不完全匹配。我寧願期望值在0.5或更少(找到一半的搜索詞)。

此外,我已經看過Lucene和獅身人面像。有幾個rankers,但似乎秩= 1的定義可能完全取決於應用程序的要求。例如,如果搜索短語完全包含在數據庫中,則需要rank = 1,但當搜索短語與數據庫中的整個內容匹配時,有人可能會預期它爲1。

SELECT id, TEXT, (MATCH (TEXT) AGAINST (@str) * 
        IF(TEXT LIKE CONCAT("%", @str, "%"), 2, 1)) AS rank 
FROM test 
WHERE MATCH (TEXT) AGAINST (@str) ORDER BY rank DESC LIMIT 100; 

1.使用FULLTEXT和x2/fudge辦法由Aaron建議從數據庫獲取前100排名列:


所以,我在三個步驟解決問題

這樣做會使檢索大多數相關行的工作量減少,從而減少下一步的數據量。事實上,MySQL的排名值完全被忽略。

2.對於每個100行的計算歸一化的秩編程關於Java/Groovy的側,基於應用的需求(如在[0,1]範圍內)

這是非常具有挑戰性的,但我能夠基於簡單的數學公式和少量規則創建相對簡單的算法。經過一些優化後,花費大約6毫秒來計算所有100行的排名。

3.按新排名排序結果,向用戶顯示排名前10的結果。

我沒有表現與排名< 0.5的結果,並在UI我還強調高級別(0.8-1)

我測試了它的結果,它工作得很好。但是,對於某些情況,從步驟#1開始的FULLTEXT搜索完全不會返回結果。當搜索短語一般出現在數據庫中時,會發生這種情況,但很少有詞語的結尾是不同的。我的排名算法可能會評估它爲0.3-0.7排名,但它不是來自第1步。所以,現在我會繼續使用這種方法,但是稍後可能會考慮在步驟1中用其他方法(也許是Lucene)替換MySQL。