在 Laravel 中使用 Laravel Searchy 扩展包实现基于数据库的轻量级搜索功能

Laravel Searchy
1、简介

Laravel Searchy是一个易上手的、 轻量级的、支持MySQL的Laravel搜索扩展包,使用该扩展包可以简单高效的实现基于模型的数据查询搜索功能,该扩展包还可以基于你所启用的搜索引擎实现模糊搜索和其它加权机制。此外,不需要服务器安装任何其它软件即可开始使用该扩展包。

注:如果要兼容Laravel 4,参考https://github.com/TomLingham/Laravel-Searchy/tree/1.0

2、安装

我们使用Composer来安装该扩展包,在composer.jsonrequire项中添加如下两个依赖:

"require": {
    "tom-lingham/searchy" : "2.*"
}

然后运行composer update将依赖下载到vendor目录。

安装完成后需要注册服务提供者到config/app.phpproviders

TomLingham\Searchy\SearchyServiceProvider::class

顺便在config/app.phpaliases中注册相应门面:

'Searchy' => TomLingham\Searchy\Facades\Searchy::class

3、使用

如果要搜索用户(对应数据表users)的名字和Email:

$users = Searchy::users('name', 'email')->query('John Smith')->get();

同样的功能还可以这样实现:

$users = Searchy::search('users')->fields('name', 'email')->query('John Smith')->get();

在本例中,我们通过fields方法传递搜索字段

以上代码都会返回包含搜索结果的对象数组,如果要返回数据库查询对象实例可以使用getQuery()替代get()方法:

$users = Searchy::search('users')->fields('name', 'email')->query('John Smith')    ->getQuery()->having('relevance', '>', 20)->get();

查询多个字段

例如,你想要搜索用户的名字、Email和用户名,可以这样调用:

$users = Searchy::users('name', 'email', 'username')->query('John Smith')->get();

如果需要动态传入搜索字段,可以以数组形式传递参数:

$users = Searchy::users(['name', 'email', 'username'])->query('John Smith')->get();

查询连接/级联字段

有时候你可能需要搜索级联字段,例如,你想要一次性整体返回first_namelast_name,可以通过两个冒号来分隔字段:

$users = Searchy::users('first_name::last_name')->query('John Smith')->get();

软删除记录

默认情况下,软删除记录不会包含到搜索结果。如果你想要包含软删除记录可以通过在指定表和字段之后添加withTrashed()来实现:

Searchy::users('name')->withTrashed()->query('Batman')->get();

返回指定字段

你可以指定搜索要返回的字段:

$users = Searchy::users('first_name::last_name')->query('John Smith')->select('first_name')->get();
// Or you can swap those around...
$users = Searchy::users('first_name::last_name')->select('first_name')->query('John Smith')->get();

此外,这里还会返回别名字段 relevance,不管输入的是什么。

4、配置

你可以发布配置文件到config目录以覆盖默认配置:

php artisan vendor:publish

在生成的配置文件searchy.php中可以设置搜索使用的默认驱动,目前支持fuzzysimplelevenshtein

搜索时还可以使用如下语法动态设置搜索驱动:

Searchy::driver('fuzzy')->users('name')->query('Batman')->get();

5、搜索驱动

Searchy使用”驱动“来处理多条件字段的匹配,简单来说,驱动就是一个指定分组的”匹配器“,用于基于指定条件匹配字符串。目前Searchy只支持三种驱动:Simple、Fuzzy和Levenshtein(实验性)

Simple

Simple搜索驱动只使用了3个匹配器,每个匹配器都有一个相关的乘法因子以便最好匹配我的测试环境:

protected $matchers = [
    'TomLingham\Searchy\Matchers\ExactMatcher'                 => 100,
    'TomLingham\Searchy\Matchers\StartOfStringMatcher'         => 50,
    'TomLingham\Searchy\Matchers\InStringMatcher'              => 30,
];

Fuzzy

Fuzzy搜索驱动是另外一个匹配器组合。这里面的乘法因子都是我用过的,你也可以手动修改它们:

protected $matchers = [
    'TomLingham\Searchy\Matchers\ExactMatcher'                 => 100,
    'TomLingham\Searchy\Matchers\StartOfStringMatcher'         => 50,
    'TomLingham\Searchy\Matchers\AcronymMatcher'               => 42,
    'TomLingham\Searchy\Matchers\ConsecutiveCharactersMatcher' => 40,
    'TomLingham\Searchy\Matchers\StartOfWordsMatcher'          => 35,
    'TomLingham\Searchy\Matchers\StudlyCaseMatcher'            => 32,
    'TomLingham\Searchy\Matchers\InStringMatcher'              => 30,
    'TomLingham\Searchy\Matchers\TimesInStringMatcher'         => 8,
];

Levenshtein

Levenshtein搜索驱动使用Levenshetein Distance(一种字符串相似度算法)来计算字符串之间的“距离”,这要求你在MySQL中有一个与 levenshtein( string1, string2 )类似的存储过程(这个存储过程在扩展包源码res目录下的SQL文件中):

protected $matchers = [
    'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100
];

6、匹配器

ExactMatcher

完全匹配

StartOfStringMatcher

匹配字符串开头是否一致,比如“hel”和“Hello World”

AcronymMatcher

匹配字符串缩写,例如“fb”和“foo bar”或“Fred Brown”

ConsecutiveCharactersMatcher

匹配字符串是否包含一致的连续字符,此外还会计算匹配字符所占百分比并应用相应的乘法因子,例如搜索“fba”会匹配“foo bar”或者“Afraid of bats”,而不会匹配“fabulous”

StartOfWordsMatcher

匹配字符串开头的单词是否一致,例如,“jo ta”与“John Taylor”或者“Joshua B. Takeshi”

StudlyCaseMatcher

驼峰式匹配,例如“hp”和“HtmlServiceProvider”或者“HashParser”,但不会匹配“hasProvider”

InStringMatcher

字符串包含匹配,例如“smi”和“John Smith”或者“Smiley Face”

TimesInStringMatcher

字符串包含次数匹配,例如搜索“tha”会匹配“I hope that that cat has caught that mouse”(3×)或者“Thanks, it was great!”(1×)

LevenshteinMatcher

参考Levenshtein Driver。

7、扩展

驱动

实现自己的搜索驱动很简单,只需要创建一个继承自TomLingham\Searchy\SearchDrivers\BaseSearchDriver的类并添加一个 $matchers属性数组即可。

匹配器

要创建自己的匹配器,可以创建一个继承自TomLingham\Searchy\Matchers\BaseMatcher的类并重写formatQuery方法,然后返回一个在指定位置以%通配符格式化的字符串。要实现更加高级的扩展需要重写buildQuery方法。

学院君

学院君 has written 548 articles

资深PHP工程师,Laravel学院院长