如何提升Laravel Scout+Meilisearch+PowerGrid的搜索速度?
Meilisearch + Laravel Scout + PowerGrid 搜索性能优化方案
问题背景
在PowerGrid Livewire组件中,通过Laravel Scout结合Meilisearch实现Product模型全文搜索,功能正常但单次搜索耗时约5秒。当前流程为:先从Meilisearch获取匹配的产品ID,再通过whereIn('id', $ids)查询数据库获取完整记录用于PowerGrid展示。单独测试Meilisearch响应仅10-20ms,但二次数据库查询导致显著延迟。Meilisearch索引已包含10个可搜索属性,scout.php配置正确,希望优化流程甚至跳过二次数据库查询。
优化方案
1. 直接从Meilisearch返回完整数据(跳过数据库查询)
由于你的Meilisearch索引已经包含PowerGrid展示所需的所有字段,完全可以直接用Meilisearch的结果渲染,无需再查询数据库。
修改datasource()方法:
public function datasource() { $search = $this->search ?? null; // 无搜索关键词时,保留原有数据库查询逻辑(支持分页、排序) if (!$search) { return Product::query(); } try { // 通过raw()获取Meilisearch原始响应,避免Laravel Scout自动触发数据库查询 $searchResults = Product::search($search) ->take(50) ->raw(); $hits = $searchResults['hits']; if (empty($hits)) { return Product::whereRaw('0=1'); } // 将Meilisearch返回的文档转为PowerGrid可识别的模型集合 return collect($hits)->map(function ($hit) { return new Product($hit); }); } catch (\Throwable $e) { Log::info('Meilisearch error: '.$e->getMessage()); return Product::whereRaw('0=1'); } }
注意:如果PowerGrid需要展示模型关联数据或实时更新的数据库字段,此方案不适用;仅展示索引内字段时,这是最优提速方案。
2. 优化数据库whereIn查询性能
若必须查询数据库(如需要关联数据),可从以下几点优化:
- 确保
id字段为主键(默认带索引,whereIn查询效率更高) - 禁用模型事件,减少额外开销
- 只查询PowerGrid需要的字段,避免
select *
修改后的datasource():
public function datasource(): Builder { $query = Product::query(); $search = $this->search ?? null; if ($search) { try { // 直接从Meilisearch原始响应提取ID,避免拉取完整文档 $ids = collect(Product::search($search)->take(50)->raw()['hits']) ->pluck('id') ->toArray(); if (!empty($ids)) { $query->whereIn('id', $ids) ->withoutEvents() // 禁用模型事件,减少额外操作 ->select(['id', 'name', 'sku', 'supplier_name', 'brand_name']); // 仅查询展示所需字段 } else { $query->whereRaw('0=1'); } } catch (\Throwable $e) { Log::info('Meilisearch error: '.$e->getMessage()); $query->whereRaw('0=1'); } } return $query; }
3. 利用Meilisearch原生查询增强灵活性
通过Scout的query()方法直接构建Meilisearch原生查询,更灵活控制排序、过滤逻辑,同时避免不必要的数据库交互:
public function datasource() { $search = $this->search ?? null; if (!$search) { return Product::query(); } try { $searchResults = Product::search($search) ->take(50) ->query(function ($meilisearchQuery) { // 添加Meilisearch原生排序、过滤规则 $meilisearchQuery->sort('name', 'asc'); }) ->raw(); $hits = $searchResults['hits']; if (empty($hits)) { return Product::whereRaw('0=1'); } return collect($hits)->map(fn($hit) => new Product($hit)); } catch (\Throwable $e) { Log::info('Meilisearch error: '.$e->getMessage()); return Product::whereRaw('0=1'); } }
4. 缓存搜索结果(非实时场景)
如果产品数据不是实时更新的,可缓存Meilisearch搜索结果,减少重复查询:
public function datasource() { $search = $this->search ?? null; if (!$search) { return Product::query(); } try { // 缓存搜索结果5分钟,可根据业务调整缓存时长 $hits = Cache::remember('product_search_'.$search, 300, function () use ($search) { return Product::search($search)->take(50)->raw()['hits']; }); if (empty($hits)) { return Product::whereRaw('0=1'); } return collect($hits)->map(fn($hit) => new Product($hit)); } catch (\Throwable $e) { Log::info('Meilisearch error: '.$e->getMessage()); return Product::whereRaw('0=1'); } }
5. Meilisearch索引层面优化
- 精简索引字段:移除不需要搜索或展示的字段,减小索引体积,提升查询速度
- 调整字段权重:给
name、sku等核心搜索字段设置更高权重,减少无效结果返回 - 开启Meilisearch查询缓存:在Meilisearch配置中启用查询缓存,降低重复计算开销
内容的提问来源于stack exchange,提问作者Shehara Nayanananda
相关产品推荐
相关产品推荐

