【问题标题】:MySQL - Escaping ampersand (&) in fulltext searchesMySQL - 在全文搜索中转义 & 符号
【发布时间】:2014-08-05 23:32:49
【问题描述】:

我们正在使用全文搜索来搜索公司的名称,一切顺利,直到我们有了一个名称中带有 & 符号的公司,例如'小姐'。

SELECT name FROM company WHERE MATCH (name) against ('M&S' IN BOOLEAN MODE);

由于 MySQL 将 & 符号视为布尔运算符,因此无法返回任何结果。需要布尔模式,因此不能简单地将其关闭。

我正在寻找的是一种逃避 & 符号的方法,以便 MySQL 正确处理它并找到记录。

放弃全文搜索以支持LIKEs 也不完全是一种选择

感谢您的帮助

【问题讨论】:

  • 我没有看到 & 在文档中列为特殊字符:dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html。您确定不是因为M&S 太短而无法编入索引吗?请参阅ft_min_word_len 选项,默认为 4。
  • @Barmar ft_min_word_len 设置为 2,因为我们有很多非常短的公司名称。
  • 另一种可能性是& 被视为单词分隔符。所以这实际上是两个词:MS,它们之间有标点符号。
  • 请参阅dev.mysql.com/doc/refman/5.5/en/fulltext-fine-tuning.html,了解如何更改被视为单词一部分而不是分隔符的字符。
  • @Barmar 我尝试set global ft_boolean_syntax = '+ -><()~*:""_|'; 从语法中删除 & ,但这似乎没有任何区别。也许Positions 10, 13, and 14 (which by default are set to “:”, “&”, and “|”) are reserved for future extensions 会阻止这种情况。

标签: mysql escaping full-text-search


【解决方案1】:

似乎& 在您用于全文搜索的排序规则中不被视为单词字符。

因此您必须创建自己的排序规则(或重新编译您的 MySQL 服务器),在其中将 & 添加到我在 MySQL 文档中找到的单词字符列表中( http://dev.mysql.com/doc/refman/5.0/en/fulltext-fine-tuning.html) :

如果您想更改被视为单词的字符集 字符,您可以通过多种方式执行此操作,如 以下列表。进行修改后,您必须重建 包含任何 FULLTEXT 索引的每个表的索引。认为 您希望将连字符 ('-') 视为单词字符。 使用以下方法之一:

修改 MySQL 源代码:在 myisam/ftdefs.h 中,查看 true_word_char() 和 misc_word_char() 宏。将“-”添加到这些宏之一和 重新编译 MySQL。

修改字符集文件:这不需要重新编译。这 true_word_char() 宏使用“字符类型”表来区分 其他字符的字母和数字。 .您可以编辑内容 字符集 XML 文件之一中的数组的 指定“-”是一个“字母”。然后使用给定的字符集 你的全文索引。有关阵列的信息 格式,请参阅第 10.3.1 节,“字符定义数组”。

为索引列使用的字符集添加新的排序规则, 并更改列以使用该排序规则。一般信息 关于添加排序规则,请参阅第 10.4 节,“将排序规则添加到 字符集”。有关全文索引的特定示例,请参阅 第 12.9.7 节,“为全文索引添加排序规则”。

更新:如果您使用 latin1 排序规则,请打开位于 mysql/share/charsets/latin1.xml 的 XML 文件。并在地图中找到相应的字符代码 - 在这种情况下,您可以将地图设为小写或大写,因为这与 & 符号无关:

<lower>
<map>
 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
</map>
</lower>

& 符号的 unicode 为 U+0026,在 utf-8 编码中为 0x26,因此请在地图中搜索 26 - 它位于第 3 行第 7 列。

然后在ctype-map 中将字符的类型从表示标点符号的 10 更改为表示小写字母的 01:

<ctype>
<map>
 00
 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
 48 10 10 10 10 10 01 10 10 10 10 10 10 10 10 10
 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
 10 00 10 02 10 10 10 10 10 10 01 10 01 00 01 00
 00 10 10 10 10 10 10 10 10 10 02 10 02 00 02 01
 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
</map>
</ctype>

重新启动您的 MySQL 服务器,相应的排序规则正在处理 &amp;,就像它是一个小写字母一样。

当然最好先复制并重命名您的新排序规则XML-文件,然后复制并粘贴Index.xml 中的相应行(不要忘记在XML 中使用新的未使用ID标签在那里)并将它们链接到您的新排序规则XML-file,这样您就不会丢失原始排序规则。

你可以找到我从这里获得大部分信息的完整文档: http://dev.mysql.com/doc/refman/5.0/en/full-text-adding-collation.html

注意 - 对于所有使用 Mysql 5.7 版本的用户,请使用未使用的排序规则 ID。 mysql文章http://dev.mysql.com/doc/refman/5.0/en/fulltext-fine-tuning.html是针对Mysql 5.5版本的。要获得最大排序 ID,请使用以下查询 -

   SELECT MAX(ID) FROM INFORMATION_SCHEMA.COLLATIONS;

【讨论】:

  • 哇!这实际上可能是一个解决方案。可悲的是,我真的无法重新编译 MySQL 来测试它,我听说这绝对是一场噩梦。
  • @RobForrest 您不必重新编译它,这只是此处描述的 3 种可用方法之一。看我引用的第 3 段:This requires no recompilation ... You can edit the contents of the array in one of the character set XML files to specify that '-' is a “letter.”
  • 我想更多地研究修改字符集,但我真的不知道从哪里开始。我能找到的所有文档都非常糟糕,你有什么例子吗?
  • 这绝对有效!您能想到这可能导致的任何影响或并发症吗?
  • @dušan-brejka 如果新的排序规则基于已在多字节排序规则中编译的现有排序规则,则不再需要重新编译 mysql 以将排序规则添加到多字节字符集,例如:dev.mysql.com/doc/refman/5.7/en/ldml-collation-example.html
【解决方案2】:

编辑: 所以 & 将它分成两个单独的词......因为它们是 1 个字母,所以它不会返回任何东西。我用“Ma&Sa”进行了测试..我的 ft_min_word_len = 4 ......它没有返回任何东西,所以因为该字符串的长度 > 4 但它没有返回它必须将它分成两个单词......它看起来像诺斯基尔多南提出的建议是你必须做的。

所以这可能是也可能不是答案.. 但我希望它有助于解决这个问题.. 试试这个。

首先:运行这条语句 -- SHOW VARIABLES LIKE 'ft_min_word_len'; 并确认长度实际上是 = 2 如果是,我不确定它与长度超过 4 的单词有何不同

第二:我这样做并得到了结果。

设置:

我在我的本地主机数据库上设置了一个示例表...

create table company(
`id` int,
`name` varchar(55)
);

insert into company
(`id`, `name`)
values
(1, 'oracle'),
(2, 'microsoft'),
(3, 'M&S'),
(4, 'dell');

测试: 在 ft_min_word_len = 4 时进行了测试,显然它没有返回任何内容。

SELECT `name` FROM company WHERE MATCH (`name`) against ("M&S" IN BOOLEAN MODE);

我不想尝试重新启动我的 localhost 数据库以将长度重置为 2(以防我不小心弄乱了一些东西,因为我经常使用它)..

但我想尝试查找长度超过 4 且其中包含 & 的公司名称。

更多设置:

insert into company
(`id`, `name`)
values
(5, 'Mary&Sasha');

另一个测试:

SELECT `name` FROM company WHERE MATCH (`name`) against ("Mary&Sasha" IN BOOLEAN MODE); 

这返回了http://screencast.com/t/Rx8mh98OUp

我也这样做只是为了防止排序规则搞砸了,但我怀疑这是问题所在..

整理材料:

ALTER TABLE company MODIFY
    `name` VARCHAR(55)
      CHARACTER SET latin1
      COLLATE latin1_german2_ci;

您还可以检查您的表格排序规则:

SHOW TABLE STATUS;

希望这至少是一些帮助:)

