【问题标题】:MySQL search against column and replacing textMySQL 搜索列并替换文本
【发布时间】:2020-04-03 14:21:19
【问题描述】:

我正在尝试从针对查询的 mysql 匹配项中删除特定字符(连字符/破折号),因为搜索实际破折号会引发错误。这是我所拥有的,但我收到一个错误,即使用 match+replace 的语法不正确

SELECT *
FROM table
WHERE
MATCH (replace(search_text,'-','')) 
AGAINST ('+5552400*' IN BOOLEAN MODE)

“search_text”列的值是 555-2400,我正在尝试匹配。 搜索必须包含通配符。

search_text 可以包含 4 到 500 个字符。它可能是电话号码、电子邮件地址、邮寄地址、名字、姓氏等等。

这是一个自动完成搜索,如果我输入以下任何内容,555-2400 的结果应该会显示出来:

5
55
555
555-
555-2
555-24
555-240
555-2400

【问题讨论】:

  • 请提供一个样本数据集以及您想要的输出。对于您期望发生的事情有点困惑,但看起来您想要AGAINST ('"555-2400"' IN BOOLEAN MODE),它不会像没有引号一样排除2400。由于使用 REPLACE(search_text) 会消除 FTS 索引带来的任何好处,我认为您最好改用 search_text LIKE '%555-2400%'
  • 我希望它是通配符。这是用于自动完成搜索。因此,如果一个人开始输入 555- 它应该将 555-2400 显示为潜在匹配。
  • 如果我切换到像 '%555-2400%' 这样的 search_text,查询会明显变慢。该表有几百万条记录。在某些时候,搜索服务器可能是最好的。
  • @Cary 您希望如何处理电子邮件地址搜索,因为@ 也不是FULLTEXT 索引的有效单词字符,并且代表布尔模式中的距离运算符?这闻起来像键值存储表或无模式结构。有关FULLTEXT 的限制和处理电话号码搜索的4 种方法的解释,请参阅我的答案。但我强烈建议您认真考虑重构您的表模式,摒弃接受任何值的概念,或者将该数据移动到像 MongoDB 这样的 NOSQL 数据库,这样可以更好地支持您的需求。
  • 对于其他人,来自How to allow fulltext searching with hyphens in the search query 的参考答案有助于 OP

标签: mysql replace full-text-search


【解决方案1】:

您不应操纵查询的MATCH() 子句中的列值,因为这将导致全表扫描,从而破坏了FULLTEXT 索引的目的。这是因为 MySQL 需要从每一行中检索数据来确定结果函数值。

另外修改MATCH 子句是not permitted with INNODB storage tables,因为提供的列列表必须与FULLTEXT 索引中的内容完全匹配。

生成的列DB-Fiddle

一种方法是利用 MySQL 的 Generated Columns 功能来替换有问题的布尔运算符字符并在生成的列上使用单独的 FULLTEXT 索引。

生成的列将允许您的原始数据保持不变,以用于其他不相关的全文搜索,并允许使用AGAINST('+5552400*') 子句而不会发生布尔运算符冲突。辅助列还有助于减少误报,并通过向源列添加额外文本来降低可能出现的索引大小。

生成的列将导致INSERTUPDATE 操作受到轻微影响。因为每一行都会导致额外的操作为您自动添加第二列值。

期望的结果

| id  | search_text           |
| --- | --------------------- |
| 1   | called 555-2400 ext 4 |
| 2   | called 555-2400ext 4  |

架构

CREATE TABLE table_name (
  `id` INTEGER,
  `search_text` VARCHAR(21),
  FULLTEXT idx (search_text)
);

INSERT INTO table_name
  (`id`, `search_text`)
VALUES
  ('1', 'called 555-2400 ext 4'),
  ('2', 'called 555-2400ext 4'),
  ('3', 'called 555-2432 ext 1'),
  ('4', 'called 555-2432ext 1'),
  ('5', 'called 444-2400 ext 2'),
  ('6', 'called 444-2432 ext 2');

添加生成的列

ALTER TABLE table_name
ADD COLUMN search_text_parsed TEXT 
    GENERATED ALWAYS AS (REPLACE(search_text, '-', '')) STORED,
ADD FULLTEXT INDEX `idx2` (`search_text_parsed`);

您可以根据需要添加更多布尔运算符以删除,方法是更改​​生成的列表达式(REPLACE(REPLACE(search_text, '-', ''), '@', ''))

搜索查询

SELECT *
FROM table_name
WHERE MATCH(search_text_parsed)
      AGAINST('+5552400*' IN BOOLEAN MODE);

结果

| id  | search_text           | search_text_parsed   |
| --- | --------------------- | -------------------- |
| 1   | called 555-2400 ext 4 | called 5552400 ext 4 |
| 2   | called 555-2400ext 4  | called 5552400ext 4  |

此外,您可以直接在查询中自动执行术语值验证,以确保文本不包含任何有问题的布尔运算符,方法是使用
REPLACE(search_text, '-', '')

SET @term='555-2400';

SELECT *
FROM table_name
WHERE MATCH(search_text_parsed)
      AGAINST(CONCAT('+', REPLACE(@term, '-', ''), '*') IN BOOLEAN MODE);

其他方法

由于以不同的方式提出了这个问题,因此这里的问题与您的根本问题非常相似:How to allow fulltext searching with hyphens in the search query

【讨论】:

    【解决方案2】:

    尝试更改您的 SQL 查询以匹配确切的短语(使用双引号),这允许您在查询中包含破折号。

    SELECT *
    FROM table
    WHERE
    MATCH (search_text) 
    AGAINST ('+"555-2400"' IN BOOLEAN MODE)
    

    请注意,这样您会丢失最后的星号 (*),这可能会给您带来问题。这意味着您将不再获得仅以“555-2400”开头的结果,而只会获得完全匹配的结果。

    在此处查看更多信息:https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html

    【讨论】:

    • 绝对需要通配符,因为此搜索用于自动完成搜索功能。搜索“555-24”也应该得到 555-2400 的结果。
    【解决方案3】:

    根据您对问题的描述,也许您最好使用普通索引(而不是全文索引)

    ALTER TABLE table_name ADD INDEX (search_text);
    

    然后您可以使用LIKE '<search-term>%' 子句进行高效的“starts-with”查询。

    例如

    SELECT *
    FROM table_name
    WHERE search_text LIKE '555%'
    

    更多信息在这里:https://dev.mysql.com/doc/refman/8.0/en/index-btree-hash.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 2023-03-15
      • 2016-09-22
      • 2013-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多