我已经将桌子从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
并再次进行了优化但又有了相同的结果。
这些表之间的唯一区别是引擎和字符集。此表使用utf8
,myisam
表使用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:
这是一个循序渐进的过程,应该重现你的问题。 (这实际上就是你应该如何编写你的问题。)环境是一个新安装的虚拟机,配有Debian 9.8和Percona Server Ver 5.6.43-84.3。
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');
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”)。create table test.my_stopwords (value varchar(30)) engine=innodb;
/etc/mysql/my.cnf
并在[mysqld]
块中添加以下两行:
[mysqld]
# other settings
innodb_ft_server_stopword_table = "test/my_stopwords"
innodb_ft_min_token_size = 1
service mysql restart
重启MySQLoptimize table test.ft_innodb;
它实际上将重建整个tabe,包括所有索引。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
中定义的表实际存在。
MyISAM的FULLTEXT和InnoDB之间存在一些差异。我认为你被处理'短'和/或停止词语所困扰。 MyISAM将显示行,但InnoDB将无法显示。
使用FT(以及切换到InnoDB之后)我所做的是过滤用户的输入以避免短字。它需要额外的努力,但得到我想要的行。我的情况稍有不同,因为生成的查询是这样的。请注意,我添加了+
来要求单词,但不是短于3的单词(我的ft_min_token_size
是3)。这些搜索是针对build a table
和build 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”(作为单词)快速淡化可能的行。第二部分也确保字符串中有一个单词a
。 REGEXP
价格昂贵,但仅适用于通过第一次测试的那些行。
要搜索“长字”:
WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)
要搜索单词“a”:
WHERE d REGEXP '[[:<:]]a[[:>:]]'
警告:这种情况会很慢。
注意:我的示例允许单词以任何顺序,以及字符串中的任何位置。也就是说,这个字符串在我的所有例子中都会匹配:“她渴望得到他的一句话。”
搜索中常见的技术是使用“已清理”字符串创建一个额外列以进行搜索。然后将FULLTEXT索引添加到该列而不是原始列。
在您的情况下,删除停用词是主要区别。但也可能存在可能(应该?)删除的标点符号。有时带有连字符的单词或单词或收缩或部件号或型号会造成麻烦。可以修改它们以更改标点符号或间距,使其更符合FT要求和/或用户的输入风格。另一件事是在搜索字符串列中添加单词,这些单词是列中单词的常见拼写错误。
当然,这比你想做的更多。但我认为它提供了一个可行的解决方案。