php操作elasticsearch,亲测可用

PHP 环境要求:PHP >= 7.2 + 安装 ES 客户端

复制代码
composer require elasticsearch/elasticsearch
php 复制代码
<?php
require 'vendor/autoload.php'; // 加载 composer 依赖

use Elasticsearch\ClientBuilder;
use DateTime;

// ========== 连接 Elasticsearch ==========

// 1. 基本连接(对应 Python 基础连接)
$client = ClientBuilder::create()
    ->setHosts(['http://localhost:9200']) // ES 地址
    ->build();

// 2. 带认证连接(账号密码 + 证书)
$client = ClientBuilder::create()
    ->setHosts(['https://localhost:9200'])
    ->setBasicAuthentication('elastic', 'password') // 认证
    ->setCABundle('/path/to/http_ca.crt') // 证书
    ->build();

// 3. 多节点连接
$client = ClientBuilder::create()
    ->setHosts([
        'http://node1:9200',
        'http://node2:9200',
        'http://node3:9200',
    ])
    ->build();

// 验证连接(输出 ES 信息)
print_r($client->info());

// ========== 索引操作 ==========

// 创建索引(结构与 Python 完全一致)
$indexParams = [
    'index' => 'articles', // 索引名
    'body' => [
        'settings' => [
            'number_of_shards' => 1,   // 主分片
            'number_of_replicas' => 0,  // 副本
        ],
        'mappings' => [
            'properties' => [
                'title' => ['type' => 'text', 'analyzer' => 'standard'],
                'content' => ['type' => 'text'],
                'author' => ['type' => 'keyword'],
                'publish_date' => ['type' => 'date'],
                'views' => ['type' => 'integer'],
                'tags' => ['type' => 'keyword'],
            ]
        ]
    ]
];

// 创建索引,忽略已存在错误(400)
try {
    $client->indices()->create($indexParams);
} catch (Exception $e) {}

// 检查索引是否存在
if ($client->indices()->exists(['index' => 'articles'])) {
    echo "索引存在\n";
}

// 删除索引(忽略 400/404 错误)
try {
    $client->indices()->delete(['index' => 'articles']);
} catch (Exception $e) {}

// ========== 文档操作 ==========

// 插入单个文档(ID=1)
$doc = [
    'title' => 'Elasticsearch入门教程',
    'content' => 'Elasticsearch是一个分布式搜索引擎...',
    'author' => '张三',
    'publish_date' => (new DateTime())->format('Y-m-d H:i:s'),
    'views' => 1000,
    'tags' => ['elasticsearch', '搜索', '教程']
];

$response = $client->index([
    'index' => 'articles',
    'id' => 1,
    'body' => $doc
]);

echo "插入结果:" . $response['result'] . "\n";

// ========== 批量插入文档(2~101) ==========
$params = ['body' => []];
for ($i = 2; $i < 102; $i++) {
    // 批量第一行:指定操作
    $params['body'][] = [
        'index' => [
            '_index' => 'articles',
            '_id' => $i
        ]
    ];
    // 批量第二行:文档内容
    $params['body'][] = [
        'title' => "文章标题$i",
        'content' => "文章内容$i",
        'author' => "作者" . $i % 5,
        'publish_date' => (new DateTime())->format('Y-m-d H:i:s'),
        'views' => $i * 100
    ];
}

// 执行批量
$responses = $client->bulk($params);
$success = $i - 2 - count($responses['items']);
echo "成功:" . ($i - 2) . ",失败:" . count($responses['items']) . "\n";

// ========== 获取单个文档 ==========
$doc = $client->get([
    'index' => 'articles',
    'id' => 1
]);
print_r($doc['_source']);

// ========== 更新文档 ==========
$client->update([
    'index' => 'articles',
    'id' => 1,
    'body' => [
        'doc' => [
            'views' => 1500,
            'updated_at' => (new DateTime())->format('Y-m-d H:i:s')
        ]
    ]
]);

// ========== 删除文档 ==========
$client->delete([
    'index' => 'articles',
    'id' => 1
]);

// ========== 搜索操作 ==========

// match 查询(标题搜索)
$result = $client->search([
    'index' => 'articles',
    'body' => [
        'query' => [
            'match' => [
                'title' => 'Elasticsearch 教程'
            ]
        ]
    ]
]);

// 遍历搜索结果
foreach ($result['hits']['hits'] as $hit) {
    echo "ID: {$hit['_id']}, Score: {$hit['_score']}\n";
    echo "Title: {$hit['_source']['title']}\n";
}

// ========== bool 复合查询 ==========
$result = $client->search([
    'index' => 'articles',
    'body' => [
        'query' => [
            'bool' => [
                'must' => [
                    ['match' => ['content' => '搜索引擎']]
                ],
                'filter' => [
                    ['range' => ['views' => ['gte' => 500]]],
                    ['term' => ['author' => '张三']]
                ]
            ]
        ],
        'sort' => [
            ['views' => 'desc'],
            '_score'
        ],
        'from' => 0,
        'size' => 10,
        '_source' => ['title', 'author', 'views']
    ]
]);

