如何在 Laravel 中用 ZincSearch 实现站内搜索引擎
之前我已经使用Elasticsearch作为自己站内搜索的驱动,但是ES的体量过大对于一些低配置的服务器来说,使用起来比较吃力。有一天看到一个群里的朋友推荐了ZincSearch。ZincSearch作为一款Elasticsearch 的轻量级替代品,我觉得对于许多小站长来说肯定非常实用。经过一段时间的研究,终于将ZincSearch纳入了laravel开发框架里面了。下面我将详细讲解我们的操作。
一、在您的服务器上安装ZincSearch
1、我的服务器是宝塔面板管理的,我需要先安装Docker。点击右侧Docker按钮,然后弹窗根据指引安装即可,过程非常简单,不再赘述。
2、在Docker中安装ZincSearch。
在宝塔面板的终端或 Docker 控制台中执行命令:
docker pull zincsearch/zinc:latest
启动 ZincSearch 容器
docker run -d -p 4080:4080 zincsearch/zinc:latest
按照以上步骤,你就可以在宝塔面板中成功安装并启动 ZincSearch 了。
二、在您的laravel网站上使用ZincSearch
1、laravel框架中需要安装 GuzzleHTTP
composer require guzzlehttp/guzzle
2、Laravel 配置,添加 Zinc 配置到 .env
ZINC_HOST=http://localhost:4080
ZINC_USER=admin
ZINC_PASSWORD=admin
ZINC_INDEX=posts
3、创建 Zinc 服务类
// app/Services/ZincSearchService.php
namespace App\Services;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class ZincSearchService
{
protected Client $client;
public function __construct()
{
$this->client = new Client([
'base_uri' => env('ZINC_HOST'),
'auth' => [env('ZINC_USER'), env('ZINC_PASSWORD')],
'headers' => ['Content-Type' => 'application/json']
]);
}
// 创建索引
public function createIndex(): void
{
try {
$this->client->put("/api/index/{$this->getIndexName()}", [
'json' => [
'name' => $this->getIndexName(),
'storage_type' => 'disk',
'mappings' => [
'properties' => [
'title' => ['type' => 'text'],
'content' => ['type' => 'text']
]
]
]
]);
} catch (GuzzleException $e) {
// 处理异常
}
}
// 索引文档
public function indexDocument(string $id, array $document): void
{
$this->client->put("/api/{$this->getIndexName()}/_doc/{$id}", [
'json' => $document
]);
}
// 搜索文档
public function search(string $query, int $from = 0, int $num =20): array
{
try {
$response = $this->client->post("/api/{$this->getIndexName()}/_search", [
'json' => [
"search_type" => "match",
'query' => [
'term' => $query
],
'from' => $from,
'max_results' => $num,
]
]);
return json_decode($response->getBody(), true);
} catch (GuzzleException $e) {
return [];
}
}
// 更新文档
public function updateDocument(string $id, array $document): void
{
$this->client->put("/api/{$this->getIndexName()}/_doc/{$id}", [
'json' => $document
]);
}
// 删除文档
public function deleteDocument(string $id): void
{
$this->client->delete("/api/{$this->getIndexName()}/_doc/{$id}");
}
private function getIndexName(): string
{
return env('ZINC_INDEX', 'default_index');
}
}
4、模型集成
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'content'];
protected static function booted()
{
static::created(function ($post) {
app(ZincSearchService::class)->indexDocument([
'id' => $post->id,
'title' => $post->title,
'content' => $post->content
]);
});
static::updated(function ($post) {
app(ZincSearchService::class)->updateDocument($post->id, [
'title' => $post->title,
'content' => $post->content
]);
});
static::deleted(function ($post) {
app(ZincSearchService::class)->deleteDocument($post->id);
});
}
}
5、批量导入模型制作
public function createArticleAll(Request $request)
{
$a=Article::chunk(200, function ($posts) {
foreach ($posts as $post) {
app(ZincSearchService::class)->indexDocument($post->id,[
'id' => $post->id,
'time' => $post->updated_at->format('Y-m-d H:i:s'),
'image' => $post->image,
'intro' => $post->intro,
'title' => $post->title,
'details' => $post->details,
]);
}
});
return $a;
}
6、搜索模型
$service = new ZincSearchService();
$data=[
'user'=>1,
'posts'=>$service->search($request->k,0,15),
'keyword'=>$request->k,
];
return view('search',compact('data'));
7、路由配置
// routes/web.php
Route::get('/search', [SearchController::class, 'search']);
Route::get('/createArticleAll', [SearchController::class, 'createArticleAll']);