Winter/wn-search-plugin

Winter CMS 的搜索插件

资助包维护!
wintercms
Open Collective

安装次数: 2,583

依赖关系: 2

建议者: 0

安全: 0

星标: 7

关注者: 5

分支: 2

开放问题: 1

类型: winter-plugin

v1.0.0 2024-07-23 03:37 UTC

This package is auto-updated.

Last update: 2024-09-23 06:17:58 UTC


README

Build Status MIT License Discord

为 Winter 添加全文搜索功能,基于 Laravel Scout 的基础构建。该插件主要作为 Laravel Scout 的包装器,并在 Winter 的架构内提供其所有功能集,同时还包括一些额外的功能,以使其在 Winter 中使用更加便捷。

需求

  • PHP 8.0 或更高版本
  • Winter CMS 1.2.0 或更高版本(由于 Laravel 9 的需求)

入门指南

要安装插件,您可以通过 Winter CMS 市场place 进行安装,或者您可以使用 Composer 安装。

composer require winter/wn-search-plugin

然后,运行迁移以确保插件启用

php artisan winter:up

配置

此插件的配置主要通过 search.php 配置文件进行。您可以通过运行以下命令将此配置发布到项目的 config 目录

php artisan vendor:publish --provider="Winter\Search\Plugin"

这将在 config/winter/search/search.php 创建您的配置文件,其中您可以覆盖所有默认配置值。

准备您的模型

作为包装器,您可以使用 Laravel Scout 提供的所有基本功能。与搜索插件的实现相比,只有几个细微的差异

  • 配置值存储在 search 键中。无论何时提及 scout 配置值,您都必须使用 search
  • 软删除模型通过使用 Winter\Storm\Database\Traits\SoftDelete 特性确定,而不是基于 Laravel 的基本 SoftDeletes 特性。

要使特定的数据库模型可搜索,您只需将该模型的 Winter\Search\Behaviors\Searchable 行为添加到该模型即可。此行为将注册一个模型观察者,该观察者将自动同步模型记录到索引

<?php

namespace Winter\Plugin\Models;

use Model;

class MyModel extends Model
{
    public $implement = [
        \Winter\Search\Behaviors\Searchable::class,
    ];
}

对于 Halcyon 模型,您必须使用 Winter\Search\Behaviors\Halcyon\Searchable 行为,以便正确地挂钩 Halcyon 提供的独特功能。

当模型创建、更新或删除时,索引将自动更新以反映该模型记录的状态。

配置可搜索数据

默认情况下,整个模型将转换为数组形式并持久化到搜索索引中。如果您希望限制存储在索引中的数据,您可以在模型中提供 $searchable 属性。此属性将代表您希望存储在索引中的所有模型属性

<?php

namespace Winter\Plugin\Models;

use Model;

class Post extends Model
{
    public $implement = [
        \Winter\Search\Behaviors\Searchable::class,
    ];

    public $searchable = [
        'title',
        'summary'
    ];
}

如果您想要更多的数据控制,您可以覆盖 toSearchableArray 方法

<?php

namespace Winter\Plugin\Models;

use Model;

class Post extends Model
{
    public $implement = [
        \Winter\Search\Behaviors\Searchable::class,
    ];

    /**
     * Get the indexable data array for the model.
     *
     * @return array
     */
    public function toSearchableArray()
    {
        $array = $this->toArray();

        // Customize the data array...

        return $array;
    }
}

将搜索添加到第三方模型

您可以通过使用 Winter 中的 动态类扩展 功能将搜索功能添加到第三方插件。这通常在 Plugin.php 注册文件中完成,通常位于 boot() 方法中。

以这种方式扩展模型时,您还可能需要指定希望包含在搜索索引中的可搜索数据,使用之前指定的$searchable属性或toSearchableArray()方法。

<?php

namespace Winter\Plugin;

class Plugin extends \System\Classes\PluginBase
{
    public function boot()
    {
        \ThirdParty\Plugin\Models\Model::extend(function ($model) {
            $model->implement[] = \Winter\Search\Behaviors\Searchable::class;

            // Add a dynamic property to specify the searchable data
            $model->addDynamicProperty('searchable', [
                'id',
                'title',
                'description',
            ]);

            // Or, add a dynamic method instead.
            $model->addDynamicMethod('toSearchableArray', function () use ($model) {
                $array = $model->toArray();

                // Customize the data array...

                return $array;
            });
        });
    }
}

配置模型ID

通常,模型的键将作为存储在搜索索引中的模型唯一ID。如果您希望使用其他列作为模型的标识符,您可以覆盖getSearchKeygetSearchKeyName方法来自定义此行为。

<?php

namespace Winter\Plugin\Models;

use Model;

class User extends Model
{
    public $implement = [
        \Winter\Search\Behaviors\Searchable::class,
    ];

    /**
     * Get the value used to index the model.
     *
     * @return mixed
     */
    public function getSearchKey()
    {
        return $this->email;
    }

