【问题标题】:How to search multiple words?如何搜索多个单词?
【发布时间】:2020-12-29 23:31:29
【问题描述】:

到目前为止,我一直在尝试制作一个搜索引擎,但我缺少的功能是,当用户查询一个长句子或两个或三个单词时,我的数据库中的 on not in line-wise 不是将要显示,它显示 0 个结果。

到目前为止,它在单字搜索方面运行良好。

public function getResultsHtml($page, $pageSize, $term){

    $fromLimit = ($page - 1) * $pageSize;

    //page 1: (1-1) * 20
    //page 2:(2-1) * 20

    $query = $this->con->prepare("SELECT *
                                    FROM sites WHERE title LIKE :term
                                    OR url LIKE :term
                                    OR keywords LIKE :term
                                    OR description LIKE :term
                                    ORDER BY clicks DESC
                                    LIMIT :fromLimit,:pageSize");

    $searchTerm = "%" . $term ."%";

    $query->bindParam(":term",$searchTerm);
    $query->bindParam(":fromLimit",$fromLimit,PDO::PARAM_INT);
    $query->bindParam(":pageSize",$pageSize,PDO::PARAM_INT);
    $query->execute();

    $resultsHtml ="<div class='siteResults'>";

    while($row = $query->fetch(PDO::FETCH_ASSOC)){
        $id = $row["id"];
        $url = $row["url"];
        $title = $row["title"];
        $description = $row["description"];

        $title = $this->trimField($title,55);
        $description = $this->trimField($description,230);

        $resultsHtml .="<div class='resultContainer'>

                            <h3 class='title'>
                                <a class='result' href='$url' data-linkId='$id'>
                                    $title
                                </a>
                            </h3>
                            <span class='url'>$url</span>
                            <span class='description'>$description</span>

                        </div>";


    }

    $resultsHtml .= "</div>";

    return $resultsHtml;


}

因此,当用户搜索苹果时,它正在检索数据,我们可以在第一张图片的“苹果商店搜索”中看到搜索结果。但是在第二张图片中,当我们搜索“苹果搜索”时,它应该能够向我们展示“苹果商店搜索”。

第一张图片

第二张图片

【问题讨论】:

  • 向我们展示一些用户输入的示例以及应该匹配什么样的数据
  • 您可以对条件进行分组:WHERE (col1 LIKE :word1 OR col2 LIKE :word1) AND (col1 LIKE :word2 OR col2 LIKE :word2)。您拥有的作品越多,您可以添加更多AND 组。这将要求所有单词都存在,但可以在不同的列中。不过,您需要先在空间上爆炸单词,以便可以将它们作为单独的值传递。
  • 要么将用户的输入划分为单独的单词/标记并单独搜索它们,要么使用 FTS。
  • 感谢您提供清晰的解决方案,我已添加图像

标签: php html mysql sql


【解决方案1】:

首先,您需要将 $term 分解为单独的单词。对于欧洲语言,您可以简单地使用explode:

<?php

$terms = explode(' ', $term);

// lets say your $term is 'apple orange lemon banana'
// $terms would be ['apple', 'orange', 'lemon', 'banana']

为绑定准备条款列表

然后,我将为术语绑定构建一个键值数组:

<?php

// build a term list with term key (:term{number}) and associated term
// for binding
$term_params = [];
foreach ($terms as $key => $term) {
        $term_params[":term{$key}"] = "%{$term}%";
}

// $term_params would be:
// [
//   ':term0' => '%apple%',
//   ':term1' => '%orange%',
//   ':term2' => '%lemon%',
//   ':term3' => '%banana%',
// ]

为绑定准备 SQL 中的 Where 子句

现在,假设逻辑是需要在任一目标字段中显示的所有术语:

<?php

// a list of fields to search from
$fields = ['title', 'url', 'keywords', 'description'];

// build a where clause SQL based on the terms and fields
$or_where_clauses = [];
foreach (array_keys($term_params) as $term_key) {
      $or_where_clauses[] = implode(' OR ', array_map(function ($field) use ($term_key) {
                return "{$field} LIKE {$term_key}";
        }, $fields));
}

$where_clauses_sql = implode(' AND ', array_map(function ($term_clause) {
        // quote each term clause with bracket for proper OR logic to work
        return '(' . $term_clause . ')';
}, $or_where_clauses);

生成的 where 子句 sql 将是:

(title like :term0 OR url like :term0 OR keywords like :term0 OR description like :term0)
AND
(title like :term1 OR url like :term1 OR keywords like :term1 OR description like :term1)
AND
...
...
(title like :termN OR url like :termN OR keywords like :termN OR description like :termN)

把所有东西放在一起

然后我可以构建 SQL 字符串并相应地将术语绑定到查询:

<?php

// prepare the select statement
$query = $this->con->prepare("SELECT *
                            FROM sites WHERE {$where_clauses_sql}
                            ORDER BY clicks DESC
                            LIMIT :fromLimit,:pageSize");

// bind the terms
foreach ($term_params as $key => $value) {
        $query->bindParam($key, $value);
}
$query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT);
$query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT);
$query->execute();

// ...

缺点和可能的解决方案

请注意,这种方法不了解该字段中术语的出现次数。分隔单词的方法远非完美。例如:

  1. 它不处理标点符号。
  2. 它无法搜索带有单数的复数词,或vis verse。
  3. 可能会错误地匹配单词的一部分(例如搜索“sing”并匹配“dressing”)。
  4. 它无法处理 CJK 或没有空格的语言)。

您可以使用Elastic Search 之类的软件来获得更好的功能。通过插件和适当的配置,您甚至可以给出具有相关性的排序结果,或者在搜索过程中赋予不同的字段不同的重要性等。

但这与使用的 SQL Server 完全不同。您需要学习和计划如何在其中索引您的内容,以及如何将数据保存到 SQL。

【讨论】:

    【解决方案2】:

    我们不知道您如何从用户那里获取搜索词,但我猜您将它们作为数组获取。所以你可以试试下面的代码:

    <?php
    ...
    $textSearch = "";
    for($i = 0 ; $i< count($userInputs);$i++){
        if($i !== count($userInputs) -1){
            $userInputData       = $userInputs[$i]; 
            $textSearch         .= "'%$userInputData%' OR";
        }else{
            $textSearch         .= "'%$userInputData%'";
        }
    }
    

    并将$textSearch 放入您的查询中。 请记住,$userInputs 是您之前拥有的数组。

    更新

    如图所示,您可以在给定代码的开头添加$userInput = explode(" ",$textFromUser)

    【讨论】:

    • 感谢您提供清晰的解决方案,我已添加图像
    • 我从索引页面的一个输入表单中获取搜索词到搜索页面
    • 我做了一个更新来澄清它,如果你从用户那里得到输入,只需用 $textFromUser 替换它,祝你好运
    • "%$userInputData% AND" - AND 将成为搜索词的一部分(因为搜索词周围没有引号)?您还应该使用准备好的语句,而不是像这样将数据注入到查询中。
    • @VikasKonaparthi,你必须在 explode 的第一个参数中添加一个空格,例如 " ";
    猜你喜欢
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 2011-10-06
    • 1970-01-01
    相关资源
    最近更新 更多