onurakman / laravel-meilisearch
简单的Laravel封装,用于使用Meilisearch
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.4
- illuminate/http: ^8|^9
- illuminate/support: ^8|^9
Requires (Dev)
- orchestra/testbench: ^6.0
- phpunit/phpunit: ^9.5
README
当您想在Laravel应用程序中使用Meilisearch时,可以使用Laravel Scout。这是一个将模型同步到Meilisearch并快速搜索模型的好方法。然而,有时Laravel Scout不足以满足需求。例如,如果您想
- 对您的Meilisearch数据库有更多控制:例如,不仅仅保存模型。
- 设置可搜索、可过滤或可排序的属性。
- 对Meilisearch执行更复杂的查询,例如使用多个过滤器。
- 使用查询构建器从Meilisearch数据库获取数据。
- 使用Meilisearch未提供的某些功能。例如,以随机顺序显示文档或显示在过滤文档中不可用的分面。
该包处理这类情况。您决定发送哪些信息到Meilisearch,以及想要哪些返回信息。专门为Meilisearch构建的查询构建器有助于构建更复杂的查询。
使用此包时,您应自行决定何时以及发送哪些数据到Meilisearch。因此,如果希望在模型保存或创建后自动将模型发送到Meilisearch,Laravel Scout可能是一个更好的解决方案。
与Meilisearch的兼容性
目前,此包支持Meilisearch版本0.27。Meilisearch 0.28版本引入了一些破坏性变更。兼容0.28版本的新版本将很快发布。
安装
当使用Meilisearch版本0.27时,使用以下命令
composer require eelcol/laravel-meilisearch:<=1
当使用Meilisearch版本0.28时
composer require eelcol/laravel-meilisearch:^1.0
设置.env
将您的.env更改为包括以下变量
MEILISEARCH_HOST=...
MEILISEARCH_KEY=...
发布资产
php artisan vendor:publish --tag=laravel-meilisearch
入门指南
创建索引
首先,您需要创建一个索引以保存文档。例如,您需要一个索引来保存我们的产品目录。因此,可以使用以下命令
php artisan meilisearch:create-index products
此命令将创建一个名为database/meilisearch/products.php
的文件。在此文件中,您可以调整此索引的设置。这不是必需的,但强烈推荐。如果您保留标准设置,Meilisearch将使用您数据的所有列进行搜索。为了实现这一点,Meilisearch必须索引您数据的所有列。这将花费更长的时间,并使用更多的服务器资源。这就是为什么建议指定哪些列应该是可搜索的、可过滤的和可排序的。
每次您想更改设置时,只需更改此文件即可。更改后,运行以下命令。
将索引迁移到Meilisearch数据库
现在,必须实际创建索引。为此,请运行以下命令
php artisan meilisearch:set-index-settings
这与Laravel的数据库迁移类似。首先,您必须创建一个数据库迁移,然后运行迁移以实际创建表或进行调整。
每次您更改database/meilisearch/products.php
文件时,都要运行此命令。此外,在每次部署时也要运行此命令,以确保生产中有最新的Meilisearch实例。
Meilisearch中的主数据
Meilisearch文档中提到的所有功能都包含在此包中。以下列出了一些最重要的功能:
插入数据
要将文档插入到索引products
中,您可以执行以下操作之一
Meilisearch::addDocument('products', [ 'id' => 1, 'title' => 'iPhone SE' ]);
Meilisearch::addDocuments('products', [ [ 'id' => 1, 'title' => 'iPhone SE' ], [ 'id' => 2, 'title' => 'Samsung Galaxy' ] ]);
您也可以直接插入一个模型或集合。模型被转换为数组。为了执行此操作,包将检查对象上是否存在以下方法,按以下顺序
- toMeilisearch() - toSearchableArray() - toArray()
例如,一个Product
模型可以看起来像以下这样
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; class Product extends Model { use HasFactory; protected $guarded = ['id']; public function toMeilisearch(): array { return [ 'id' => $this->id, 'title' => $this->title, 'slug' => Str::slug($this->title), ]; } }
模型可以像这样插入
$product = App\Models\Product::find(1); Meilisearch::addDocument('products', $product); // the product will be inserted like: // [ // 'id' => 1, // 'title' => 'iPhone SE', // 'slug' => 'iphone-se' // ]
也可以直接插入一个集合
$products = App\Models\Product::all(); Meilisearch::addDocuments('products', $products);
检索数据
可以使用 getDocuments
方法检索索引的文档。当您想要应用过滤器时,可以使用查询构建器。数据将自动分页。
$documents = Meilisearch::getDocuments('products');
使用查询构建器
如果您想应用过滤或排序,我建议使用查询构建器。您可以在测试文件夹中查看一些示例。下面列出了几个简单示例。
根据属性过滤
可以使用 where
方法进行简单过滤
$documents = MeilisearchQuery::index('products') ->where('title', '=', 'iPhone SE') ->get(); $documents = MeilisearchQuery::index('products') ->where('price', '<', 100) ->get();
多个 wheres
也可以组合使用
$documents = MeilisearchQuery::index('products') ->where('title', '=', 'iPhone SE') ->where('price', '<', 100) ->get();
使用 'or' 过滤
目前,在顶层上无法使用 'OR' 进行过滤。如果您想使用 'OR' 进行过滤,必须首先创建一个 'where-group'。以下调用将生成错误
$documents = MeilisearchQuery::index('products') ->where('title', '=', 'iPhone SE') ->orWhere('title', '=', 'Samsung Galaxy') ->get();
以下代码将正常工作
$documents = MeilisearchQuery::index('products') ->where(function ($q) { $q->where('title', '=', 'iPhone SE'); $q->orWhere('title', '=', 'Samsung Galaxy'); }) ->get();
这是因为 Meilisearch 过滤器的工作方式和此包渲染过滤器的方式,同时也防止了在组合 'AND' 和 'OR' 语句时可能出现的问题。例如,以下查询可能会返回意外的结果
$documents = MeilisearchQuery::index('products') ->where('title', '=', 'iPhone SE') ->orWhere('title', '=', 'Samsung Galaxy') ->where('price', '<', 100) ->get();
这个查询应该是
- (title = 'iPhone SE' OR title = 'Samsung Galaxy') AND price < 100 - title = 'iPhone SE' OR (title = 'Samsung Galaxy' AND price < 100) - etc...
所以现在,在使用 'OR' 语句时,您应该首先开始一个 where
-group。
在 ... 中
这与数组配合得最好。例如,您有一个具有多个类别的产品
[ 'id' => 1, 'title' => 'iPhone SE', 'categories' => [ 'phones', 'smartphones', 'iphones' ], 'id' => 2, 'title' => 'Samsung Galaxy', 'categories' => [ 'phones', 'smartphones', 'samsung' ], ]
这些数据可以进行查询
MeilisearchQuery::index('products') ->whereIn('categories', ['phones', 'iphones']) ->get();
whereIn
方法将检查 至少有一个值 存在于模型上。因此,上述查询将返回 所有 文档。
使用 'matches' 过滤
whereIn
方法将检查至少有一个值存在于模型上。而 whereMatches
方法将检查 所有 值都存在于模型上
// this query will return both iPhone SE and Samsung Galaxy MeilisearchQuery::index('products') ->whereMatches('categories', ['phones', 'smartphones']) ->get(); // this query will return ONLY the iPhone SE MeilisearchQuery::index('products') ->whereMatches('categories', ['phones', 'iphone']) ->get(); // this query will return ONLY the Samsung Galaxy MeilisearchQuery::index('products') ->whereMatches('categories', ['phones', 'samsung']) ->get();
使用分面
被标记为 filterable
的列可以用作分面。查询构建器将返回这些分面,并附带产品计数。可以通过使用 setFacets
或 addFacet
方法来定义分面
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->setFacets([ 'color', 'brand' ]) ->get(); MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->addFacet('color') ->addFacet('brand') ->get();
析取分面分布
在当前版本的 Meilisearch 中,当您对属性进行过滤时,不会返回该属性的分面。请参阅以下讨论:meilisearch/product#187
例如,当您运行上述查询时,将返回颜色 grey
、silver
、gold
、yellow
。接下来,您只想显示颜色为 yellow
的产品。因此,您应用了一个过滤器
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->where('color', '=', 'yellow') ->setFacets([ 'color', 'brand' ]) ->get();
但是,当您这样做时,分面 color
现在将仅返回 yellow
。这使得向最终用户显示所有可能的颜色变得更加困难。这就是为什么这个包有一个 keepFacetsInMetadata
方法。您可以在其中应用过滤器,这些过滤器在检索元数据时将不会应用。
当使用 keepFacetsInMetadata
方法时,该包将创建两个 Meilisearch 查询。一个查询应用了所有过滤器来获取产品,另一个查询应用了部分过滤器来获取元数据(分面)。
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->keepFacetsInMetadata(function ($q) { $q->where('color', '=', 'yellow'); }) ->setFacets([ 'color', 'brand' ]) ->get();
这样,返回的数据将包含在 phones
类别中的产品上可用的所有分面。因此,您可以在对颜色进行过滤时轻松显示所有可用的颜色。
请注意,此方法将生成另一个查询。因为大多数时候,Meilisearch 查询都非常快(< 10ms),我相信这不会对网站速度造成任何重大影响。
限制和偏移量
可以轻松地将限制和偏移量添加到查询中。以下查询将返回 10 个结果,从第 20 个结果开始
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->limit(10) ->offset(20) ->get();
分页结果
就像使用 Laravel 查询构建器进行数据库查询一样,您可以分页来自 Meilisearch 的结果。只需使用 paginate
方法即可。当使用此方法时,之前的 limit
和 offset
调用将被忽略。
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->paginate(10);
可选地提供要使用的查询参数名称以获取当前页。默认使用 'page'。
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->paginate(10, 'pageNumber');
对结果进行排序
随机顺序
默认情况下,Meilisearch不提供随机排序文档的选项。然而,有时你可能希望显示一些随机产品。为了实现这一功能,本包添加了这一功能。请注意,该包将向你的Meilisearch数据库进行查询,针对每一个随机元素,还会额外进行1次查询。所以,如果你想以随机顺序获取100个文档,就会进行101次查询。尽管Meilisearch查询非常快,但是当你进行这么多次查询时,速度仍然可能会变慢。因此,我建议仅在文档数量较少(少于10个)时使用此方法,或者例如缓存结果。
MeilisearchQuery::index('products') ->where('categories', '=', 'phones') ->inRandomOrder() ->limit(10) ->get();