[扩展推荐] 将自然语句转换为 Laravel 数据库查询语句(语义搜索)

Example of a search string syntax and its result

使用简单且可自定义的语法,基于一个唯一字符串生成数据库查询

介绍

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 选项是我们到目前为止配置的选项,即列的别名。 operatorvalue 选项允许您根据运算符和值分别限制列查询。 keyoperatorvalue 选项可以是正则表达式模式,也可以是常规字符串,用于完全匹配。

日期列

如果列被标记为 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 列被标记为 booleandate 并使用 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' => [ /* ... */ ],
    ],
];

以下是选项解析的排序:

  1. 使用模型里的配置;
  2. 使用配置信息里 key 是模型类的配置;
  3. 使用配置信息里 key 为 default 的配置;
  4. 最后使用一些默认的选项。

错误处理

提供的搜索字串有时候会出错,一般原因如下:

  • 不符合子串搜索的语法;
  • 尝试请求不存在的字段;
  • 使用错误的操作符;
  • 提供错误的请求值。

以上的这些错误都会抛出 InvalidSearchStringException。你可以在配置信息里设置是否要将这些异常传达到 laravel 默认的异常处理器中,配置信息请见 config/search-string.php

// config/search-string.php
return [
    'fail' => 'all-results', // (默认) 不报错,并记录信息
    'fail' => 'no-results',  // 不报错,不记录信息
    'fail' => 'exceptions',  // 抛出异常

    // ...
];
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://github.com/lorisleiva/laravel-se...

译文地址:https://learnku.com/laravel/t/28220

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7
游离不2

还不如SQL了

4年前 评论
Summer

这个扩展可用来实现类似于 GitHub 高级搜索:https://github.com/search/advanced

例如搜索 GitHub 上 Star 数大于 100 的 PHP 项目,搜索语句为 stars:>100 language:PHP

file

链接:https://github.com/search?utf8=%E2%9C%93&a...=

4年前 评论
junliuxian 2年前

@Summer 感觉可以把这种具体的用途或者适用于哪些方面放到文章前面,可能有些人看了大半天都不知道具体有什么用,不知道具体的使用场景。

4年前 评论

类似自定义的一套搜索语法,不错。

4年前 评论

这不能称为所谓“语义搜索”。

4年前 评论

:blush: 我对此库进行了提炼,有兴趣的同学可以查看博文 精干的查询构建器支持自然语句和数组式语法 来了解更多!

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!