Home > Article > Backend Development > 在 Laravel 中使用 Laravel Searchy 扩展包实现基于数据库的轻量级搜索功能
Laravel Searchy是一个易上手的、 轻量级的、支持MySQL的Laravel搜索扩展包,使用该扩展包可以简单高效的实现基于模型的数据查询搜索功能,该扩展包还可以基于你所启用的搜索引擎实现模糊搜索和其它加权机制。此外,不需要服务器安装任何其它软件即可开始使用该扩展包。
注:如果要兼容Laravel 4,参考 https://github.com/TomLingham/Laravel-Searchy/tree/1.0
我们使用Composer来安装该扩展包,在 composer.json 的 require 项中添加如下两个依赖:
"require": { "tom-lingham/searchy" : "2.*"}
然后运行 composer update 将依赖下载到 vendor 目录。
安装完成后需要注册服务提供者到 config/app.php 的 providers :
TomLingham\Searchy\SearchyServiceProvider::class
顺便在 config/app.php 的 aliases 中注册相应门面:
'Searchy' => TomLingham\Searchy\Facades\Searchy::class
如果要搜索用户(对应数据表 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_name 和 last_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 ,不管输入的是什么。
你可以发布配置文件到 config 目录以覆盖默认配置:
php artisan vendor:publish
在生成的配置文件 searchy.php 中可以设置搜索使用的默认驱动,目前支持 fuzzy 、 simple 和 levenshtein 。
搜索时还可以使用如下语法动态设置搜索驱动:
Searchy::driver('fuzzy')->users('name')->query('Batman')->get();
Searchy使用”驱动“来处理多条件字段的匹配,简单来说,驱动就是一个指定分组的”匹配器“,用于基于指定条件匹配字符串。目前Searchy只支持三种驱动:Simple、Fuzzy和Levenshtein(实验性)
Simple搜索驱动只使用了3个匹配器,每个匹配器都有一个相关的乘法因子以便最好匹配我的测试环境:
protected $matchers = [ 'TomLingham\Searchy\Matchers\ExactMatcher' => 100, 'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, 'TomLingham\Searchy\Matchers\InStringMatcher' => 30,];
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搜索驱动使用Levenshetein Distance(一种字符串相似度算法)来计算字符串之间的“距离”,这要求你在MySQL中有一个与 levenshtein( string1, string2 ) 类似的存储过程(这个存储过程在扩展包源码 res 目录下的SQL文件中):
protected $matchers = [ 'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100];
完全匹配
匹配字符串开头是否一致,比如“hel”和“Hello World”
匹配字符串缩写,例如“fb”和“foo bar”或“Fred Brown”
匹配字符串是否包含一致的连续字符,此外还会计算匹配字符所占百分比并应用相应的乘法因子,例如搜索“fba”会匹配“foo bar”或者“Afraid of bats”,而不会匹配“fabulous”
匹配字符串开头的单词是否一致,例如,“jo ta”与“John Taylor”或者“Joshua B. Takeshi”
驼峰式匹配,例如“hp”和“HtmlServiceProvider”或者“HashParser”,但不会匹配“hasProvider”
字符串包含匹配,例如“smi”和“John Smith”或者“Smiley Face”
字符串包含次数匹配,例如搜索“tha”会匹配“I hope that that cat has caught that mouse”(3×)或者“Thanks, it was great!”(1×)
参考Levenshtein Driver。
实现自己的搜索驱动很简单,只需要创建一个继承自 TomLingham\Searchy\SearchDrivers\BaseSearchDriver 的类并添加一个 $matchers 属性数组即可。
要创建自己的匹配器,可以创建一个继承自 TomLingham\Searchy\Matchers\BaseMatcher 的类并重写 formatQuery 方法,然后返回一个在指定位置以%通配符格式化的字符串。要实现更加高级的扩展需要重写 buildQuery 方法。