使用 Laravel 和 Typesense Cloud 进行无服务器搜索
2024 年 9 月 22 日
随着用户对更高性能、更低延迟和更多功能的追求,现代 Web 应用程序多年来变得越来越复杂。作为开发人员,选择 Laravel 很容易。但是,当你超越应用程序平台时,你就必须考虑数据了。
该数据库是否符合我的需求?它可以扩展吗?我可以从中生成报告吗?而且大多数系统都需要搜索功能。如果您选择的数据库是 AWS DynamoDB,该怎么办?Laravel 生态系统有使用 Eloquent 将数据存储在 DynamoDB 中的库。但由于它是一个简单的键/值数据存储,因此搜索仅限于索引和昂贵的过滤器。
这正是 Typesense 等专用搜索数据库真正大放异彩的地方。它可以处理那些艰难的搜索、对结果进行排名和评分,并将这些复杂性抽象为 API 供开发人员使用。但是您将如何填充这个辅助存储?传统方法会说使用 Scout。但在本文中,我想通过使用 DynamoDB 的 Stream 功能和 AWS Lambda 来演示一种更适合 AWS 的方式,以使我们的主 DynamoDB 表与 Typesense 集合保持同步。
披露
但在开始之前,为了披露, 类型感应 赞助我试用他们的产品并报告我的发现。他们吸引了我的注意力,但没有吸引我的意见。以下是我作为一名开发人员将搜索集成到包含 AWS DynamoDB 和 Typesense 的 Laravel 应用程序中时所获得的客观经验。
建筑学
在深入研究代码之前,让我们先从宏观角度看一下我们的解决方案。想象一个非常传统的 Laravel 应用程序,它有一些 Blade 视图、控制器和一个简单的 Todo 模型。用户可以列出 Todo、创建新的 Todo,然后在
name
和
description
。对于存储,我们将使用 DynamoDB 和 Typesense 的云选项。
让我们开始吧!
构建
所有型号
让我们开始了解基础级别的 Todo 模型。我发现从下往上进行有助于将各个部分连接在一起,直到我获得完整的图像,如上面的架构图所示。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use BaoPham\DynamoDb\DynamoDbModel;
class Todo extends DynamoDbModel
{
use HasFactory;
protected $primaryKey = 'id'; // required
}
请注意,我正在扩展我的模型
DynamoDbModel
。这来自于这个广受欢迎且维护良好的图书馆
GitHub 存储库。
。
这个库的优点在于我可以使用我习惯的 Laravel Eloquent 方法。在
TodoController
尽管我使用的是 DynamoDB,但保存看起来仍然像正常情况一样。
public function store(request $request): redirectresponse
{
$todo = new todo;
$todo->id = Uuid::uuid4()->toString();
$todo->name = $request->name;
$todo->description = $request->description;
$todo->save();
return redirect('/todos')->with('status', 'todo data has been inserted');
}
Typesense 云和集合
在了解如何将 DynamoDB 连接到 Typesense 之前,我想向您展示使用 Typesense Cloud 产品是多么简单和强大。您可以 100% 自己运行 Typesense,如果您是自托管或 VM 托管您的 Laravel 应用程序,这可能是您想要采用的方法。但是,如果您在 AWS 上部署并使用 DynamoDB,那么云托管的 Typesense 非常有意义。
我真正喜欢云服务的一点是,我可以在抽象级别管理集群。我设置内存、节点、是否需要高可用性和高磁盘性能等内容,Typesense 会处理其余的事情。我需要做的就是生成一些 API 密钥、附加权限,然后我的客户端就可以自由地执行所需的操作了。
集群配置最终看起来如下图所示。
集群就位后,其他 Typesense 操作便可开始。我必须定义一个集合,其中包含文档架构,然后才能开始插入文档。例如,以下是搜索我们的 Todos 集合时显示的内容。
将 DynamoDB 连接到 Typesense
在建立 Typesense 云集群后,我们来谈谈与 DynamoDB 的同步。在使用 DynamoDB 等云原生技术构建系统时,有时留在该生态系统中执行额外处理(例如将数据与 DynamoDB 和 Typesense 同步)更有意义。
如果您不熟悉 AWS,DynamoDB 提供了一组通过 Streams 实时响应更改的功能。可以从 AWS Kinesis 或 Lambda 函数读取 DynamoDB 流。对于此示例,Lambda 函数是我要采用的路线。它很简单、独立,而且我不需要 Kinesis 提供的可扩展性。
DynamoDB 提供了几种读取更改的选项。我可以只读取关键更改、新图像或新旧图像。我选择只读取新图像,这样我就可以得到完整的 DynamoDB 项目,而不必返回并进行双重读取以稍后补充该项目。对于此实现,我使用 Golang 从流中读取并填充 Typesense。我发现在 AWS Lambda 中执行事件驱动工作时,Golang 是一个不错的选择。
无需深入研究其工作原理(末尾有一个存储库链接),以下是 Typesense 的具体部分。Typesense 的另一个优势是,从 SDK 的角度来看,使用其 API 时有很多选择。
// New Client
client = typesense.NewClient(
typesense.WithServer(url),
typesense.WithAPIKey(apiKey))
// Write a Document (Upsert)
result, err := client.Collection("todos").Documents().Upsert(ctx, typesenseTodo)
以上所有操作都必须由用户操作触发。当用户创建新的 Todo(如下面的 UI 图像所示)时,控制器将创建一个新模型并将其保存到数据库。
再次注意,我正在使用 Eloquent 来执行保存。
public function store(request $request): redirectresponse
{
$todo = new todo;
$todo->id = Uuid::uuid4()->toString();
$todo->name = $request->name;
$todo->description = $request->description;
$todo->save();
return redirect('/todos')->with('status', 'todo data has been inserted');
}
搜索收藏
在使用时,有一些很好的资源可以同步 Typesense 侦察 。但是,我喜欢构建事物的原因是,通常有多种方法可以解决问题。我真正喜欢 DynamoDB 模式的原因是,一切都发生在其自己的隔离空间中。对 DynamoDB 的写入操作发生后,用户从 API 调用中返回控制权。同步过程与用户的操作异步发生。通过让 Stream 触发 Lambda 函数,该工作负载也被隔离。
话虽如此,使用 Scout 集成,我的
Todo
模型将公开搜索功能。这不是我下面要展示的内容。我需要使用与 Lambda 函数中相同的 Typesense API,但我将使用好用的 PHP,而不是 Golang。
Laravel 提供程序
为了以一致且可靠的方式使用客户端,让我们创建一个
Provider
这样我就可以将其注入我的
TodoController
。
php artisan make:provider SearchProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\HttpClient\HttplugClient;
use Typesense\Client;
class SearchProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(Client::class, function ($app) {
$client = new Client(
[
'api_key' => '<API Key>',
'nodes' => [
[
'host' => '<Typesense Host>',
'port' => '443',
'protocol' => 'https',
],
],
'client' => new HttplugClient(),
]
);
return $client;
});
}
}
注入和搜索
有
SearchProvider
建立暴露 Typesense 客户端的程序后,我现在可以开始从我的 Web 应用程序处理搜索操作。我首先将客户端注入到我的控制器中。
private Client $client;
public function __construct(Client $client)
{
$this->client = $client;
}
配置完成后,我就可以用控制器方法处理搜索操作。
public function search(request $request): view
{
$search = '';
if ($request->search) {
$search = $request->search;
}
$values = $this->client->collections["todos"]->documents->search(
[
'q' => $search,
'query_by' => 'name,description',
'sort_by' => 'created_at:desc',
]
);
$searched = [];
foreach ($values['hits'] as $todo) {
$item = $todo['document'];
$t = new Todo;
$t->id = $item['id'];
$t->name = $item['name'];
$t->description = $item['description'];
$t->created_at = $item['created_at'];
$t->updated_at = $item['updated_at'];
array_push($searched, $t);
}
log::debug($searched);
return view('todo')->with(['todos' => $searched]);
}
通知中
query_by
属性是搜索条件的一部分,我需要跨多个字段进行搜索。在 DynamoDB 中执行此类操作会很困难,但使用 Typesense 则非常简单。搜索参数允许进一步自定义,例如提供搜索条件和排序顺序。我喜欢使用 Typesense 的原因是,我不必添加或添加
%
来创建某种通配符语法。Typesense 可以帮我处理。它还可以处理诸如排版错误之类的问题,我将在下面向您展示。
此外,您还会在结果中看到命中情况、得分和其他一些有用信息。这些额外数据让您能够制作更丰富的 UI 和用户体验。
搜索单词时
Demonstration
我正确地看到了我在前几段中创建的一条记录。这一切都说得通。我也可以使用
Demo
或者
stration
或任何其他
contains
類型子句。
但是,如果用户犯了错误怎么办?他们本来想搜索
Demo
,但他们输入的是
Deom
。如果您的搜索提供商可以进行这种更正,那不是很好吗?Typesense 就可以!
这些类型的功能就是您在存储数据时选择专用提供商的原因。
解决方案回顾
这就是代码和用户体验的总结。快速回顾一下我们构建的内容:
- PHP Laravel 应用程序
- 创建全部
- 存储在 DynamoDB 中
- 通过 AWS Lambda 触发与 Typesense 的同步
- 搜索全部
- 使用 PHP Typesense 客户端作为服务提供商
- 注入到控制器中以实现共享重用
想法和印象
首先,我在 90 年代末开始为 Web 构建,当时 PHP 正在与 Perl 的 CGI 竞争。我非常喜欢这个社区和生态系统,而且它每年都在不断进步。Laravel 为整个应用程序体验增添了一些神奇的东西。我喜欢可以混合搭配组件并根据用户需求添加我想要的东西。
这就是为什么将 Typesense 纳入项目既简单又强大的原因。Laravel 通过 Eloquent 让我能够将数据保存在我想要的位置,然后我可以决定从那里做什么。
我对将 Laravel 与 DynamoDB 和 Typesense 结合使用的想法可以总结如下:
- 使用 Eloquent 保存模型非常无缝
- 使用 AWS DynamoDB Streams 符合我构建可扩展且满足最高级别服务需求的云原生解决方案的愿望
- 使用 PHP Typesense 客户端作为提供程序非常容易,并且通过依赖注入将其注入控制器正是我期望它工作的方式。
- Typesense API 简洁、易用且功能强大。我能够进行多字段搜索、命中、排名和排序,这真是太棒了。此外,我可以将服务器作为托管组件运行在他们的云中,这真是太棒了。
我希望 Typesense 云提供的是纯粹的无服务器和基于消费的模型。使用 DynamoDB 和 Lambda,我只需为我使用的资源付费,即按写入/读取的数据或计算所花费的时间付费。与 Typesense 云不同,我需要预先支付容量费用,这可能会留下未使用的容量,而这些容量会在计费期间被浪费。作为构建者,我有责任管理这一点并有效正确使用资源。
尽管如此,这并不是我不使用 Typesense 云的原因。它工作得非常好,而且比自己运行服务容易得多。
包起来
非常感谢您阅读有关使用 Typesense 搜索数据存储保存和同步基于 Laravel 的 Web 应用程序的替代方法。我已经运行了大量代码和配置,我无法在一篇文章中演示它们。
以下是我使用的存储库。请随意克隆并自行运行。如果您有任何反馈,请将其留在存储库中。
为客户开发软件是我一生的工作。我很高兴看到用户感到高兴,他们不必担心或考虑隐藏在表面之下的复杂性。选择 Laravel、AWS 和 Typesense 是开始构建和实现这些抽象的良好基础决策。
它们拥有坚如磐石的平台、库和出色的社区。现在是加入 PHP 和 Laravel 社区的最佳时机!
再次感谢您的阅读并祝您搭建愉快!
帖子 使用 Laravel 和 Typesense Cloud 进行无服务器搜索 首先出现在 Laravel 新闻 。
加入 Laravel 时事通讯 获取最新信息 类似这样的 Laravel 文章将直接发送到您的收件箱。