如何在搜索查询中允许连字符进行全文搜索

16

我有像“some-or-other”这样的关键词,在使用MySQL数据库进行搜索时需要考虑连字符。我目前正在使用全文本函数。

是否有一种方法可以转义连字符? 我知道其中一种选项是在myisam/ftdefs.h文件中注释掉#define HYPHEN_IS_DELIM,但不幸的是我的主机不允许这样做。还有其他选择吗?

这是我现在的代码:

$search_input = $_GET['search_input'];
$keyword_safe = mysql_real_escape_string($search_input);
$keyword_safe_fix = "*'\"" . $keyword_safe . "\"'*";


$sql = "
    SELECT *,
        MATCH(coln1, coln2, coln3) AGAINST('$keyword_safe_fix') AS score
        FROM table_name
    WHERE MATCH(coln1, coln2, coln3) AGAINST('$keyword_safe_fix')
    ORDER BY score DESC
";
4个回答

19

从这里http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

解决包含破折号或连字符的单词的方法是使用全文搜索布尔模式,并用双引号括起来。

或者从这里http://bugs.mysql.com/bug.php?id=2095

还有另一种解决方法。它最近添加到手册中: “修改字符集文件:这不需要重新编译。 true_word_char() 宏使用“字符类型”表来区分字母和数字以外的其他字符。您可以编辑一个字符集 XML 文件中的内容,以指定“-”为“字母”。然后将给定的字符集用于 FULLTEXT 索引。”

我还没有尝试过。

编辑:这里http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html提供了更多的附加信息

用双引号(“”)括起来的短语只匹配包含该短语文字的行,因为它是如按照原样输入的。全文搜索引擎将短语分成单词,并在 FULLTEXT 索引中对这些单词进行搜索。在 MySQL 5.0.3 之前,该引擎会在找到的记录中执行子字符串搜索,因此匹配必须包括短语中的非字母字符。从 MySQL 5.0.3 开始,非单词字符不需要完全匹配:短语搜索仅要求匹配与短语中完全相同的单词且顺序相同。例如,"test phrase" 在 MySQL 5.0.3 中与 "test, phrase" 匹配,但在此之前则不会。

如果短语中不包含索引中的任何单词,则结果为空。例如,如果所有单词都是停用词或短于索引单词的最小长度,则结果为空。


4
好的,尝试这个SELECT * FROM your_table_name WHERE MATCH (your_table_column_name) AGAINST ('"SQL-MySQL"' IN BOOLEAN MODE); 在这里查看更多信息:http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html - Yasen Zhelev
太棒了!非常顺利地完成了工作!非常感谢 :) - Jay
1
@Yasen Zhelev 实际上...我刚刚进行了一些测试,并注意到搜索“blah-blah-”或“blah-blah-”(通常是2个连字符)会得到0个结果。有什么办法可以绕过这个问题吗? - Jay
@Yasen Zhelev:我只是复制并粘贴了我在帖子中的代码。当我这样做时,我意识到“IN BOOLEAN MODE”不允许评分...有没有什么办法可以解决这个问题?因为这会破坏使用全文搜索引擎的目的。 - Jay
1
@Yasen Zhelev: "score" 部分是指 fulltext 函数评估表中每一行与搜索关键词相关性的能力。根据全文搜索的文档,BOOLEAN MODE 似乎不允许这样做... 我想知道是否有办法在 BOOLEAN MODE 中编写一个允许搜索查询中包含连字符的 score 函数? - Jay
显示剩余4条评论

4

有些人建议使用以下查询:

SELECT id 
FROM texts
WHERE MATCH(text) AGAINST('well-known' IN BOOLEAN MODE)
HAVING text LIKE '%well-known%';

但是,由于需要考虑所使用的全文操作符的许多变体。任务:实现类似于+well-known +(>35-hour <39-hour) working week*的查询。太复杂了!
并且不要忘记ft_min_word_len的默认长度,因此搜索up-to-date只会在结果中返回date
技巧:
因此,我更喜欢一种技巧,这样就不需要使用HAVING等构造了。
  1. Instead of adding the following text to your database table:

    "The Up-to-Date Sorcerer" is a well-known science fiction short story.
    copy the hyphen words without hypens to the end of the text inside a comment:
    "The Up-to-Date Sorcerer" is a well-known science fiction short story.<!-- UptoDate wellknown -->

  2. If the users searches for up-to-date remove the hyphen in the sql query:
    MATCH(text) AGAINST('uptodate ' IN BOOLEAN MODE)

通过这样,您的用户可以找到包含 up-to-date 作为一个词,而不是获取所有只包含 date 的结果(因为 ft_min_word_len 删除了 upto)。

当然,在 echo 文本之前,您应该删除 <!-- ... --> 注释。

优点

  • 查询更简单
  • 用户能够像往常一样使用所有全文搜索操作符
  • 查询更快
  • 如果用户搜索 -well-known +science,MySQL 将其视为不包括 *well*,可以包括 *known*,必须包括 *science*。这不是用户期望的结果。这个技巧也解决了这个问题(因为 SQL 查询搜索的是 -wellknown +science

2
也许更简单的方法是使用 Binary 运算符。
SELECT * 
FROM your_table_name 
WHERE BINARY your_column = BINARY "Foo-Bar%AFK+LOL"

http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html#operator_binary

BINARY 操作符将其后的字符串转换为二进制字符串。这是一种强制按字节比较而不是按字符比较列的简单方法。即使列未定义为BINARYBLOB,这也会导致比较区分大小写。 BINARY 还使尾随空格变得重要。


3
这导致我的MySQL Workbench崩溃。 - mnutsch
1
这将一个2秒的查询变成了一个12秒的查询。 - CGSmith105

0

对于这个问题,我的首选解决方案是从搜索词和被搜索的数据中移除连字符。我在全文表中保留了两列 - 搜索返回搜索 包含经过消毒处理的数据,去除了各种字符,这是用户搜索词与之进行比较的内容,而我的代码也已消毒处理过。

然后我展示 返回 列。

这意味着我在数据库中有两份数据副本,但对我来说,这种权衡是值得的。我的全文表只有约500,000行,所以在我的用例中并不重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接