【Elasticsearch】 PHP 使用ES示例demo

小破孩
2025-11-13 / 0 评论 / 11 阅读 / 正在检测是否收录...

1. 使用 Composer 安装

# 安装 Composer(如果未安装)
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

# 创建项目目录
mkdir elasticsearch-php-demo
cd elasticsearch-php-demo

# 初始化 Composer
composer init

# 安装 Elasticsearch PHP 客户端
composer require elasticsearch/elasticsearch

2. 手动安装(不使用 Composer)

<?php
// 手动引入 Elasticsearch 客户端
require_once 'path/to/elasticsearch-php/autoload.php';

示例 1:基础连接和索引操作

<?php
require 'vendor/autoload.php';

use Elasticsearch\ClientBuilder;

class ElasticsearchDemo {
    private $client;
    
    public function __construct() {
        // 创建 Elasticsearch 客户端
        $this->client = ClientBuilder::create()
            ->setHosts(['localhost:9200']) // ES 服务器地址
            ->setRetries(2)                // 重试次数
            ->build();
            
        // 检查连接
        if (!$this->checkConnection()) {
            throw new Exception("无法连接到 Elasticsearch");
        }
    }
    
    /**
     * 检查 Elasticsearch 连接
     */
    private function checkConnection() {
        try {
            return $this->client->ping();
        } catch (Exception $e) {
            echo "连接失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 创建索引
     */
    public function createIndex($indexName) {
        $params = [
            'index' => $indexName,
            'body' => [
                'settings' => [
                    'number_of_shards' => 2,
                    'number_of_replicas' => 1
                ],
                'mappings' => [
                    'properties' => [
                        'title' => [
                            'type' => 'text',
                            'analyzer' => 'standard'
                        ],
                        'content' => [
                            'type' => 'text',
                            'analyzer' => 'standard'
                        ],
                        'author' => [
                            'type' => 'keyword'
                        ],
                        'publish_date' => [
                            'type' => 'date'
                        ],
                        'views' => [
                            'type' => 'integer'
                        ]
                    ]
                ]
            ]
        ];
        
        try {
            $response = $this->client->indices()->create($params);
            echo "索引 {$indexName} 创建成功\n";
            return $response;
        } catch (Exception $e) {
            echo "创建索引失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 删除索引
     */
    public function deleteIndex($indexName) {
        $params = ['index' => $indexName];
        
        try {
            // 检查索引是否存在
            if ($this->client->indices()->exists($params)) {
                $response = $this->client->indices()->delete($params);
                echo "索引 {$indexName} 删除成功\n";
                return $response;
            } else {
                echo "索引 {$indexName} 不存在\n";
                return false;
            }
        } catch (Exception $e) {
            echo "删除索引失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 添加文档
     */
    public function addDocument($indexName, $document, $id = null) {
        $params = [
            'index' => $indexName,
            'body'  => $document
        ];
        
        // 如果指定了 ID
        if ($id !== null) {
            $params['id'] = $id;
        }
        
        try {
            $response = $this->client->index($params);
            echo "文档添加成功,ID: " . $response['_id'] . "\n";
            return $response;
        } catch (Exception $e) {
            echo "添加文档失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 获取文档
     */
    public function getDocument($indexName, $id) {
        $params = [
            'index' => $indexName,
            'id'    => $id
        ];
        
        try {
            $response = $this->client->get($params);
            return $response;
        } catch (Exception $e) {
            echo "获取文档失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 更新文档
     */
    public function updateDocument($indexName, $id, $updateData) {
        $params = [
            'index' => $indexName,
            'id'    => $id,
            'body'  => [
                'doc' => $updateData
            ]
        ];
        
        try {
            $response = $this->client->update($params);
            echo "文档更新成功\n";
            return $response;
        } catch (Exception $e) {
            echo "更新文档失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 删除文档
     */
    public function deleteDocument($indexName, $id) {
        $params = [
            'index' => $indexName,
            'id'    => $id
        ];
        
        try {
            $response = $this->client->delete($params);
            echo "文档删除成功\n";
            return $response;
        } catch (Exception $e) {
            echo "删除文档失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 搜索文档
     */
    public function search($indexName, $query) {
        $params = [
            'index' => $indexName,
            'body'  => [
                'query' => $query
            ]
        ];
        
        try {
            $response = $this->client->search($params);
            return $response;
        } catch (Exception $e) {
            echo "搜索失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * 批量插入文档
     */
    public function bulkInsert($indexName, $documents) {
        $params = ['body' => []];
        
        foreach ($documents as $document) {
            $params['body'][] = [
                'index' => [
                    '_index' => $indexName
                ]
            ];
            $params['body'][] = $document;
        }
        
        try {
            $response = $this->client->bulk($params);
            echo "批量插入完成,处理了 " . count($documents) . " 个文档\n";
            return $response;
        } catch (Exception $e) {
            echo "批量插入失败: " . $e->getMessage() . "\n";
            return false;
        }
    }
}

// 使用示例
try {
    $es = new ElasticsearchDemo();
    
    // 创建索引
    $es->createIndex('blog');
    
    // 添加单个文档
    $document = [
        'title' => 'Elasticsearch PHP 客户端使用指南',
        'content' => '这是一篇关于如何使用 PHP 操作 Elasticsearch 的详细教程。',
        'author' => '张三',
        'publish_date' => '2023-10-01',
        'views' => 100
    ];
    $es->addDocument('blog', $document, '1');
    
    // 批量插入文档
    $documents = [
        [
            'title' => 'PHP 开发技巧',
            'content' => '分享一些 PHP 开发中的实用技巧和最佳实践。',
            'author' => '李四',
            'publish_date' => '2023-10-02',
            'views' => 150
        ],
        [
            'title' => 'Linux 系统管理',
            'content' => 'Linux 系统管理的基本命令和操作指南。',
            'author' => '王五',
            'publish_date' => '2023-10-03',
            'views' => 200
        ]
    ];
    $es->bulkInsert('blog', $documents);
    
    // 搜索文档
    $query = [
        'match' => [
            'title' => 'PHP'
        ]
    ];
    $searchResult = $es->search('blog', $query);
    
    echo "搜索到 " . $searchResult['hits']['total']['value'] . " 个结果:\n";
    foreach ($searchResult['hits']['hits'] as $hit) {
        echo "- " . $hit['_source']['title'] . " (作者: " . $hit['_source']['author'] . ")\n";
    }
    
} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

示例 2:高级搜索功能

<?php
require 'vendor/autoload.php';

use Elasticsearch\ClientBuilder;

class AdvancedSearchDemo {
    private $client;
    
    public function __construct() {
        $this->client = ClientBuilder::create()
            ->setHosts(['localhost:9200'])
            ->build();
    }
    
    /**
     * 多字段搜索
     */
    public function multiFieldSearch($indexName, $keyword) {
        $params = [
            'index' => $indexName,
            'body' => [
                'query' => [
                    'multi_match' => [
                        'query' => $keyword,
                        'fields' => ['title^2', 'content', 'author'], // title 字段权重为 2
                        'type' => 'best_fields'
                    ]
                ],
                'highlight' => [
                    'fields' => [
                        'title' => new \stdClass(),
                        'content' => new \stdClass()
                    ]
                ]
            ]
        ];
        
        return $this->client->search($params);
    }
    
    /**
     * 布尔搜索
     */
    public function boolSearch($indexName, $must = [], $should = [], $must_not = []) {
        $boolQuery = [];
        
        if (!empty($must)) {
            $boolQuery['must'] = $must;
        }
        if (!empty($should)) {
            $boolQuery['should'] = $should;
        }
        if (!empty($must_not)) {
            $boolQuery['must_not'] = $must_not;
        }
        
        $params = [
            'index' => $indexName,
            'body' => [
                'query' => [
                    'bool' => $boolQuery
                ]
            ]
        ];
        
        return $this->client->search($params);
    }
    
    /**
     * 范围查询
     */
    public function rangeSearch($indexName, $field, $gte = null, $lte = null) {
        $range = [];
        if ($gte !== null) $range['gte'] = $gte;
        if ($lte !== null) $range['lte'] = $lte;
        
        $params = [
            'index' => $indexName,
            'body' => [
                'query' => [
                    'range' => [
                        $field => $range
                    ]
                ]
            ]
        ];
        
        return $this->client->search($params);
    }
    
    /**
     * 聚合查询
     */
    public function aggregationSearch($indexName) {
        $params = [
            'index' => $indexName,
            'body' => [
                'size' => 0, // 不需要返回具体文档
                'aggs' => [
                    'authors' => [
                        'terms' => [
                            'field' => 'author.keyword',
                            'size' => 10
                        ]
                    ],
                    'total_views' => [
                        'sum' => [
                            'field' => 'views'
                        ]
                    ],
                    'avg_views' => [
                        'avg' => [
                            'field' => 'views'
                        ]
                    ]
                ]
            ]
        ];
        
        return $this->client->search($params);
    }
}

// 使用示例
$searchDemo = new AdvancedSearchDemo();

// 多字段搜索
$result = $searchDemo->multiFieldSearch('blog', 'PHP');
print_r($result['hits']);

// 布尔搜索
$boolResult = $searchDemo->boolSearch('blog', 
    [ // must 条件
        ['match' => ['author' => '张三']],
        ['range' => ['views' => ['gte' => 50]]]
    ],
    [ // should 条件
        ['match' => ['title' => '教程']],
        ['match' => ['content' => '指南']]
    ]
);

// 聚合查询
$aggResult = $searchDemo->aggregationSearch('blog');
echo "总浏览量: " . $aggResult['aggregations']['total_views']['value'] . "\n";
echo "平均浏览量: " . $aggResult['aggregations']['avg_views']['value'] . "\n";
?>

示例 3:完整的博客搜索应用

<?php
require 'vendor/autoload.php';

use Elasticsearch\ClientBuilder;

class BlogSearch {
    private $client;
    private $indexName = 'blog_posts';
    
    public function __construct() {
        $this->client = ClientBuilder::create()
            ->setHosts(['localhost:9200'])
            ->build();
            
        $this->createBlogIndex();
    }
    
    private function createBlogIndex() {
        // 检查索引是否存在,不存在则创建
        if (!$this->client->indices()->exists(['index' => $this->indexName])) {
            $params = [
                'index' => $this->indexName,
                'body' => [
                    'settings' => [
                        'number_of_shards' => 1,
                        'number_of_replicas' => 1,
                        'analysis' => [
                            'analyzer' => [
                                'ik_smart_analyzer' => [
                                    'type' => 'custom',
                                    'tokenizer' => 'ik_smart'
                                ]
                            ]
                        ]
                    ],
                    'mappings' => [
                        'properties' => [
                            'title' => [
                                'type' => 'text',
                                'analyzer' => 'ik_smart_analyzer'
                            ],
                            'content' => [
                                'type' => 'text',
                                'analyzer' => 'ik_smart_analyzer'
                            ],
                            'author' => [
                                'type' => 'keyword'
                            ],
                            'tags' => [
                                'type' => 'keyword'
                            ],
                            'category' => [
                                'type' => 'keyword'
                            ],
                            'publish_date' => [
                                'type' => 'date'
                            ],
                            'views' => [
                                'type' => 'integer'
                            ],
                            'status' => [
                                'type' => 'keyword'
                            ]
                        ]
                    ]
                ]
            ];
            
            $this->client->indices()->create($params);
        }
    }
    
    public function addBlogPost($post) {
        $params = [
            'index' => $this->indexName,
            'body' => $post
        ];
        
        return $this->client->index($params);
    }
    
    public function searchPosts($keyword, $filters = [], $page = 1, $pageSize = 10) {
        $from = ($page - 1) * $pageSize;
        
        $query = [
            'bool' => [
                'must' => [],
                'filter' => []
            ]
        ];
        
        // 关键词搜索
        if (!empty($keyword)) {
            $query['bool']['must'][] = [
                'multi_match' => [
                    'query' => $keyword,
                    'fields' => ['title^3', 'content^2', 'tags^2'],
                    'type' => 'best_fields'
                ]
            ];
        }
        
        // 过滤器
        foreach ($filters as $field => $value) {
            if (!empty($value)) {
                $query['bool']['filter'][] = [
                    'term' => [$field => $value]
                ];
            }
        }
        
        $params = [
            'index' => $this->indexName,
            'body' => [
                'from' => $from,
                'size' => $pageSize,
                'query' => $query,
                'sort' => [
                    ['publish_date' => ['order' => 'desc']],
                    ['views' => ['order' => 'desc']]
                ],
                'highlight' => [
                    'fields' => [
                        'title' => [
                            'number_of_fragments' => 0
                        ],
                        'content' => [
                            'fragment_size' => 150,
                            'number_of_fragments' => 3
                        ]
                    ]
                ]
            ]
        ];
        
        $response = $this->client->search($params);
        
        return [
            'total' => $response['hits']['total']['value'],
            'posts' => $response['hits']['hits'],
            'took' => $response['took']
        ];
    }
    
    public function getRelatedPosts($postId, $limit = 5) {
        // 先获取当前文章
        $currentPost = $this->client->get([
            'index' => $this->indexName,
            'id' => $postId
        ]);
        
        $tags = $currentPost['_source']['tags'] ?? [];
        
        if (empty($tags)) {
            return [];
        }
        
        $params = [
            'index' => $this->indexName,
            'body' => [
                'size' => $limit,
                'query' => [
                    'bool' => [
                        'must' => [
                            'terms' => [
                                'tags' => $tags
                            ]
                        ],
                        'must_not' => [
                            'term' => [
                                '_id' => $postId
                            ]
                        ]
                    ]
                ],
                'sort' => [
                    ['views' => ['order' => 'desc']],
                    ['publish_date' => ['order' => 'desc']]
                ]
            ]
        ];
        
        $response = $this->client->search($params);
        
        return $response['hits']['hits'];
    }
}

// 使用示例
$blogSearch = new BlogSearch();

// 添加示例文章
$samplePosts = [
    [
        'title' => 'PHP 8 新特性详解',
        'content' => 'PHP 8 引入了许多令人兴奋的新特性,如命名参数、联合类型、属性等...',
        'author' => '技术达人',
        'tags' => ['PHP', '编程', '后端'],
        'category' => '编程语言',
        'publish_date' => '2023-09-15',
        'views' => 245,
        'status' => 'published'
    ],
    [
        'title' => 'Elasticsearch 入门教程',
        'content' => '学习如何使用 Elasticsearch 进行全文搜索和数据聚合...',
        'author' => '搜索专家',
        'tags' => ['Elasticsearch', '搜索', '数据库'],
        'category' => '数据库',
        'publish_date' => '2023-09-20',
        'views' => 189,
        'status' => 'published'
    ]
];

foreach ($samplePosts as $post) {
    $blogSearch->addBlogPost($post);
}

// 搜索文章
$result = $blogSearch->searchPosts('PHP 教程', ['category' => '编程语言'], 1, 10);

echo "找到 {$result['total']} 篇文章,耗时 {$result['took']} 毫秒\n";
foreach ($result['posts'] as $post) {
    echo "标题: " . $post['_source']['title'] . "\n";
    if (isset($post['highlight'])) {
        echo "高亮: " . implode('...', $post['highlight']['content'] ?? []) . "\n";
    }
    echo "---\n";
}
?>
0

评论 (0)

取消