本文目录导读:

在PHP中连接和操作 Elasticsearch(ES),主要有以下几种方式:
使用官方客户端(推荐)
安装
composer require elasticsearch/elasticsearch
基本连接
<?php
require 'vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;
// 单节点连接
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
// 多节点连接(集群)
$client = ClientBuilder::create()
->setHosts([
'192.168.1.1:9200',
'192.168.1.2:9200',
'192.168.1.3:9200'
])
->build();
// 带认证的连接
$client = ClientBuilder::create()
->setHosts(['https://user:password@localhost:9200'])
->setSSLVerification(false) // 开发环境可以关闭SSL验证
->build();
基本操作示例
创建索引
$params = [
'index' => 'my_index',
'body' => [
'settings' => [
'number_of_shards' => 3,
'number_of_replicas' => 2
],
'mappings' => [
'properties' => [
'title' => [
'type' => 'text'
],
'content' => [
'type' => 'text'
],
'price' => [
'type' => 'float'
],
'created_at' => [
'type' => 'date',
'format' => 'yyyy-MM-dd HH:mm:ss'
]
]
]
]
];
$response = $client->indices()->create($params);
添加文档
// 单条添加
$params = [
'index' => 'my_index',
'id' => '1',
'body' => [
'title' => 'PHP教程',
'content' => '学习PHP编程',
'price' => 29.99,
'created_at' => '2024-01-15 10:30:00'
]
];
$response = $client->index($params);
// 批量添加
$params = ['body' => []];
for ($i = 1; $i <= 100; $i++) {
$params['body'][] = [
'index' => [
'_index' => 'my_index',
'_id' => $i
]
];
$params['body'][] = [
'title' => "文章{$i}",
'content' => "这是第{$i}篇文章的内容",
'price' => rand(10, 100),
'created_at' => date('Y-m-d H:i:s', strtotime("-{$i} days"))
];
}
$responses = $client->bulk($params);
搜索文档
// 基本搜索
$params = [
'index' => 'my_index',
'body' => [
'query' => [
'match' => [
'title' => 'PHP'
]
],
'size' => 10,
'from' => 0,
'sort' => [
'price' => 'desc'
]
]
];
$response = $client->search($params);
// 处理结果
$hits = $response['hits']['hits'];
$total = $response['hits']['total']['value'];
foreach ($hits as $hit) {
echo "ID: " . $hit['_id'] . "\n";
echo "Score: " . $hit['_score'] . "\n";
print_r($hit['_source']);
echo "---\n";
}
// 高级搜索(多条件)
$params = [
'index' => 'my_index',
'body' => [
'query' => [
'bool' => [
'must' => [
['match' => ['title' => 'PHP']],
['range' => ['price' => ['gte' => 20, 'lte' => 50]]]
],
'filter' => [
['term' => ['status' => 'active']]
]
]
],
'aggs' => [
'avg_price' => [
'avg' => ['field' => 'price']
],
'price_ranges' => [
'range' => [
'field' => 'price',
'ranges' => [
['to' => 30],
['from' => 30, 'to' => 60],
['from' => 60]
]
]
]
]
]
];
$response = $client->search($params);
更新文档
// 部分更新
$params = [
'index' => 'my_index',
'id' => '1',
'body' => [
'doc' => [
'price' => 39.99,
'updated_at' => date('Y-m-d H:i:s')
]
]
];
$response = $client->update($params);
// 通过脚本更新
$params = [
'index' => 'my_index',
'id' => '1',
'body' => [
'script' => [
'source' => 'ctx._source.price += params.discount',
'params' => ['discount' => -5]
]
]
];
$response = $client->updateByQuery($params);
删除文档
// 删除单个
$params = [
'index' => 'my_index',
'id' => '1'
];
$response = $client->delete($params);
// 条件删除
$params = [
'index' => 'my_index',
'body' => [
'query' => [
'range' => [
'created_at' => [
'lte' => '2024-01-01'
]
]
]
]
];
$response = $client->deleteByQuery($params);
使用CURL直接操作(无依赖)
function esRequest($method, $uri, $body = null) {
$url = "http://localhost:9200/{$uri}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($body) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// 使用示例
$result = esRequest('GET', 'my_index/_search', [
'query' => [
'match' => ['title' => 'PHP']
]
]);
封装成服务类(推荐用于项目)
<?php
namespace App\Services;
use Elastic\Elasticsearch\ClientBuilder;
class ElasticsearchService
{
private $client;
private $index;
public function __construct($hosts = ['localhost:9200'], $index = 'default_index')
{
$this->client = ClientBuilder::create()
->setHosts($hosts)
->build();
$this->index = $index;
}
public function setIndex($index)
{
$this->index = $index;
return $this;
}
public function search($query, $page = 1, $size = 10, $sort = [])
{
$params = [
'index' => $this->index,
'body' => [
'query' => $query,
'from' => ($page - 1) * $size,
'size' => $size
]
];
if (!empty($sort)) {
$params['body']['sort'] = $sort;
}
try {
$response = $this->client->search($params);
return [
'total' => $response['hits']['total']['value'],
'hits' => $response['hits']['hits'],
'page' => $page,
'size' => $size
];
} catch (\Exception $e) {
return ['error' => $e->getMessage()];
}
}
public function indexDocument($id, $data)
{
$params = [
'index' => $this->index,
'id' => $id,
'body' => $data
];
return $this->client->index($params);
}
public function deleteDocument($id)
{
$params = [
'index' => $this->index,
'id' => $id
];
return $this->client->delete($params);
}
public function bulkIndex($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);
}
}
配置和最佳实践
连接池配置
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->setConnectionPool('\Elasticsearch\ConnectionPool\StaticNoPingConnectionPool')
->setRetries(3)
->setHandler(ClientBuilder::singleHandler())
->build();
错误处理
try {
$response = $client->search($params);
} catch (\Elastic\Elasticsearch\Exception\ElasticsearchException $e) {
echo "ES错误: " . $e->getMessage();
} catch (\Exception $e) {
echo "一般错误: " . $e->getMessage();
}
性能优化
// 设置批量处理大小
$batchSize = 1000;
$documents = [/* ... */];
$params = ['body' => []];
foreach ($documents as $doc) {
$params['body'][] = [
'index' => ['_index' => 'my_index', '_id' => $doc['id']]
];
$params['body'][] = $doc['data'];
if (count($params['body']) >= $batchSize * 2) {
$client->bulk($params);
$params = ['body' => []];
}
}
// 处理剩余文档
if (!empty($params['body'])) {
$client->bulk($params);
}
调试和监控
// 开启调试模式
$logger = ClientBuilder::defaultLogger('path/to/logs/elastic.log');
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->setLogger($logger)
->build();
// 查看请求信息
$response = $client->search($params);
echo "用时: " . $response['took'] . "ms\n";
echo "分片信息: " . print_r($response['_shards'], true);
选择哪种方式取决于你的项目需求:
- 小型项目:使用官方客户端最简单
- 大型项目:封装成服务类更易维护
- 无依赖需求:CURL方式最轻量