【问题标题】:php search with latin basic, but return results with diactrics使用拉丁语基本的php搜索,但返回带有变音符号的结果
【发布时间】:2017-04-21 16:05:31
【问题描述】:

我在这里遇到了一个复杂的情况,我希望朝着正确的方向前进。

我需要允许 Basic Latin 搜索带回带有变音符号的结果。由于数据是用 HTML 而不是纯 ASCII 存储的,这使情况变得更加复杂。我一直在取得一些进展,但遇到了两个问题。

首先:我可以将数据部分转换成一些有用的东西,使用类似这样的东西:

$string = 'Véra';
$converted = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
setlocale(LC_ALL, 'en_US.UTF8');
$translit = iconv('UTF-8', 'ASCII//TRANSLIT', $converted);
echo $translit;

这带来了这个结果:V'era 这是一个开始,但我真正需要的是Vera。我可以对结果字符串执行 preg_replace ,但是有没有办法在没有连字符的情况下将它带回来?这只是一个例子;数据库中有更多的变音符号(例如ñ 等等)。我觉得这个问题之前已经解决过(例如iconv returns strange results),但似乎没有列出任何解决方案。

更大的问题:我需要转换一个字符串,例如Vera,并能够用Véra 带回结果。以及Vera 的结果。但是,我相信我需要先解决问题 1,然后才能达到这一点。

我正在考虑类似 if ($translit) { return $string} 但我有点不确定如何处理这个问题。

感谢所有帮助。

编辑:我认为这可能会更容易直接在数据库中完成,但是我遇到了 DQL 的问题。我知道有一些方法可以使用存储过程在 SQL 中执行此操作,但是对数据库的访问权限有限,我愿意在 Doctrine 中提出任何处理此问题的建议

好的,所以也许我让这件事变得太难了

我所需要的只是一种在数据库中查找已被 HTML 编码的条目的方法,而无需使用特定编码但 没有变音符号本身。如果我搜索Jose,它应该会在数据库中显示任何标记为José

【问题讨论】:

  • 您使用的是哪种类型的 SQL 数据库?
  • @ahmetson 这是一个mysql数据库,但是所有的连接都是通过Doctrine/DQL。我知道我可能可以通过存储过程来处理这个问题,但 Ops 似乎不喜欢这样。我可能不得不采用这种方法,但我想先探索其他选择
  • 希望对您有所帮助 - forums.mysql.com/read.php?98,246527,246527
  • @AhmedGinani 谢谢,我确实看到了那个。它看起来确实很有希望;你知道这(或等效的)是否可以在 DQL 中工作?我可能无法在这个特定的数据库中存储过程。
  • 它们是否总是命名实体并且总是acutegravetilde?输入总是 ascii,或者也可以重音和/或实体化?

标签: php symfony utf-8 doctrine


【解决方案1】:

前言:尚不清楚要搜索的数据是否已经在数据库中,或者您是否只是利用了数据库具有字符比较逻辑的事实。我将假设数据源是数据库。

您尝试搜索 html 的事实引发了一个问题,即您是否真的想要搜索 HTML,或者实际上想要搜索 HTML 中的用户可见文本并去除 html 标签(如果在tag 属性?如果一个词被一个空的<span> 打断了怎么办?它应该匹配吗?如果它被一个<br> 打断了怎么办?)


MySQL 有 character sets(字符如何编码)和 collat​​ions(字符如何比较)的概念

相关文档:

假设您的 mysql 客户端/终端正确设置为 UTF8 编码,那么下面演示了覆盖排序规则的效果(使用 ß 作为特别有趣的示例)

> SET NAMES 'utf8';
> SELECT
   'ß',
   'ss',
   'ß' = 'ss' COLLATE utf8_unicode_ci AS ss_unicode,
   'ß' = 'ss' COLLATE utf8_general_ci AS ss_general,
   'ß' = 's' COLLATE utf8_general_ci AS s_general;
+----+----+------------+------------+-----------+
| ß  | ss | ss_unicode | ss_general | s_general |
+----+----+------------+------------+-----------+
| ß  | ss |          1 |          0 |         1 |
+----+----+------------+------------+-----------+
1 row in set (0.00 sec)

注意:generalunicode 排序规则的更快但不严格正确的版本——但如果你说土耳其语,即使这样也是错误的(参见:dotted uppercase i

我会将解码后的 html 保存在数据库中并在此进行搜索,以确保排序规则设置正确。

  • 使用SHOW CREATE TABLE xxx 确认表/列排序规则正确。手动更改 (ALTER TABLE ...),或按照 this answer 使用原则注释并使用原则迁移进行更新(并随后使用 SHOW CREATE TABLE 确认您的原则版本尊重排序规则)
  • 确认原则是configured 以使用utf8 编码。

如果您只需要覆盖某个特定查询的排序规则(例如,您无权更改数据库结构,否则会破坏其他代码):

  • 如果您需要映射到学说 ORM 对象,请使用 NativeQuery 并按照上面的示例添加 COLLATE 覆盖。

  • 如果您只需要记录 ID 和字段,那么您可以使用直接的 query 绕过 ORM 并使用 COLLATE 覆盖

【讨论】:

  • 我应该提到,我无法更改数据库。
  • 你不能改变数据库结构,或者它的内容? (或两者兼而有之?)是否允许添加? (例如新列或新用户定义函数?
  • 结构(就像在存储过程中添加一样,尽管我可以重新审视这个)。如有必要,我可以向架构添加更改(所以对新列是)
  • 如果不添加解码为 utf8 的预先计算的列(或缓存表,如果您愿意),我看不到任何模糊的执行方式; PHP 从 5.3 开始有自己的 Collat​​or 类,但要使用它,您必须拉回所有数据库结果并在 PHP 中比较它们,这会很慢。
【解决方案2】:

您可以在请求时使用 REGEX_REPLACE 函数去除数据库中的变音符号。 Mysql 数据库没有内置的 regex_replace 函数,但是你可以使用用户定义的库,或者将库更改为 MariaDB。基于 Mysql 的 MariaDB(将数据迁移到 MariaDB 将很容易)。

然后在 MariaDB 中,您可以使用如下查询:

SELECT * FROM `test` WHERE 'jose' = REGEXP_REPLACE(name, '(&[A-Za-z]*;)', '')
// another variant with PHP variable
SELECT `table`.name FROM `table` WHERE $search = REGEXP_REPLACE(name, '(&[A-Za-z]*;)', '')  

甚至 phpMyAdmin 也支持 MariaDB。我在演示页面上测试了我的查询。它工作得很好:


或者如果你想留在 MySql 上,添加这个 UDF:

https://github.com/mysqludf/lib_mysqludf_preg

【讨论】:

  • 如前所述,这是一个无法以这种方式修改的大型现有数据库。
猜你喜欢
  • 2017-02-10
  • 2018-02-13
  • 2020-02-12
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多