futape/search

一个易于使用和扩展的搜索框架

4.0.0 2020-04-01 15:32 UTC

This package is auto-updated.

Last update: 2024-09-29 05:29:39 UTC


README

此框架提供了一组基本的实用工具,用于构建您的搜索索引,并使用各种可定制的匹配策略进行搜索。

您在索引对象的选取和结构方面完全自由。
新的匹配器可以在几秒钟内实现并添加。同样也适用于高亮显示。

安装

composer require futape/search

架构

匹配器 & 值

匹配器是用于将索引值与搜索词进行匹配的组件。值和搜索词的类型在技术上完全自由。然而,不同的具体匹配器可能指定或强制类型。
匹配器类始终与值类一起使用,是匹配器可以与之一起使用的类,且是唯一的类。值实例包含实际的值,匹配器将与之匹配。同样,该值的类型在技术上未定义,但值可以指定或强制一个。
除了管理的值之外,值实例还管理一个匹配分数,以及管理值的突出显示版本。
每个值都附加了一个高亮显示器。默认情况下,它初始化为 DummyHighlighter。一旦将可搜索项添加到 Index,索引的高亮显示器(默认为 HtmlHighlighter)将转发到所有由可搜索项提供的值。
请注意,值实例旨在一次由一个可搜索项提供,并且不会被多个可搜索项共享。否则,您可能会遇到未定义的行为。
有一个抽象匹配器和值可以扩展以构建自己的,以及一些预定义的具体匹配器。还有一个 AbstractArrayValueAbstractStringValue 类,它们扩展了 AbstractValue 类,并管理要管理的值为数组或字符串。

标记匹配器

TokenMatcherTokenValue 一起工作,它管理一个一维字符串数组。
在给定的搜索词中严格搜索管理数组。对于每个匹配项,分数增加1,并突出显示匹配值。

文件名匹配器

FilenameMatcherFilenameValue 一起工作,它管理一个字符串路径。
将路径的基本名严格与搜索词进行比较。如果失败,匹配器会尝试匹配路径的文件名,这要求路径不以斜杠结尾或指向一个目录。如果任何尝试匹配,分数增加1,并突出显示匹配部分。

全文匹配器

FulltextMatcherFulltextValue 一起工作,它管理任何任意字符串。
在字符串中搜索搜索词。使用 setWordBoundarySeverity() 配置术语可以存在的区域。下面记录了可用的 WORD_BOUNDARY_SEVERITY_* 常量

  • WORD_BOUNDARY_SEVERITY_EXTRA_HIGH:术语必须由空白字符包围
  • WORD_BOUNDARY_SEVERITY_HIGH:术语必须由任何非字母数字字符(包括 "_")包围
  • WORD_BOUNDARY_SEVERITY_MEDIUM(默认):类似于 WORD_BOUNDARY_SEVERITY_HIGH,但一个数字后面的非数字(或反之亦然)也标记了一个术语边界
  • WORD_BOUNDARY_SEVERITY_LOW:没有任何规则。术语可以在搜索值中的任何位置存在。

如果找到,则分数将增加找到的该词出现的次数,同时也会考虑该词的复杂度(即其长度/词数)。

创建您自己的匹配器

要创建自己的匹配器,只需创建一个扩展AbstractMatcher类的类和一个扩展AbstractValue类的类。然后设置匹配器类的SUPPORTED_VALUE常量为您值类的FQCN。
在您的匹配器类中,您必须实现matchValue方法,该方法接受您的值类实例管理的值、搜索词和一个高亮显示实例,以及高亮值和匹配分数的引用。
您的值必须实现resetHighlighted方法,该方法接受值实例管理的值的副本,该副本将成为高亮值,将其重置到初始状态并返回它。通常这意味着使用值的突出显示器的lowlight方法低亮它。

要实现一个仅比较值与词、如果匹配则高亮显示该值并增加分数的匹配器,您可以创建一个如下所示的类

EqualsMatcher.php

use Futape\Search\Matcher\AbstractMatcher;

class EqualsMatcher extends AbstractMatcher
{
    const SUPPORTED_VALUE = EqualsValue::class;

    /**
     * @param mixed $value
     * @param mixed $term
     * @param HighlighterInterface $highlighter
     * @param mixed $highlighted
     * @param int $score
     */
    protected function matchValue($value, $term, HighlighterInterface $highlighter, &$highlighted, int &$score): void
    {
        if ($value == $term) {
            $highlighted = $highlighter->highlight($highlighted);
            $score++;
        }
    }
}

EqualsValue.php

use Futape\Search\Matcher\AbstractValue;

class EqualsValue extends AbstractValue
{
    /**
     * @param mixed $highlighted
     * @return mixed
     */
    protected function resetHighlighted($highlighted)
    {
        return $this->getHighlighter()->lowlight($highlighted);
    }
}

高亮显示器

