2

我希望能够使用 php 搜索特定字符串的数组(或者更好的是,mysql 表的列)。但是,我的目标是让它返回它找到的字符串和匹配字符的数量(以正确的顺序)或其他方式来查看搜索结果的合理性,这样我就可以利用该信息来决定是否我想默认显示排名靠前的结果,或者给用户提供排名靠前的几个选项。我知道我可以做类似的事情

$citysearch = mysql_query("  SELECT city FROM $table WHERE city LIKE '$city' ");

但我想不出一种方法来确定它的准确性。

目标是:
a) 如果搜索词是“milwakee”或类似词,则找到“Milwaukee”。
b) 如果搜索词是“west”,则返回“West Bend”和“Westmont”之类的内容。

有人知道这样做的好方法吗?

4

4 回答 4

3

您应该查看MySQL 中的全文搜索。还可以查看 Zend 的 Apache Lucene 项目端口Zend_Search_Lucene

于 2009-01-13T05:42:38.227 回答
2

更多的搜索让我找到了 Levenshtein 距离,然后找到了similar_text,这被证明是最好的方法。

similar_text("input string", "match against this", $pct_accuracy);

比较字符串,然后将准确性保存为变量。Levenshtein 距离决定了从一个字符串到另一个字符串需要对单个字符执行多少删除、插入或替换函数,并允许对每个函数进行不同的加权(例如,您可以使替换成本更高一个字符而不是删除一个字符)。它显然比similar_text 更快但不太准确。我在其他地方读过的其他帖子提到,对于少于 10000 个字符的字符串,速度没有功能差异。

我最终使用了我发现的东西的修改版本来使它工作。这最终保存了前 3 个结果(完全匹配的情况除外)。

$input = $_POST["searchcity"];
$accuracy = 0;
$runner1acc = 0;
$runner2acc = 0;
while ($cityarr = mysql_fetch_row($allcities)) {
  $cityname = $cityarr[1];
  $cityid = $cityarr[0];
  $city = strtolower($cityname);
  $diff = similar_text($input, $city, $tempacc);

  // check for an exact match
  if ($tempacc == '100') {

    // closest word is this one (exact match)
    $closest = $cityname;
    $closestid = $cityid;
    $accuracy = 100;

    break;
  }

  if ($tempacc >= $accuracy) { // more accurate than current leader
    $runner2 = $runner1;
    $runner2id = $runner1id;
    $runner2acc = $runner1acc;
    $runner1 = $closest;
    $runner1id = $closestid;
    $runner1acc = $accuracy;
    $closest  = $cityname;
    $closestid = $cityid;
    $accuracy = $tempacc;
  }
  if (($tempacc < $accuracy)&&($tempacc >= $runner1acc)) { // new 2nd place
    $runner2 = $runner1;
    $runner2id = $runner1id;
    $runner2acc = $runner1acc;
    $runner1 = $cityname;
    $runner1id = $cityid;
    $runner1acc = $tempacc;
  }
  if (($tempacc < $runner1acc)&&($tempacc >= $runner2acc)) { // new 3rd place
    $runner2 = $cityname;
    $runner2id = $cityid;
    $runner2acc = $tempacc;
  }
}

echo "Input word: $input\n<BR>";
if ($accuracy == 100) {
  echo "Exact match found: $closestid $closest\n";
} elseif ($accuracy > 70) { // for high accuracies, assumes that it's correct
  echo "We think you meant $closestid $closest ($accuracy)\n";
} else {
  echo "Did you mean:<BR>";
  echo "$closestid $closest? ($accuracy)<BR>\n";
  echo "$runner1id $runner1 ($runner1acc)<BR>\n";
  echo "$runner2id $runner2 ($runner2acc)<BR>\n";
}
于 2009-01-15T22:41:29.227 回答
0

这可能非常复杂,尽管我确信它们存在,但我个人并不知道有任何好的 3rd 方库。不过,其他人可能会提出一些罐头解决方案。

我过去几次从头开始写过类似的东西。如果您沿着这条路线走,那么您可能不想在 PHP 中单独执行此操作,因为每个查询都涉及获取所有记录并对其执行计算。它几乎肯定会涉及创建一组满足您的规范的索引表。

例如,您必须想出如何想象“密尔沃基”最终拼写为“密尔沃基”的规则。我对此的解决方案是进行元音压缩和重复压缩(不确定这些是否真的是搜索词)。因此,密尔沃基将被索引为:

  • 密尔沃基
  • m_lw__k__
  • m_lw_k_

当搜索查询输入“milwaukee”时,我将对文本输入运行相同的过程,然后在索引表上运行搜索:

SELECT cityId,
       COUNT(*)
  FROM myCityIndexTable
 WHERE term IN ('milwaukee', 'm_lw__k__', 'm_lw_k_')

当搜索查询进入“milwakee”时,我将对文本输入运行相同的过程,然后在索引表上运行搜索:

SELECT cityId,
       COUNT(*)
  FROM myCityIndexTable
 WHERE term IN ('milwaukee', 'm_lw_k__', 'm_lw_k_')

在 Milwaukee(拼写正确)的情况下,它将返回“3”作为计数。

在 Milwakee(拼写错误)的情况下,它将返回“2”作为计数(因为它与m_lw__k__模式不匹配,因为它只有一个元音在中间)。

如果您根据计数对结果进行排序,您最终会满足您的一条规则,即“密尔沃基”最终将作为可能的匹配项排序高于“密尔沃基”。

如果您想以通用方式构建此系统(正如您$table在查询中使用的暗示的那样),那么您可能需要在其中的某个地方使用另一个映射表来将您的术语映射到适当的表。

我并不是说这是最好的(甚至是好的)方法,只是我过去做过的事情,如果你打算在没有第三方解决方案的情况下尝试这样做,可能会对你有用。

于 2009-01-13T05:38:28.560 回答
0

LIKE 最令人抓狂的结果是这个“%man”,这将返回文件中的所有女人!在上市的情况下,也许一个不错的解决方案是继续缩短搜索针。在您的情况下,当您的搜索 $ 与“milwa”一样短时,将出现匹配项。

于 2010-01-23T02:04:18.770 回答