PHP/MYSQL-布尔全文搜索-精确字符串运算符(“";”)在预处理语句和SQL查询中给出不同的结果集

PHP/MYSQL-布尔全文搜索-精确字符串运算符(“";”)在预处理语句和SQL查询中给出不同的结果集,php,mysql,exact-match,boolean-search,Php,Mysql,Exact Match,Boolean Search,我在我的网站上制作了一个高级搜索页面,使用PHP编写的语句使用布尔全文搜索查询MySQL数据库MyISAM表 它非常类似于谷歌提供的高级搜索的第一部分,包括以下标准: 所有这些话 确切的词语或陈述(注意:这就是问题所在) 这些词中的任何一个 这些话都没有 我检索每个输入值,清理并处理字符串集合的每个部分,然后通过prepared语句附加适当的信息以形成mySQL查询 因此,本质上,对于以下搜索: 全部- 精确的- 任何-希捷东芝 没有- 将输出为以下字符串: seagate* toshi

我在我的网站上制作了一个高级搜索页面,使用PHP编写的语句使用布尔全文搜索查询MySQL数据库MyISAM表

它非常类似于谷歌提供的高级搜索的第一部分,包括以下标准:

  • 所有这些话
  • 确切的词语或陈述(注意:这就是问题所在)
  • 这些词中的任何一个
  • 这些话都没有
我检索每个输入值,清理并处理字符串集合的每个部分,然后通过prepared语句附加适当的信息以形成mySQL查询

因此,本质上,对于以下搜索:

  • 全部-
  • 精确的-
  • 任何-希捷东芝
  • 没有-
将输出为以下字符串:

seagate* toshiba*
查询将产生如下结果:

SELECT id, description
FROM `items` 
WHERE MATCH (description)
AGAINST ('seagate* toshiba*' IN BOOLEAN MODE)
这将列出所有行,在描述字段中,单词“seagate”后跟任何内容,“toshiba”后跟任何内容

这可以很好地工作,以下输出也可以:

-(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) +("serial ata 600") seagate* toshiba*
+("serial ata 600")
它将如上所述列出所有行,但不包括描述字段中带有“750gb”和“320gb”的任何行

通过向“所有这些单词”字符串添加一个值,我们将得到以下输出:

-(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) +("serial ata 600") seagate* toshiba*
+("serial ata 600")
它将如上所述列出所有行,但仅显示在描述字段中同时包含“16mb”和“7200rpm”的下行

现在查看有问题的部分。如果我使用“语句的确切单词”字符串并添加值“serial ata 600”,我们将得到以下输出:

-(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) +("serial ata 600") seagate* toshiba*
+("serial ata 600")
使用phpmyadmin将此字符串和结果查询作为sql查询运行,我将得到一个由2行组成的结果集,这些行与搜索条件匹配

然而,当在我的网站上运行时,我得到了6行的结果,这表明+(“serial ata 600”)“被完全忽略了

如果我只为字符串“Exact word of statement”输入一个值,那么我们将得到以下输出:

-(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) seagate* toshiba*
+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) +("serial ata 600") seagate* toshiba*
+("serial ata 600")
结果表明,该字符串将列出包含“serial”或“ata”或“600”的所有行

通过在mysql中直接运行相同的查询,该结果将列出所有包含单词“serial ata 600”的行

在报告中指出:

A phrase that is enclosed within double quote (“"”) characters matches 
only rows that contain the phrase literally, as it was typed.
MySQL中就是这种情况,但是当使用PHP作为预处理语句运行相同的查询时,返回不同的结果集

以下是准备好的声明:

if ($result = $link->prepare("
    SELECT id, description
    FROM `items` 
    WHERE MATCH (description)
    AGAINST (? IN BOOLEAN MODE)
"))
{
    $result->bind_param("s", $pattern);
    ... ETC
}
下面是直接在此之前的
$pattern
的输出:

+("serial ata 600")
有没有人可能会提出这种行为的原因,因为我看不出PHP和MySQL在工作方式上有任何不同的原因

我可以提供关于如何根据请求生成字符串的任何附加代码,但输出与我的示例中的一样


任何建议/建议/输入/反馈或评论都将不胜感激。

这是一个事先准备好的陈述平铺直叙的地方。在内部,准备引擎将执行相当于:

$quoted = mysql_real_escape_string('+("serial ata 600")');
这就等于

+(\"serial ata 600\")
现在,您不再使用三个单词的引号短语,而是发送以下单独的单词:

+("serial

ata

600")
这是因为
引号是SQL元字符,您需要将它们视为元字符。但是,由于它们是元字符,prep引擎将引用它们,将它们减少为普通的简引号,现在它们不再包含搜索短语。它们已经成为搜索短语的一部分

不知道这是否真的有效,但您可能必须重写准备好的语句,使其更像

... MATCH AGAINST (CONCAT('("', ?, '")'))

嗨,马克,非常感谢你的投入!实际上,它似乎确实是这样工作的,但实现起来更为复杂。尽管如此,你让我走上了思想链条的正确轨道,我能够解决这个问题,尽管我不太确定如何解决。我将联盟更改为UTF-8 bin,并将所有值转换为大写,这在任何情况下都更加统一。但是现在,我的查询似乎不用concat就可以运行了!谢谢你的帮助<代码>等价于:$quoted=mysql\u real\u escape\u字符串(“+(\“串行ata 600\”))我实际上一直在使用
mysqli\u real\u escape\u string()
来“清理”输入文本,这产生了如您所述的结果,但是在
$pattern
输出之前,再次清理了结果,产生了如我所述的结果。双引号必须按照您在示例中描述的那样进行转义:
+(\“serial ata 600\”
。如果没有转义,结果将返回3个单独的值,正如您所建议的那样。非常感谢您的理解!