高亮显示器用于突出显示匹配值,以及用于低亮值(即根据高亮显示器的逻辑和字符处理值,但不高亮显示它)。
从技术上讲,高亮显示器可以突出显示任何值,无论是标量值(如字符串或浮点数)还是对象。它不知道它应该处理的值的类型,每个高亮显示器都有不同的行为,并实现自己的高亮显示值的方式,并具有不同的返回值。唯一的例外是HighlighterInterface::highlightAreas()方法,它始终期望一个字符串参数。
例如,字符串高亮显示器试图将值作为字符串突出显示,如果值无法转换为字符串,则失败。其他高亮显示器可能根本不转换值,只需将它们包装在一个特殊的“突出显示”对象中。
除了几个预定义的高亮显示器以及一个抽象的高亮显示器和用于构建您自己的高亮显示器的接口之外,还有一些。

字符串高亮显示器

字符串高亮显示器是为突出显示字符串而设计的。
有两个预定义的字符串高亮显示器

  • PlainHighlighter:以类似markdown的方式突出显示值,用于纯文本(例如**Foo** Bar
  • HtmlHighlighter:使用HTML mark标签突出显示值(例如<mark>Foo</mark> Bar)并转义特殊字符

哑高亮显示器

DummyHighlighter仅存在,以便有一个实现了HighlighterInterface但没有任何功能的高亮显示器。它只是将值原样返回。

创建您自己的高亮显示器

要构建您自己的高亮显示器,您可以从头开始实现HighlighterInterface或通过扩展AbstractStringHighlighter
后者是一个用于创建字符串高亮显示器的抽象类。您唯一需要做的就是定义一个开始突出显示部分的单个字符串和一个结束它的字符串。

YellHighlighter.php

use Futape\Search\Highlighter\AbstractStringHighlighter;

class YellHighlighter extends AbstractStringHighlighter
{
    /** @var string */
    protected $opening = '!!!';

    /** @var string */
    protected $closing = '!!!';
}

当编写自己的HighlighterInterface::highlightAreas()实现时,您可能想要使用HighlighterHelper::processAreas()方法,该方法规范化并验证区域数组。

索引对象(可搜索的)

这些是您可以搜索的索引中的对象。
因为要匹配的数据源对这个框架来说是完全未知的,并且属于您的应用程序,因此没有具体的类来构建索引对象。
相反,您必须创建自己的Searchables

Searchables是包含某些实体数据的各种值对象的对象。如何检索这些数据完全取决于您。
当索引被搜索时,值对象被传递给支持这些值对象的匹配器,并由它们与搜索词进行比较。

要创建自己的可搜索对象,您可以实现SearchableInterface接口或扩展AbstractSearchable类。
在扩展AbstractSearchable类时,唯一需要实现的方法是initMatcherValues,它在构造函数中被调用,并且应该通过调用registerMatcherValue方法来填充匹配器值。您可以进行如下操作。

use Futape\Search\AbstractSearchable;
use Your\Domain\Model\Article;

class ArticleSearchable extends AbstractSearchable {
{
    /** @var Article */
    protected $article;
    
    public function __construct(Article $article)
    {
        $this->article = $article;
        parent::__construct();
    }

    protected function initMatcherValues(): void
    {
        $this->registerMatcherValue('tags', new TokenValue($this->article->getTags()));
        $this->registerMatcherValue('categories', new TokenValue($this->article->getCategories()));
        // ...
    }
    
    public function getArticle(): Article
    {
        return $this->article;
    }
}

再次强调,您完全自由地选择数据的来源。与上面示例中向构造函数传递模型实例不同,您可以直接传递一个值(甚至什么也不传)并执行一些API调用或您能想到的任何操作。

用法

use Futape\Search\Highlighter\PlainHighlighter;
use Futape\Search\Index;
use Futape\Search\Matcher\Token\TokenMatcher;
use Your\Domain\Model\Article;
// See "Indexed Objects (Searchables)" section for this example searchable
use Your\Domain\Search\ArticleSearchable;

$index = (new Index(new PlainHighlighter()))
    // Attach a matcher to process the searchables' values
    ->attachMatcher(new TokenMatcher())
    
    // Add searchables to the index
    ->addSearchable(new ArticleSearchable(new Article(42))) // tags: animals, zoo, plants; categories: nature, plants
    ->addSearchable(new ArticleSearchable(new Article(101))) // tags: park, plants; categories: vacation
    ->addSearchable(new ArticleSearchable(new Article(61))) // tags: skyscrapers, concrete; categories: vacation, cities
    
    // Execute the search
    ->search('plants');

foreach ($index->getSearchables() as $searchable) {
    var_dump($searchable->getScore());
    var_dump($searchable->getMatcherValue('tags')->getScore());
    var_dump($searchable->getMatcherValue('tags')->getHighlighted());
    var_dump($searchable->getMatcherValue('categories')->getScore());
    var_dump($searchable->getMatcherValue('categories')->getHighlighted());
}

/*
first iteration (article 42):
2
1
['animals', 'zoo', '**plants**']
1
['nature', '**plants**']

second iteration (article 101):
1
1
['park', '**plants**']
0
['vacation']

third iteration (article 61):
0
0
['skyscrapers', 'concrete']
0
['vacation', 'cities']
*/

测试

该库通过PHP Unit进行单元测试。

要执行测试,请安装composer依赖项(包括开发依赖项),切换到tests目录,并运行以下命令

../vendor/bin/phpunit