FullText搜索Innodb失败,MyIsam返回结果

问题描述 投票:0回答:3

我已经将桌子从myisam升级到innodb但是没有相同的表现。当应该存在某种关系时,innodb会返回0分数。 myisam表返回相同术语的匹配项(我保留了旧表的副本,因此我仍然可以运行相同的查询)。

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table_myisam
where id = 1;

返回:

+-------+
| score |
+-------+
|     1 |
+-------+

但:

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table
where id = 1;

收益:

+-------+
| score |
+-------+
|     0 |
+-------+

我认为ex可能没有被索引,因为innodb_ft_min_token_size被设置为3。我把它降低到1并优化了表格,但没有任何影响。列内容长度为99个字符,因此我推测整个列因innodb_ft_max_token_size而未编入索引。我把它增加到了150并再次进行了优化但又有了相同的结果。

这些表之间的唯一区别是引擎和字符集。此表使用utf8myisam表使用latin1

有没有人看过这些行为,或者有如何解决它的建议?

更新:我将ft_stopword_file=""添加到我的my.cnf并再次运行OPTIMIZE TABLE table。这次我得到了

优化|注意|表不支持优化,而是重新创建+分析

此更改后查询有效。 Ex不是一个停止词,但不确定为什么会有所作为。

但是失败的新查询是:

SELECT MATCH (Columns) AGAINST ('+Term +Ex +in' IN BOOLEAN MODE) as score FROM Table where id = 1;

+-------+
| score |
+-------+
|     0 |
+-------+

in导致失败,但这是我表中的下一个词。

SELECT MATCH (Columns) AGAINST ('+Term +Ex' IN BOOLEAN MODE) as score FROM Table where id = 1;

+--------------------+
| score              |
+--------------------+
| 219.30206298828125 |
+--------------------+

我也尝试了CREATE TABLE my_stopwords(value VARCHAR(30)) ENGINE = INNODB;,然后用my.cnf更新了innodb_ft_server_stopword_table='db/my_stopwords'。我重新启动并运行:

show variables like 'innodb_ft_server_stopword_table';

带回来:

+---------------------------------+---------------------------+
| Variable_name                   | Value                     |
+---------------------------------+---------------------------+
| innodb_ft_server_stopword_table | 'db/my_stopwords'; |
+---------------------------------+---------------------------+

所以我认为in不会导致查询失败,但它会继续。我也再次尝试过OPTIMIZE TABLE table甚至ALTER TABLE table DROP INDEX ...ALTER TABLE table ADD FULLTEXT KEY ...都没有影响。

第二次更新问题在于停用词。

$userinput = preg_replace('/\b(a|about|an|are|as|at|be|by|com|de|en|for|from|how|i|in|is|it|la|of|on|or|that|the|this|to|was|what|when|where|who|will|with|und|the|www)\b/', '', $userinput);

解决了这个问题,但这对我来说并不是一个好的解决方案。我想要一个解决方案,避免在mysql中打破这个停止词。

停用词表数据:

