在mysql中搜索名称的最佳方式


Best way to search for names in mysql

我想搜索一个名为"Martins Silva"的用户。我在BOOLEAN模式下使用全文。

 MATCH(name,lastname) AGAINST('+martins +silva' IN BOOLEAN MODE)

搜索结果"Martins Silva"返回

Orleans Silva De Martins (1)
Armistrong Oliveira Martins Da Silva (2)
Douglas Martins Vieira Da Silva (3)
Glauciene Silva Martins (4)
Jose Martins Silva (5)
...

问题是,数据库中有一个名为"Martins Silva"的用户,但它只出现在540位置的结果集上。

这是我从阅读文档和了解排名计算方式中所期望的结果。然而,这对我解决这个问题没有帮助。我也试着用LIKE搜索,但我得到了同样的结果。

考虑到这个结果集,对我来说最好的是:

Martins silva (540) -> because it is the exact phrase 
Jose Martins Silva (5) -> because it is the exact phrase that appears in a position first than in (2)
Armistrong Oliveira Martins Da Silva (2) -> distance between martins and silva is shorter than in (3)
Douglas Martins Vieira Da Silva (3)
Glauciene Silva Martins (4) -> lower priority when it is out of order
Orleans Silva De Martins (1)   

所以,我想我可以用一种算法来解决这个问题,该算法考虑单词在查询中的顺序或位置。

我试着计算Levenstein距离,但对于大型数据库来说,它真的很慢。

MySQL中有办法解决这个问题吗?或者我必须使用Apache Lucene之类的东西?或者我做错了什么?这个搜索是我网站上的主要内容,它必须非常好地工作。

非常感谢你们,伙计们!

在您的特定情况下,您需要实现一个levenstein函数来实现这一点。比赛将无法以正确的方式进行。通过排序级别相关性ASC,您将从最相关到最不相关。

levenstein函数添加到您的数据库:

DELIMITER $$
CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
IF s1 = s2 THEN
RETURN 0;
ELSEIF s1_len = 0 THEN
RETURN s2_len;
ELSEIF s2_len = 0 THEN
RETURN s1_len;
ELSE
WHILE j <= s2_len DO
SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
END WHILE;
WHILE i <= s1_len DO
SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
WHILE j <= s2_len DO
SET c = c + 1;
IF s1_char = SUBSTRING(s2, j, 1) THEN
SET cost = 0; ELSE SET cost = 1;
END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
IF c > c_temp THEN SET c = c_temp; END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
IF c > c_temp THEN
SET c = c_temp;
END IF;
SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
END WHILE;
SET cv1 = cv0, i = i + 1;
END WHILE;
END IF;
RETURN c;
END$$
DELIMITER ;

这个模式查询将按最相关的姓氏第一和名字第二进行排序,并将最相关的显示为第一行,添加LIMIT 1只得到最相关的结果:

Select lastname, levenshtein(lastname,$var1) as relevance1,
name, levenshtein(name,$var2) as relevance2
FROM database
ORDER BY relevance 1 ASC, relevance 2 ASC

如果您想要精确搜索,那么您可以合并列并使用它们进行搜索。使用类似的东西

从tableName中选择CONCAT(firstname,",lastname),其中CONCAT(lastname,",lastname")="Martins Silva";

文本搜索的最佳解决方案是Lucene。

如果你使用任何其他的文本搜索算法,它会很慢,lucene已经测试了性能和编码容易性。http://lucene.apache.org/