echo "总命中:" . $result['hits']['total']['value'] . "\n";
foreach ($result['hits']['hits'] as $hit) {
    print_r($hit['_source']);
}

// ========== 高亮搜索 ==========
$result = $client->search([
    'index' => 'articles',
    'body' => [
        'query' => [
            'match' => [
                'content' => 'Elasticsearch'
            ]
        ],
        'highlight' => [
            'fields' => [
                'content' => [
                    'pre_tags' => ['<em>'],
                    'post_tags' => ['</em>']
                ]
            ]
        ]
    ]
]);

foreach ($result['hits']['hits'] as $hit) {
    if (isset($hit['highlight'])) {
        echo implode(',', $hit['highlight']['content']) . "\n";
    }
}

// ========== 聚合分析:按作者分组 + 平均浏览量 ==========
$result = $client->search([
    'index' => 'articles',
    'body' => [
        'size' => 0,
        'aggs' => [
            'by_author' => [
                'terms' => [
                    'field' => 'author',
                    'size' => 10
                ],
                'aggs' => [
                    'avg_views' => [
                        'avg' => ['field' => 'views']
                    ]
                ]
            ]
        ]
    ]
]);

// 输出聚合结果
foreach ($result['aggregations']['by_author']['buckets'] as $bucket) {
    $author = $bucket['key'];
    $count = $bucket['doc_count'];
    $avg = round($bucket['avg_views']['value']);
    echo "作者:$author,文章数:$count,平均浏览:$avg\n";
}

// ========== 封装类(与 Python 完全对应) ==========
class ElasticsearchClient
{
    private $es;

    // 构造函数:创建连接
    public function __construct($hosts = ['http://localhost:9200'])
    {
        $this->es = ClientBuilder::create()->setHosts($hosts)->build();
    }

    // 创建索引
    public function createIndex($indexName, $mappings = null, $settings = null)
    {
        $body = [];
        if ($settings) $body['settings'] = $settings;
        if ($mappings) $body['mappings'] = $mappings;

        if (!$this->es->indices()->exists(['index' => $indexName])) {
            $this->es->indices()->create([
                'index' => $indexName,
                'body' => $body
            ]);
            return true;
        }
        return false;
    }

    // 插入文档
    public function indexDocument($indexName, $doc, $docId = null)
    {
        $params = [
            'index' => $indexName,
            'body' => $doc
        ];
        if ($docId) $params['id'] = $docId;
        return $this->es->index($params);
    }

    // 搜索文档
    public function search($indexName, $query, $size = 10, $from = 0, $sort = null)
    {
        $body = [
            'query' => $query,
            'size' => $size,
            'from' => $from
        ];
        if ($sort) $body['sort'] = $sort;

        $result = $this->es->search([
            'index' => $indexName,
            'body' => $body
        ]);

        return [
            'total' => $result['hits']['total']['value'],
            'hits' => array_map(function ($hit) {
                return $hit['_source'];
            }, $result['hits']['hits'])
        ];
    }

    // 按查询条件删除
    public function deleteByQuery($indexName, $query)
    {
        return $this->es->deleteByQuery([
            'index' => $indexName,
            'body' => ['query' => $query]
        ]);
    }

    // 关闭客户端
    public function close()
    {
        $this->es = null;
    }
}

// ========== 封装类使用示例 ==========
$client = new ElasticsearchClient();

// 创建测试索引
$client->createIndex('test_index', [
    'properties' => [
        'title' => ['type' => 'text'],
        'content' => ['type' => 'text']
    ]
]);

// 插入文档
$client->indexDocument('test_index', [
    'title' => '测试文档',
    'content' => '这是一个测试'
]);

// 搜索
$res = $client->search('test_index', [
    'match' => ['title' => '测试']
]);

print_r($res);
相关推荐
武子康21 小时前
调查研究-197 FAISS vs Elasticsearch 全面对比:从向量检索、全文搜索到 RAG 选型指南
人工智能·elasticsearch·agent
Elasticsearch2 天前
Elasticsearch ES|QL:现已支持视图、子查询和读取时模式定义
elasticsearch
Elasticsearch5 天前
Kibana 中的 SNMP 拓扑数据:从采集到 Canvas
elasticsearch
Elasticsearch7 天前
3个信号、2个环境变量、0个采集器:使用 Python 和 Elastic 的托管 OTLP 端点实现 OpenTelemetry
elasticsearch
两个人的幸福7 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
Elasticsearch9 天前
如何通过 Claude Code 来写入 CSV 数据到 Elasticsearch
elasticsearch
BingoGo9 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack9 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820710 天前
PHP 扩展——从入门到理解
php