CREATE TABLE `my_stopwords` (
  `value` varchar(30) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Name: my_stopwords
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2019-04-09 17:39:55
    Update_time: NULL
     Check_time: NULL
      Collation: latin1_swedish_ci
       Checksum: NULL
 Create_options: 
        Comment: 
mysql full-text-search innodb myisam mysql-5.6
3个回答
3
投票

这是一个循序渐进的过程,应该重现你的问题。 (这实际上就是你应该如何编写你的问题。)环境是一个新安装的虚拟机,配有Debian 9.8和Percona Server Ver 5.6.43-84.3。

  1. 创建一个带有全文索引和一些虚拟数据的InnoDB表: create table test.ft_innodb ( txt text, fulltext index (txt) ) engine=innodb charset=utf8 collate=utf8_unicode_ci; insert into test.ft_innodb (txt) values ('Some dummy text'), ('Text with a long and short stop words in it ex');
  2. 执行测试查询以验证它是否仍然无法正常工作: select txt , match(t.txt) against ('+some' in boolean mode) as score0 , match(t.txt) against ('+with' in boolean mode) as score1 , match(t.txt) against ('+in' in boolean mode) as score2 , match(t.txt) against ('+ex' in boolean mode) as score3 from test.ft_innodb t; 结果(四舍五入): txt | score0 | score1 | score2 | score3 -----------------------------------------------|--------|--------|--------|------- Some dummy text | 0.0906 | 0 | 0 | 0 Text with a long and short stop words in it ex | 0 | 0 | 0 | 0 如你所见,它不能用停用词(“+ with”)或短词(“+ ex”)。
  3. 为自定义停用词创建一个空的InnoDB表: create table test.my_stopwords (value varchar(30)) engine=innodb;
  4. 编辑/etc/mysql/my.cnf并在[mysqld]块中添加以下两行: [mysqld] # other settings innodb_ft_server_stopword_table = "test/my_stopwords" innodb_ft_min_token_size = 1
  5. 使用service mysql restart重启MySQL
  6. 再次从(2.)运行查询(结果应该相同)
  7. 使用重建全文索引 optimize table test.ft_innodb; 它实际上将重建整个tabe,包括所有索引。
  8. 再次执行(2.)中的测试查询。结果是: txt | score1 | score1 | score2 | score3 -----------------------------------------------|--------|--------|--------|------- Some dummy text | 0.0906 | 0 | 0 | 0 Text with a long and short stop words in it ex | 0 | 0.0906 | 0.0906 | 0.0906

你觉得它对我来说很好。而且重现起来非常简单。 (再次 - 这就是你应该写下你的问题的方式。)

由于你的程序相当混乱而不是详细,所以很难说你可能出现什么问题。例如:

CREATE TABLE my_stopwords(value VARCHAR(30)) ENGINE = INNODB;

这不包含您定义该表的数据库中的信息。请注意,我已将所有表格与相应的数据库作为前缀。现在考虑以下内容:我更改my.cnf并设置innodb_ft_server_stopword_table = "db/my_stopwords"。注意 - 我的服务器上没有这样的表(甚至不存在架构db)。重启MySQL服务器。并检查新设置

show variables like 'innodb_ft_server_stopword_table';

返回:

    Variable_name                   | Value
    --------------------------------|----------------
    innodb_ft_server_stopword_table | db/my_stopwords

optimize table test.ft_innodb;之后,测试查询返回:

    txt                                            | score0 | score1 | score2 | score3
    -----------------------------------------------|--------|--------|--------|-------
    Some dummy text                                | 0.0906 | 0      | 0      | 0
    Text with a long and short stop words in it ex | 0      | 0      | 0      | 0.0906

你看?它不再使用停用词了。但它适用于像“+ ex”这样的短暂不停词。因此,请确保您在innodb_ft_server_stopword_table中定义的表实际存在。


3
投票

MyISAM的FULLTEXT和InnoDB之间存在一些差异。我认为你被处理'短'和/或停止词语所困扰。 MyISAM将显示行,但InnoDB将无法显示。

使用FT(以及切换到InnoDB之后)我所做的是过滤用户的输入以避免短字。它需要额外的努力,但得到我想要的行。我的情况稍有不同,因为生成的查询是这样的。请注意,我添加了+来要求单词,但不是短于3的单词(我的ft_min_token_size是3)。这些搜索是针对build a tablebuild the table

WHERE match(description) AGAINST('+build* a +table*' IN BOOLEAN MODE)
WHERE match(description) AGAINST('+build* +the* +table*' IN BOOLEAN MODE)

(尾随的*可能是多余的;我没有调查过。)

另一种方法

由于FT在非短,不间断的单词中非常有效,所以要进行两个阶段的搜索,每个阶段都是可选的:要搜索“长字”,请执行

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)
  AND d REGEXP '[[:<:]]a[[:>:]]'

第一部分通过查找“long”和“word”(作为单词)快速淡化可能的行。第二部分也确保字符串中有一个单词aREGEXP价格昂贵,但仅适用于通过第一次测试的那些行。

要搜索“长字”:

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)

要搜索单词“a”:

WHERE d REGEXP '[[:<:]]a[[:>:]]'

警告:这种情况会很慢。

注意:我的示例允许单词以任何顺序,以及字符串中的任何位置。也就是说,这个字符串在我的所有例子中都会匹配:“她渴望得到他的一句话。”


0
投票

搜索中常见的技术是使用“已清理”字符串创建一个额外列以进行搜索。然后将FULLTEXT索引添加到该列而不是原始列。

在您的情况下,删除停用词是主要区别。但也可能存在可能(应该?)删除的标点符号。有时带有连字符的单词或单词或收缩或部件号或型号会造成麻烦。可以修改它们以更改标点符号或间距,使其更符合FT要求和/或用户的输入风格。另一件事是在搜索字符串列中添加单词,这些单词是列中单词的常见拼写错误。

当然,这比你想做的更多。但我认为它提供了一个可行的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.