个性化阅读
专注于IT技术分析

如何使用Doctrine和Symfony 3实现全文搜索(MySql)

如果你的查询(如%my search%)无法满足你查询结果的要求, 那么你来对地方了。

在数据库中, 查找where子句中定义的内容时, 通常使用索引来提高性能。但是, 当涉及到过滤某些文本时, 例如使用whereTextColumn LIKE’%searchstring%'(这是我们可以轻易使用的原理)之类的东西, 则搜索速度很慢并且不适合更灵活的搜索词, 因为常规方式数据库索引的工作进行了优化, 以匹配列的”全部内容”, 而不仅仅是一部分。特别是LIKE搜索不能使用任何类型的索引。

你可以使用match()对()语句在MySQL中实现全文搜索。 MATCH()采用逗号分隔的值, 该值指示需要在其中找到你的值的列。 AGAINST()需要一个字符串进行搜索, 并使用一个可选的修饰符来确定要执行的搜索类型(自然, 布尔值等)。你需要将全文索引添加到数据库中的字段。

在MySQL中以布尔模式与查询进行简单匹配:

SELECT * FROM myTable WHERE match(fieldName) against('I search this text' IN BOOLEAN MODE) LIMIT 10;

要将准则2中的match和对抗语句与MySQL配合使用, 我们需要:

  • 创建MatchAgainst函数
  • 在symfony配置(config.yml)中注册自定义函数
  • 将FULLTEXT索引添加到数据库所需的字段中
  • 执行一些查询!

注意:本教程也适用于Symfony <2.x版本。

MatchAgainst类别

在包中创建一个名为Extensions的文件夹(或根目录/ src), 然后在名为Doctrine的内部创建一个文件夹。在doctrine文件夹中创建一个名为MatchAgainst.php的新类, 并在其中设置以下代码。

不要忘记根据项目中的位置更改名称空间。

<?php
// myBundle/Extensions/Doctrine/MatchAgainst.php
 
namespace myBundle\Extensions\Doctrine;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;

/**
 * "MATCH_AGAINST" "(" {StateFieldPathExpression ", "}* InParameter {Literal}? ")"
 */
class MatchAgainst extends FunctionNode {

    public $columns = array();
    public $needle;
    public $mode;

    public function parse(\Doctrine\ORM\Query\Parser $parser) {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        do {
            $this->columns[] = $parser->StateFieldPathExpression();
            $parser->match(Lexer::T_COMMA);
        } while ($parser->getLexer()->isNextToken(Lexer::T_IDENTIFIER));
        $this->needle = $parser->InParameter();
        while ($parser->getLexer()->isNextToken(Lexer::T_STRING)) {
            $this->mode = $parser->Literal();
        }
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) {
        $haystack = null;
        $first = true;
        foreach ($this->columns as $column) {
            $first ? $first = false : $haystack .= ', ';
            $haystack .= $column->dispatch($sqlWalker);
        }
        $query = "MATCH(" . $haystack .
                ") AGAINST (" . $this->needle->dispatch($sqlWalker);
        if ($this->mode) {
            $query .= " " . $this->mode->dispatch($sqlWalker) . " )";
        } else {
            $query .= " )";
        }
        
        return $query;
    }

}

在config.yml中注册功能

匹配不是你可以为该理论实现的唯一自定义函数, 因此需要易于自定义。只需在ORM的dql属性中使用类路径注册MATCH_AGAINST属性。

# Doctrine Configuration
doctrine:
    # Search for the ORM property
    orm:
        # Those values should be already in your file and this doesn't matter
        auto_generate_proxy_classes: "%kernel.debug%"
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true
        # We need this the dql property to register the custom doctrine functions :
        dql:
            string_functions:
                # Match agains should have the path to the MatchAgainst class created in the previous step
                MATCH_AGAINST: myBundle\Extensions\Doctrine\MatchAgainst

将全文索引添加到表字段

要添加全文索引, 你不能仅使用mysql ui添加索引, 因为它不起作用。

