[扩展推荐] 将自然语句转换为 Laravel 数据库查询语句(语义搜索)
使用简单且可自定义的语法,基于一个唯一字符串生成数据库查询
介绍
Laravel Search String提供了一种简单的解决方案,可以使用人类可读和可自定义的语法来确定数据库查询的范围。 它将一个简单的字符串转换为一个强大的查询构建器。 例如,以下搜索字符串将获取未发布或标题为 「我的博客文章」的最新博客文章。
Article::usingSearchString('title:"My blog article" or not published sort:-created_at');
// Equivalent to:
Article::where('title', 'My blog article')
->orWhere('published', false)
->orderBy('created_at', 'desc');
下一个示例将在客户和描述列中搜索术语 「John」,同时确保发票已付款或存档。
Invoice::usingSearchString('John and status in (Paid,Archived) limit:10 from:10');
// Equivalent to:
Invoice::where(function ($query) {
$query->where('customer', 'like', '%John%')
->orWhere('description', 'like', '%John%');
})
->whereIn('status', ['Paid', 'Archived'])
->limit(10)
->offset(10);
如您所见,它不仅提供了一种非常方便的方式来与您的 Laravel API 进行通信(而不是允许许多查询字段),它还可以作为探索其数据的工具呈现给您的用户。
安装
# 通过 composer 安装
composer require lorisleiva/laravel-search-string
# (可选) 发布 search-string.php 配置文件
php artisan vendor:publish --tag=search-string
基础用法
将 SearchString
trait 添加到模型中并配置搜索字符串中使用的列。
use Lorisleiva\LaravelSearchString\Concerns\SearchString;
class Article extends Model
{
use SearchString;
protected $searchStringColumns = [
'title', 'body', 'status', 'rating', 'published', 'created_at',
];
}
使用搜索字符串语法创建数据库查询。
Invoice::usingSearchString('title:"Hello world" sort:-created_at,published')->get();
搜索字符串语法
请注意,操作符之间的空格无关紧要。
// 精确匹配
'title:Hello'
'title=Hello'
'title:"Hello World"'
'rating : 0'
'rating = 99.99'
'created_at: "2018-07-06 00:00:00"'
// 比较
'title < B'
'rating > 3'
'created_at >= "2018-07-06 00:00:00"'
// 在数组中
'title in (Hello, Hi, "My super article")'
'status in(Finished,Archived)'
'status:Finished,Archived'
// 检查日期
// - 术语必须定义为日期
'created_at = today' // today between 00:00 and 23:59
'not created_at = today' // any time before today 00:00 and after today 23:59
'created_at >= tomorrow' // from tomorrow at 00:00
'created_at <= tomorrow' // until tomorrow at 23:59
'created_at > tomorrow' // from the day after tomorrow at 00:00
'created_at < tomorrow' // until today at 23:59
// 检查布尔值
// - 术语必须定义为布尔值
'published' // published = true
'created_at' // created_at is not null
// 否定
'not title:Hello'
'not title="My super article"'
'not rating:0'
'not rating>4'
'not status in (Finished,Archived)'
'not published' // published = false
'not created_at' // created_at is null
// 空值(区分大小写)
'body:NULL' // body 为空
'not body:NULL' // body 不为空
// 搜索查询
// - 术语不能定义为布尔值
// - 至少有一列必须定义为可搜索的
'Apple' // %Aplle% like at least one of the searchable columns
'"John Doe"' // %John Doe% like at least one of the searchable columns
'not "John Doe"' // %John Doe% not like any of the searchable columns
// 与/或嵌套查询
'title:Hello body:World' // Implicit and
'title:Hello and body:World' // Explicit and
'title:Hello or body:World' // Explicit or
'A B or C D' // Equivalent to '(A and B) or (C and D)'
'A or B and C or D' // Equivalent to 'A or (B and C) or D'
'(A or B) and (C or D)' // Explicit nested priority
'not (A and B)' // Equivalent to 'not A or not B'
'not (A or B)' // Equivalent to 'not A and not B'
// 特殊的关键字
'fields:title,body,created_at' // Select only title, body, created_at
'not fields:rating' // Select all columns but rating
'sort:rating,-created_at' // Order by rating asc, created_at desc
'limit:1' // Limit 1
'from:10' // Offset 10
配置列
列别名
如果要使用其他名称查询列,可以将其定义为键/值对,其中键是数据库列名称,值是您要使用的别名。
protected $searchStringColumns = [
'title',
'body' => 'content',
'published_at' => 'published',
'created_at' => 'created',
];
你还可以提供正则表达式模式,以获得更灵活的别名定义。
protected $searchStringColumns = [
'published_at' => '/^published|live$/',
// ...
];
列选项
你可以通过为其分配一组选项来进一步配置列。
protected $searchStringColumns = [
'created_at' => [
'key' => 'created', // Default to column name: /^created_at$/
'operator' => '/^:|=$/', // Default to everything: /.*/
'value' => '/^[\d\s-:]+$/', // Default to everything: /.*/
'date' => true, // Default to true only if the column is cast as date.
'boolean' => true, // Default to true only if the column is cast as boolean or date.
'searchable' => false // Default to false.
],
// ...
];
查询模式
key
选项是我们到目前为止配置的选项,即列的别名。 operator
和 value
选项允许您根据运算符和值分别限制列查询。 key
,operator
和 value
选项可以是正则表达式模式,也可以是常规字符串,用于完全匹配。
日期列
如果列被标记为 date
,则将使用 Carbon
智能地解析查询的值。 例如,如果created_at
列被标记为 date
:
'created_at > tomorrow' // Equivalent to:
$query->where('created_at', '>', 'YYYY-MM-DD 00:00:00');
// where `YYYY-MM-DD` matches the date of tomorrow.
'created_at = "July, 6 2018"' // Equivalent to:
$query->where('created_at', '>=', '2018-07-06 00:00:00');
->where('created_at', '<=', '2018-07-06 23:59:59');
默认情况下,任何转换为日期的列(使用Laravel属性)都将标记为LaravelSearchString 的日期。 通过将 date
指定为 false
,可以强制不将列标记为日期。
布尔列
如果列被标记为 boolean
,则可以在没有任何运算符或值的情况下使用它。 例如,如果 paid
列标记为 boolean:
'paid' // Equivalent to:
$query->where('paid', true);
'not paid' // Equivalent to:
$query->where('paid', false);
如果列被标记为 boolean
和 date
,则当用作布尔值时,它将与 null
进行比较。 例如,如果 published_at
列被标记为 boolean
和 date
并使用 published
别名:
'published' // Equivalent to:
$query->whereNotNull('published');
'not published_at' // Equivalent to:
$query->whereNull('published');
默认情况下,任何强制转换为布尔值或作为日期(使用Laravel属性)的列都将标记为布尔值。 您可以通过将 boolean
指定为 false
来强制不将列标记为布尔值。
可搜索的字段
被标记为 searchable
的字段会被用来匹配搜索请求。
如以下例子, title
和 description
被标示为 searchable
:
'Apple Banana' // 等于以下:
$query->where(function($query) {
$query->where('title', 'like', '%Apple%')
->orWhere('description', 'like', '%Apple%');
})
->where(function($query) {
$query->where('title', 'like', '%Banana%')
->orWhere('description', 'like', '%Banana%');
});
'"John Doe"' // 等于以下:
$query->where(function($query) {
$query->where('title', 'like', '%John Doe%')
->orWhere('description', 'like', '%John Doe%');
});
如果为设置任何 searchable
字段,类似的搜索条件将会被忽视。
配置特殊关键字
protected $searchStringKeywords = [
'select' => 'fields', // 更新所选的查询字段
'order_by' => 'sort', // 更新查询结果的排序方式
'limit' => 'limit', // 限制结果集条数
'offset' => 'from', // 结果集偏移量
];
与字段值类似的,你可以提供一个数组来定义键、操作符以及关键字的值这种模式。注意 date 类型, boolean 类型和可搜索选项等不适用于关键字。
protected $searchStringKeywords = [
'select' => [
'key' => 'fields',
'operator' => '/^:|=$/',
'value' => '/.*/',
],
// ...
];
其他配置项
我们可以使用 searchStringColumns
和 searchStringKeywords
属性来配置搜索字段和关键词。
你也可以通过重写 getSearchStringOptions
方法来达到同样的目的,以下是此方法默认的逻辑:
public function getSearchStringOptions()
{
return [
'columns' => $this->searchStringColumns ?? [],
'keywords' => $this->searchStringKeywords ?? [],
];
}
如果你不想要这些配置信息存在你的 Model 文件里,你可以将它们移到 config/search-string.php
文件中:
// config/search-string.php
return [
'default' => [
'keywords' => [ /* ... */ ],
],
Article::class => [
'columns' => [ /* ... */ ],
'keywords' => [ /* ... */ ],
],
];
以下是选项解析的排序:
- 使用模型里的配置;
- 使用配置信息里 key 是模型类的配置;
- 使用配置信息里 key 为
default
的配置; - 最后使用一些默认的选项。
错误处理
提供的搜索字串有时候会出错,一般原因如下:
- 不符合子串搜索的语法;
- 尝试请求不存在的字段;
- 使用错误的操作符;
- 提供错误的请求值。
以上的这些错误都会抛出 InvalidSearchStringException
。你可以在配置信息里设置是否要将这些异常传达到 laravel 默认的异常处理器中,配置信息请见 config/search-string.php
:
// config/search-string.php
return [
'fail' => 'all-results', // (默认) 不报错,并记录信息
'fail' => 'no-results', // 不报错,不记录信息
'fail' => 'exceptions', // 抛出异常
// ...
];
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
还不如SQL了
这个扩展可用来实现类似于 GitHub 高级搜索:https://github.com/search/advanced
例如搜索 GitHub 上 Star 数大于 100 的 PHP 项目,搜索语句为
stars:>100 language:PHP
:链接:https://github.com/search?utf8=%E2%9C%93&a...=
@Summer 感觉可以把这种具体的用途或者适用于哪些方面放到文章前面,可能有些人看了大半天都不知道具体有什么用,不知道具体的使用场景。
类似自定义的一套搜索语法,不错。
这不能称为所谓“语义搜索”。
:blush: 我对此库进行了提炼,有兴趣的同学可以查看博文 精干的查询构建器支持自然语句和数组式语法 来了解更多!