【讨论】:

  • 谢谢约翰,但公司名称就是这样。
  • 我现在也尝试更改字符集,但似乎没有任何区别。
  • @RobForrest 我知道公司名称就是它们。我想表明的是 & 似乎不是问题,因为确切的查询适用于 'Mary&Sasha' ...你试过运行SHOW VARIABLES LIKE 'ft_min_word_len';吗?因为你必须重启数据库才能改变长度
  • 约翰,我的最小字长是 2。将其设置为 1 以符合您的示例不是一个明智的选择。
  • @RobForrest 我不是说将其设置为 1 .. 该语句只是显示了它的设置值.. 1 根本不合理...我想知道为什么我没有在 Mary&amp;Sasha 上使用它的问题
【解决方案3】:

&amp; 不是 mysql 中的特殊字符,因此您可以存储和搜索表达式 &amp; 您可以按如下方式进行测试

    SELECT name FROM  `testing` WHERE name LIKE  '%&%'

也请尝试以下类似的方法来替换 &amp;

    SET @searchstring = 'M&S';
    SET @searchstring = REPLACE(@searchstring,'&','&amp;');
    SELECT name FROM company WHERE MATCH (name) against (@searchstring IN BOOLEAN MODE);


你也可以看看正则表达式。 http://dev.mysql.com/doc/refman/5.1/en/regexp.html
这里 & 的用法如下。

    mysql> SELECT '&' REGEXP '[[.ampersand.]]';


以下查询也会为您提供结果

    SELECT * 
    FROM  `testing` 
    WHERE  `name` REGEXP CONVERT( _utf8 'M&S'
    USING latin1 ) COLLATE latin1_german2_ci 
    LIMIT 0 , 30

请也阅读这个帖子,也许你能比我理解得更好。这是 SQL 但他们似乎已经解决了问题 http://forums.asp.net/t/1073707.aspx?Full+text+search+and+sepcial+characters+like+ampersand+

对不起,我帮不上忙

【讨论】:

  • 这个问题是关于全文搜索的,即Match (x) Against (y) 不是like 所以你的第一个和第三个建议并不是真正的解决方案。至于您的第二个建议,由于 MySQL 将 & 视为特殊字符,因此不会返回预期结果。
  • OP 明确表示您不能使用 LIKE ...它在使用 like 时确实将其拉出,这是一个很好的例子.. 但我认为 IN BOOLEAN MODE 使 &一个不可转义的特殊字符
  • regexp 是一个复杂的搜索工具,它是一个建议。 Normaly & 不是特殊字符,但 john 说的很有道理
  • 是的,它不是 mysql 识别转义的特殊字符......但似乎布尔模式可以做到这一点。似乎 OP 使用 MATCH ... AGAINST.. 方法卡住了,您知道使用该参数的方法吗?
  • 你试过这个吗? “包含在双引号 (“"”) 字符中的短语仅匹配包含字面意思的短语的行,因为它是键入的。”dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
猜你喜欢
  • 1970-01-01
  • 2013-09-10
  • 2014-12-17
  • 2013-06-24
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 2018-08-16
相关资源
最近更新 更多