本文目录导读:

为PHP项目对接Elasticsearch实现全文检索,主要有以下几种方案,按推荐程度排序:
官方客户端 elasticsearch-php
安装
composer require elasticsearch/elasticsearch
基础连接配置
use Elastic\Elasticsearch\ClientBuilder;
// 单节点连接
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
// 多节点集群连接
$client = ClientBuilder::create()
->setHosts([
'http://node1:9200',
'http://node2:9200',
'http://node3:9200'
])
->setRetries(2) // 重试次数
->build();
// 带认证的连接
$client = ClientBuilder::create()
->setHosts(['https://localhost:9200'])
->setBasicAuthentication('username', 'password')
->setSSLVerification(false) // 开发环境可关闭SSL验证
->build();
索引创建与映射
// 创建索引并定义映射
$params = [
'index' => 'articles',
'body' => [
'settings' => [
'number_of_shards' => 3,
'number_of_replicas' => 1,
'analysis' => [
'analyzer' => [
'ik_smart' => [ // 中文分词
'type' => 'custom',
'tokenizer' => 'ik_smart'
]
]
]
],
'mappings' => [
'properties' => [
'title' => [
'type' => 'text',
'analyzer' => 'ik_max_word',
'search_analyzer' => 'ik_smart'
],
'content' => [
'type' => 'text',
'analyzer' => 'ik_max_word'
],
'author' => [
'type' => 'keyword' // 精确匹配
],
'publish_date' => [
'type' => 'date',
'format' => 'yyyy-MM-dd HH:mm:ss'
],
'views' => [
'type' => 'integer'
],
'tags' => [
'type' => 'keyword'
]
]
]
]
];
$response = $client->indices()->create($params);
数据索引(文档操作)
单条索引
// 索引单条数据
$params = [
'index' => 'articles',
'id' => '1001', // 可选,自动生成id
'body' => [
'title' => 'Elasticsearch PHP集成指南',
'content' => '详细介绍了如何在PHP项目中集成Elasticsearch...',
'author' => '张三',
'publish_date' => '2024-01-15 10:30:00',
'views' => 1256,
'tags' => ['PHP', 'Elasticsearch', '全文检索']
]
];
$response = $client->index($params);
// 批量索引(推荐大数据量使用)
$params = ['body' => []];
$articles = [
['id' => 1, 'title' => '文章1', 'content' => '内容1'],
['id' => 2, 'title' => '文章2', 'content' => '内容2'],
// ... 更多数据
];
foreach ($articles as $article) {
$params['body'][] = [
'index' => [
'_index' => 'articles',
'_id' => $article['id']
]
];
$params['body'][] = [
'title' => $article['title'],
'content' => $article['content']
];
}
$responses = $client->bulk($params);
全文搜索实现
基础搜索
// 全文搜索
$params = [
'index' => 'articles',
'body' => [
'query' => [
'match' => [
'content' => 'Elasticsearch PHP'
]
],
'from' => 0, // 分页
'size' => 10,
'sort' => [
'publish_date' => ['order' => 'desc']
]
]
];
$response = $client->search($params);
$hits = $response['hits']['hits'];
$total = $response['hits']['total']['value'];
foreach ($hits as $hit) {
echo $hit['_source']['title'] . "\n";
echo $hit['_score'] . "\n"; // 相关度分数
}
高级搜索组合
// 组合查询
$params = [
'index' => 'articles',
'body' => [
'query' => [
'bool' => [
'must' => [
'match' => ['title' => 'PHP']
],
'should' => [
'match' => ['content' => 'Elasticsearch']
],
'filter' => [
'range' => [
'publish_date' => [
'gte' => '2024-01-01',
'lte' => '2024-12-31'
]
]
],
'must_not' => [
'term' => ['status' => 'draft']
]
]
],
'highlight' => [
'fields' => [
'title' => ['pre_tags' => ['<em>'], 'post_tags' => ['</em>']],
'content' => ['pre_tags' => ['<em>'], 'post_tags' => ['</em>']]
]
]
]
];
$response = $client->search($params);
封装搜索类(推荐)
<?php
namespace App\Services;
use Elastic\Elasticsearch\ClientBuilder;
class ElasticsearchService
{
protected $client;
protected $index;
public function __construct($index = 'articles')
{
$this->client = ClientBuilder::create()
->setHosts(config('elasticsearch.hosts'))
->setBasicAuthentication(
config('elasticsearch.username'),
config('elasticsearch.password')
)
->build();
$this->index = $index;
}
// 索引文档
public function indexDocument(array $document, $id = null)
{
$params = [
'index' => $this->index,
'body' => $document
];
if ($id) {
$params['id'] = $id;
}
return $this->client->index($params);
}
// 搜索
public function search(string $keyword, array $options = [])
{
$params = [
'index' => $this->index,
'body' => [
'query' => [
'multi_match' => [
'query' => $keyword,
'fields' => $options['fields'] ?? ['title^2', 'content'],
'type' => $options['type'] ?? 'best_fields'
]
],
'size' => $options['size'] ?? 10,
'from' => $options['from'] ?? 0,
'highlight' => [
'fields' => [
'title' => new \stdClass(),
'content' => new \stdClass()
]
]
]
];
// 添加排序
if (!empty($options['sort'])) {
$params['body']['sort'] = $options['sort'];
}
// 添加过滤
if (!empty($options['filters'])) {
$params['body']['query']['bool']['filter'] = $options['filters'];
}
return $this->client->search($params);
}
// 删除文档
public function deleteDocument($id)
{
$params = [
'index' => $this->index,
'id' => $id
];
return $this->client->delete($params);
}
// 批量操作
public function bulkIndex(array $documents)
{
$params = ['body' => []];
foreach ($documents as $doc) {
$params['body'][] = [
'index' => [
'_index' => $this->index,
'_id' => $doc['id']
]
];
$params['body'][] = $doc['data'];
}
return $this->client->bulk($params);
}
}
与ORM集成(Laravel例子)
// config/elasticsearch.php
return [
'hosts' => explode(',', env('ELASTICSEARCH_HOSTS', 'localhost:9200')),
'username' => env('ELASTICSEARCH_USERNAME'),
'password' => env('ELASTICSEARCH_PASSWORD'),
];
// 创建Eloquent观察者
namespace App\Observers;
use App\Models\Article;
use App\Services\ElasticsearchService;
class ArticleObserver
{
protected $elasticsearch;
public function __construct(ElasticsearchService $elasticsearch)
{
$this->elasticsearch = $elasticsearch;
}
public function created(Article $article)
{
$this->elasticsearch->indexDocument($article->toArray(), $article->id);
}
public function updated(Article $article)
{
$this->elasticsearch->indexDocument($article->toArray(), $article->id);
}
public function deleted(Article $article)
{
$this->elasticsearch->deleteDocument($article->id);
}
}
数据库同步策略
全量同步
// Artisan命令示例
class SyncArticlesToElasticsearch extends Command
{
public function handle()
{
$elasticsearch = new ElasticsearchService();
Article::chunk(100, function ($articles) use ($elasticsearch) {
$documents = [];
foreach ($articles as $article) {
$documents[] = [
'id' => $article->id,
'data' => $article->toArray()
];
}
$elasticsearch->bulkIndex($documents);
});
$this->info('同步完成');
}
}
增量同步(使用消息队列)
// 队列任务示例
class SyncArticleToES implements ShouldQueue
{
public function handle(ElasticsearchService $elasticsearch)
{
// 处理数据变更
$elasticsearch->indexDocument($this->article->toArray(), $this->article->id);
}
}
性能优化建议
// 1. 连接池配置
$client = ClientBuilder::create()
->setConnectionPool('\Elastic\Elasticsearch\ConnectionPool\StaticConnectionPool')
->setHosts(['localhost:9200'])
->build();
// 2. 批量处理大小
$params['body'] = [];
$batchSize = 500;
foreach ($articles as $index => $article) {
$params['body'][] = ['index' => ['_index' => 'articles', '_id' => $article['id']]];
$params['body'][] = $article;
if (($index + 1) % $batchSize === 0) {
$client->bulk($params);
$params['body'] = [];
}
}
if (!empty($params['body'])) {
$client->bulk($params);
}
// 3. 缓存搜索结果
$cacheKey = 'search_' . md5(json_encode($params));
$results = Cache::remember($cacheKey, 300, function () use ($client, $params) {
return $client->search($params);
});
注意事项
- 中文分词:需要安装IK分词插件
- 索引优化:定期优化索引和清理旧数据
- 错误处理:做好异常捕获和日志记录
- 版本兼容:注意es版本与客户端版本对应
- 安全配置:生产环境开启SSL和认证
通过以上步骤,你可以在PHP项目中完整地对接Elasticsearch实现全文检索功能。