You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel项目中基于Elasticsearch-PHP构建多语言商品索引咨询

这个场景我之前做过类似的,结合Laravel和elasticsearch-php实现多语言商品检索其实不难,核心是做好ES的索引映射设计和数据同步逻辑,下面一步步来:

一、先搞定Elasticsearch的索引映射设计

因为你的title字段是动态多语言的,语言键不固定,所以用**动态模板(dynamic_templates)**是最合适的——它能自动识别不同语言的字段并应用对应的分词器。

首先要确保你的ES已经安装了对应语言的分词插件:

  • 中文:analysis-ik(用于精准中文分词)
  • 日文:analysis-kuromoji(用于日文分词)
  • 英文用ES自带的english分析器即可

然后用elasticsearch-php创建索引,映射代码示例如下:

use Elastic\Elasticsearch\Client;

// 假设你已经通过Laravel依赖注入拿到了ES客户端实例
$client = app(Client::class);
$indexName = 'products';

// 先判断索引是否存在,不存在则创建
if (!$client->indices()->exists(['index' => $indexName])) {
    $client->indices()->create([
        'index' => $indexName,
        'body' => [
            'settings' => [
                'number_of_shards' => 1,
                'number_of_replicas' => 0,
                // 配置自定义语言分析器
                'analysis' => [
                    'analyzer' => [
                        'zh_cn_analyzer' => [
                            'type' => 'custom',
                            'tokenizer' => 'ik_smart'
                        ],
                        'ja_jp_analyzer' => [
                            'type' => 'custom',
                            'tokenizer' => 'kuromoji_tokenizer'
                        ]
                    ]
                ]
            ],
            'mappings' => [
                'dynamic_templates' => [
                    // 中文标题字段映射
                    [
                        'zh_cn_title' => [
                            'path_match' => 'title.zh-CN',
                            'mapping' => [
                                'type' => 'text',
                                'analyzer' => 'zh_cn_analyzer',
                                'fields' => [
                                    'keyword' => ['type' => 'keyword']
                                ]
                            ]
                        ]
                    ],
                    // 日文标题字段映射
                    [
                        'ja_jp_title' => [
                            'path_match' => 'title.ja-JP',
                            'mapping' => [
                                'type' => 'text',
                                'analyzer' => 'ja_jp_analyzer',
                                'fields' => [
                                    'keyword' => ['type' => 'keyword']
                                ]
                            ]
                        ]
                    ],
                    // 英文标题字段映射
                    [
                        'en_us_title' => [
                            'path_match' => 'title.en-US',
                            'mapping' => [
                                'type' => 'text',
                                'analyzer' => 'english',
                                'fields' => [
                                    'keyword' => ['type' => 'keyword']
                                ]
                            ]
                        ]
                    ],
                    // 其他未定义语言,用默认标准分析器兜底
                    [
                        'other_language_titles' => [
                            'match_mapping_type' => 'string',
                            'path_match' => 'title.*',
                            'mapping' => [
                                'type' => 'text',
                                'analyzer' => 'standard',
                                'fields' => [
                                    'keyword' => ['type' => 'keyword']
                                ]
                            ]
                        ]
                    ]
                ],
                'properties' => [
                    'id' => ['type' => 'keyword'],
                    // title作为object类型,存放多语言键值对
                    'title' => ['type' => 'object']
                ]
            ]
        ]
    ]);
}
二、从Laravel的MySQL同步数据到Elasticsearch

数据同步分两种场景:批量导入现有数据,以及实时同步新增/修改的数据。

1. 批量导入现有数据

写一个Artisan命令一次性同步所有商品:

// 生成命令:php artisan make:command SyncProductsToEs

namespace App\Console\Commands;

use App\Models\Product;
use Elastic\Elasticsearch\Client;
use Illuminate\Console\Command;

class SyncProductsToEs extends Command
{
    protected $signature = 'products:sync-es';
    protected $description = 'Sync all products from MySQL to Elasticsearch';

    public function handle(Client $client)
    {
        $indexName = 'products';
        $bar = $this->output->createProgressBar(Product::count());

        // 分块处理,避免内存溢出
        Product::chunk(100, function ($products) use ($client, $indexName, $bar) {
            $bulkParams = [];
            foreach ($products as $product) {
                $bulkParams[] = [
                    'index' => [
                        '_index' => $indexName,
                        '_id' => $product->id
                    ]
                ];
                $bulkParams[] = [
                    'id' => $product->id,
                    'title' => json_decode($product->title, true)
                ];
                $bar->advance();
            }
            
            if (!empty($bulkParams)) {
                $client->bulk(['body' => $bulkParams]);
            }
        });

        $bar->finish();
        $this->info("\nAll products synced to Elasticsearch successfully!");
    }
}

运行命令:php artisan products:sync-es就能完成批量导入。

2. 实时同步新增/修改的数据

Product模型里添加事件监听,自动同步数据:

namespace App\Models;

use Elastic\Elasticsearch\Client;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    // 自动把title JSON转成数组
    protected $casts = ['title' => 'array'];

    protected static function booted()
    {
        // 保存/更新时同步到ES
        static::saved(function (Product $product) {
            $client = app(Client::class);
            $client->index([
                '_index' => 'products',
                '_id' => $product->id,
                'body' => [
                    'id' => $product->id,
                    'title' => $product->title
                ]
            ]);
        });

        // 删除时从ES移除
        static::deleted(function (Product $product) {
            $client = app(Client::class);
            $client->delete([
                '_index' => 'products',
                '_id' => $product->id
            ]);
        });
    }
}

如果商品数量大,建议把同步逻辑放到队列里,避免阻塞请求。

三、实现多语言检索

现在可以根据不同场景实现检索逻辑:

1. 指定单一语言检索

比如用户当前语言是中文,就精准检索中文标题字段:

$client = app(Client::class);
$indexName = 'products';
$keyword = '丰田';
$lang = 'zh-CN';

$response = $client->search([
    'index' => $indexName,
    'body' => [
        'query' => [
            'match' => [
                "title.$lang" => $keyword
            ]
        ]
    ]
]);

// 处理返回结果
foreach ($response['hits']['hits'] as $hit) {
    echo "商品ID:{$hit['_id']},标题:{$hit['_source']['title'][$lang]}\n";
}

2. 多语言全局检索(无指定语言)

multi_match匹配所有语言的标题字段:

$response = $client->search([
    'index' => $indexName,
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => $keyword,
                'fields' => ['title.*']
            ]
        ]
    ]
]);

3. 按语言优先级加权检索

优先返回用户当前语言的匹配结果,通过boost提升字段权重:

$response = $client->search([
    'index' => $indexName,
    'body' => [
        'query' => [
            'bool' => [
                'should' => [
                    ['match' => ["title.zh-CN" => ['query' => $keyword, 'boost' => 3]]],
                    ['match' => ["title.en-US" => ['query' => $keyword, 'boost' => 2]]],
                    ['match' => ["title.ja-JP" => ['query' => $keyword, 'boost' => 1]]]
                ]
            ]
        ]
    ]
]);
一些注意事项
  • 确保Laravel里的ES客户端配置正确:在config/services.php中添加elasticsearch的连接信息,通过依赖注入获取客户端实例。
  • 分词插件必须提前安装,否则中文、日文的检索效果会大打折扣。
  • 批量同步大数量数据时,调整chunk的大小避免内存溢出。
  • 高并发场景下,用队列处理实时同步逻辑,避免影响主请求响应速度。

内容的提问来源于stack exchange,提问作者DengSihan

火山引擎 最新活动