    /**
     * Get the key name used to index the model.
     *
     * @return mixed
     */
    public function getSearchKeyName()
    {
        return 'email';
    }
}

注意:某些搜索提供商(如Meilisearch)对ID允许的字符有限制。为了安全起见,我们建议您将ID限制为以下字符:A-Za-z0-9、破折号和下划线。任何其他字符可能会阻止某些搜索提供商索引您的记录。

注册搜索处理器

一旦您的模型准备就绪,可以支持搜索功能,您可以注册一个搜索处理器,允许通过包含的组件搜索模型。

在您的Plugin.php文件中通过指定返回数组的registerSearchHandlers方法来注册搜索处理器。

<?php

namespace Acme\Plugin;

class Plugin extends \System\Classes\PluginBase
{
    public function registerSearchHandlers()
    {
        return [
            'mySearch' => [
                'name' => 'My Search',
                'model' => \Winter\Plugin\Models\Post::class,
                'record' => [
                    'title' => 'title',
                    'image' => 'featured_image',
                    'description' => 'description',
                    'url' => 'url',
                ]
            ]
        ];
    }
}

每个数组项应指定一个代表搜索处理器ID的关键字名称。以下属性可以作为处理器的一部分进行指定

记录处理器

每个搜索处理器也可以提供一个结果处理器,以精细调整您希望显示或过滤的结果。在最简单的情况下,记录处理器只需返回一个包含3个属性的数组,每个记录包含以下属性

  • title:结果的标题。
  • description:结果的附加上下文。
  • url:结果将指向的URL。

它还可以可选地提供以下属性以提供更多上下文

  • group:当使用分组结果时,此结果所属的组。
  • label:结果的标签,可能提供更多上下文或对结果进行分组。
  • image:结果对应图像的路径。

当然,您可以在数组中定义额外的属性。

记录处理器可以通过多种方式配置。

字段映射数组

您可以直接返回一个具有上述属性并映射到模型内对应字段的数组。

'record' => [
    'title' => 'title',
    'image' => 'image',
    'description' => 'description',
    'url' => 'url',
]

具有回调的数组映射

类似于上述方法,您还可以指定一些或所有属性使用回调方法,该方法将接收两个参数:每个结果的结果实例和原始查询。

'record' => [
    'title' => 'title',
    'image' => 'image',
    'description' => function ($model, $query) {
        return substr($model->description, 0, 100) . '...';
    },
    'url' => 'url',
]

回调方法

您还可以使整个处理器通过回调方法进行。这提供了最大的控制权,因为您还可以过滤掉记录。

回调方法应始终返回一个包含上述主要属性的数组,但您可以根据需要包含任何附加属性。

回调方法还可以返回false以排除记录。

'record' => function ($model, $query) {
    if ($model->isNotPublished()) {
        return false;
    }

    return [
        'title' => $model->title,
        'image' => $model->image->url(),
        'description' => $model->getDescription(),
        'url' => $model->getUrl(),
    ];
}

结果相关性

默认情况下,搜索插件中的结果没有按任何特定方式排序以考虑结果的相关性。虽然这对于使用具有自己的相关性算法(或可以配置为这样)的索引引擎(如Algolia或Meilisearch)的情况可能很好,但这可能会影响使用没有相关性系统(如数据库和集合索引引擎)的结果。

为了在这些引擎中支持一定级别的关系,可以在从索引检索结果后对结果进行后处理以分配相关性得分。此系统中的相关性由索引中$searchable定义中属性名称的顺序确定。

例如,以下定义

public $searchable = [
    'title',
    'description',
    'keywords',
];

《title》字段将是相关性最高的字段,其次是《description》字段,然后是《keywords》字段。在《title》中的匹配比在《description》中的匹配权重更高,而《description》中的匹配权重又高于《keywords》中的匹配。

相关性评分还会考虑查询中使用的单词。例如,如果使用了查询《install winter cms》,则单词《install》的权重高于《winter》,而《winter》的权重又高于单词《cms》。

要启用结果相关性,您可以在任何搜索之后调用《getWithRelevance()`》或《firstRelevant()`》方法。

$results = \Acme\Blog\BlogSearch::doSearch('install winter cms')->getWithRelevance();

getWithRelevance()将检索所有记录,并按相关性评分排序,而《firstRelevant()`》将只检索最相关的记录。

如果您想自定义相关性评分,也可以为这两种方法提供可调用的函数。这个可调用的函数必须接受两个参数,即模型实例和查询中的单词数组,并返回一个floatint类型的评分,该评分将用于按降序(评分越高,相关性越高)对结果进行排序。索引中找到的每个记录都将通过这个可调用的函数。

$results = \Acme\Blog\BlogSearch::doSearch('install winter cms')->getWithRelevance(function ($model, array $words) {
    // Score each record and return score as a integer or float.
});