[扩展推荐] 为 Laravel Eloquent 提供搜索支持
在这个系列的第一部分 https://github.com/jarektkaczyk/eloquence – 这个包允许以更加简单的管理方式来使用 Eloquent 模型 - 接下来我将向你介绍 Builder 类的可搜索特性。
假设一个拥有简单好友关系的应用:
// 用户模型
// 多对多关联
public function friends()
{
return $this->belongsToMany(static::class, 'friends', 'user_id', 'friend_id');
}
// 1-1 relation
public function profile()
{
return $this->hasOne(Profile::class);
}
一个简单的基于用户为核心的表 users
,friends
表作为好友多对多关联表,这对于演示来说是足够的。 在用户表中我们只存储邮箱和用户名,在用户简介表 profiles
中存储用户的真实数据比如用户的名字、姓氏(first_name
, last_name
)等。
显然密码、时间戳等其他相关的字段不是我们现在应该关心的内容。
现在, 为了找到一个人,我们可以使用几个字段的组合:users.username
, users.email
, profiles.first_name
, profiles.last_name
, friends.first_name
, friends.last_name
。
以下是原始的 Eloquent 做法::
$name = strtolower(Input::get('query'));
$matchingUsers = User::where(function ($q) use ($name) {
$q->where('email', 'like', '%'.str_replace(' ', '', $name).'%')
->orWhere('username', 'like', "%{$name}%")
->orWhereHas('profile', function ($q) {
$q->where(function ($q) {
$q->where('first_name', 'like', "%{$name}%")
->orWhere('last_name', 'like', "%{$name}%");
});
});
})->get();
它很简单但是缺乏关键的搜索元素 - 相关性分数,也使得搜索朋友行不通。
像这样的查询返回分数需要一些计算,但是很显现,我们没有必要再创造一个谷歌哈?
不过,理论上我们喜欢这样的方式:
$name = 'john doe';
$matchingUsers = User::search($name, [
'profile.last_name' => 20,
'email' => 10,
'username' => 10,
'profile.first_name' => 5,
'friends.username' => 2,
'friends.email' => 2,
'friends.profile.first_name' => 1,
'friends.profile.last_name' => 1,
])->get()
//如果这些字段不轻易变动,那么在模型中定义下它们:
protected $searchableColumns = [
'profile.last_name' => 20,
'email' => 10,
'username' => 10,
'profile.first_name' => 5,
'friends.username' => 2,
'friends.email' => 2,
'friends.profile.first_name' => 1,
'friends.profile.last_name' => 1,
];
//然后简单地调用
User::search($name)->get()
这些列都是定义在模型中的普通关联映射,只需要这些。每个列对应的数字是它的权重,我们通过分数来定义它们的重要性。
Eloquence\Builder
这个包的工作原理是自动地加入关联表格,添加一些履行条款并且通过关联搜索排序结果。
这个功能和得分算法的创意都是基于这个包 https://github.com/nicolaslopezj/searchabl... ,但它更加灵活易于使用,更重要的是速度提高了四倍(甚至更高,实际依赖查询中附加的 where 子句)
通过下面几个例子,你可以知道怎么使用这种方式来搜索。这个搜索方式同样会提升搜索速度 – 全文搜索不会使用索引,所以在大表或者多个连接表进行搜索时会变的很慢。
假如你的需求是这样,你想禁用掉全文索引,并且使用右侧通配符 ( word *) 来代替全文索引。
// 全文搜索 + 指定字段搜索权重:
User::search('john doe', ['email' => 10, 'profile.name' => 20, 'friends.profile.name' => 5])->take(10)->get()
// 全文搜索默认的字段:
User::search('"going to LA"')->get()
// 指定搜索
User::search('"going to LA"', $fulltext = false)->get()
// 通配符搜索
User::search(['going to *', '*LA*', '*NY*'], $fulltext = false)->get()
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
:+1:
不错