PHP / MYSQL - 布尔全文搜索 - 精确字符串运算符(“”)在Prepared Statement和SQL Query中给出不同的结果集

时间:2013-05-21 15:46:11

标签: php mysql exact-match boolean-search

我正在使用PHP预处理语句在我的网站上创建一个高级搜索页面,以使用布尔全文搜索来查询MySQL数据库MyISAM表。

它非常类似于Google提供的高级搜索的第一部分,其中包含以下标准:

  • 所有这些话
  • 确切的词语或陈述(注意:这是问题所在的地方)
  • 任何这些词
  • 这些话都不是

我检索每个输入值,清理并处理字符串集合的每个部分,然后通过准备好的语句附加相应的信息以形成mySQL查询。

所以本质上是针对以下搜索:

  • 全部 -
  • 确切 -
  • Any - seagate toshiba
  • 无 -

将输出为此字符串:

seagate* toshiba*

查询会产生如下内容:

SELECT id, description
FROM `items` 
WHERE MATCH (description)
AGAINST ('seagate* toshiba*' IN BOOLEAN MODE)

其中列出所有行,其中包含“seagate”后跟任何内容,“toshiba”后跟说明字段中的任何内容。

这很好,输出:

-(750gb*) -(320gb*) seagate* toshiba*

其中列出了上述所有行,但在说明字段中排除了“750gb”和“320gb”的所有行。

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

+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) seagate* toshiba*

这将列出上述所有行,但仅显示描述字段中包含“16mb”和“7200rpm”的下降。

现在有问题的部分。如果我要使用“Exact word of statement”字符串并添加值“serial ata 600”,我们将获得输出:

+(16mb*) +(7200rpm*) -(750gb*) -(320gb*) +("serial ata 600") seagate* toshiba*

使用phpmyadmin作为sql查询运行此字符串和结果查询我得到一个匹配搜索条件的2行结果集。

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

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

+("serial ata 600")

结果将指示此字符串将列出包含“serial”或“ata”或“600”的所有行。

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

MySQL definition of this operator中声明:

A phrase that is enclosed within double quote (“"”) characters matches 
only rows that contain the phrase literally, as it was typed.

在MySQL中就是这种情况,但是当使用PHP作为Prepared Statement运行相同的查询时,会返回不同的结果集。

以下是准备好的声明:

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之间的工作方式上有任何不同。

我可以提供任何有关如何根据请求生成字符串的其他代码,但输出与我的示例中的相同。

非常感谢任何建议/建议/意见/反馈或评论。

1 个答案:

答案 0 :(得分:2)

这是一个准备好的陈述落在他们脸上的地方。在内部,准备引擎将做相当于:

$quoted = mysql_real_escape_string('+("serial ata 600")');

,它相当于

+(\"serial ata 600\")

现在你不再使用3个字的引用短语了,你发送的是以下单词:

+("serial

ata

600")

这是因为"引号是SQL元字符,您需要将它们视为元字符。但是,由于它们是元字符,准备引擎会引用它们,将它们缩减为正常的简称引号,现在它们不再包含您的搜索短语。他们已经成为搜索短语的一部分。

不知道这是否真的有用,但你可能不得不重写准备好的陈述更像

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