MySQL全文图形界面

但为什么 ?如果只有1个具有全文索引的字段, 则它将毫无问题地工作, 并且你不需要在查询中添加索引。

但是, 如果你需要执行多个字段的查询, 则必须使用以下查询手动添加索引:

"-- fulltext_index is the alias that we'll give to the fulltext index"
ALTER TABLE yourTable ADD FULLTEXT fulltext_index(fieldName1, fieldName2, fieldName3)

然后, 你将可以在理论中使用match_against函数而不会出现问题, 否则, 如果尝试使用它而不添加索引, 你将获得:

找不到与列列表匹配的FULLTEXT索引

找不到与列列表匹配的FULLTEXT索引。

在将新字段与已经存在的全文索引相关联时, 也需要小心, 因为你将需要使用新名称(正确的是, 另一个索引)注册一个新索引。

"-- Note the newfulltext_index that we're adding "
ALTER TABLE yourTable ADD FULLTEXT newfulltext_index(fieldName4, fieldName5)

如果你尝试使用与fieldName4或fieldName5匹配的查询来执行查询, 则将再次得到找不到与列列表匹配的FULLTEXT索引。

所有全文本字段都需要在同一表中与1个全文本索引相关联, 因此请删除以前的全文本索引, 并使用旧字段和新字段添加新的全文本索引。

"-- If you don't know the name of the registered indexes you can use the following line to see them"
SHOW INDEX FROM tableName

"-- Then drop the index using the name as parameter"
ALTER TABLE table DROP INDEX fulltext_index

"-- And finally add the new index with all the fields"
ALTER TABLE yourTable ADD FULLTEXT fulltext_index(fieldName1, fieldName2, fieldName3, fieldName4, fieldName5)

创建查询

自然模式全文搜索示例:

$result = $yourRepository->createQueryBuilder('p')
    ->addSelect("MATCH_AGAINST (p.fieldName1, p.fieldName2, p.fieldName3, :searchterm 'IN NATURAL MODE') as score")
    ->add('where', 'MATCH_AGAINST(p.fieldName1, p.fieldName2, p.fieldName3, :searchterm) > 0.8')
    ->setParameter('searchterm', "Test word")
    ->orderBy('score', 'desc')
    ->getQuery()
    ->getResult();

// with a result structure like :
// [["score" => '0.3123', "0" => "The array with the information of the row (all fields)"]]

前面的示例将匹配包含” Test word”的所有行, 并且记录将根据得分进行递减排序(因为这些行只能包含test或仅单词)。

布尔模式全文搜索示例:

$result = $yourRepository->createQueryBuilder('p')
    ->addSelect("MATCH_AGAINST (p.fieldName1, p.fieldName2, p.fieldName3, :searchterm 'IN BOOLEAN MODE') as score")
    ->add('where', 'MATCH_AGAINST(p.fieldName1, p.fieldName2, p.fieldName3, :searchterm) > 0.8')
    ->setParameter('searchterm', "+microsoft ~windows")
    ->orderBy('score', 'desc')
    ->getQuery()
    ->getResult();

// with a result structure like :
// [["score" => '1.423', "0" => "Only microsoft in this text with fulltext :) "]]

上一个查询将查找包含单词” microsoft”但不包含” windows”的行。


在两种模式下, 得分都没有变化。你决定使用哪种方式应取决于你是否需要布尔模式功能, 在此处全文搜索中有关布尔模式的更多信息以及在此处自然模式的更多信息。

你可以在Beberlei的DoctrineExtensionsRepository中找到更多自定义函数实现, 并使用/ master / src / Query / Mysql目录中的类, 以便仅包含所需的函数(如Soundex, Ceil, Day等)。请记住要正确启用它们, 还要签出此yml文件以查看如何注册自定义功能。

赞(0)
未经允许不得转载:srcmini » 如何使用Doctrine和Symfony 3实现全文搜索(MySql)

评论 抢沙发

评论前必须登录!