Symfony5 使用 FOSElasticaBundle 集成 Elasticsearch CJayhe 技术分享 350 views ## 前言 Symfony 5.4 作为一个PHP Web应用程序框架,提供了丰富的工具和库,简化了Web应用程序的开发过程。FOSElasticaBundle是Symfony框架的一个集成包,专门用于与Elasticsearch的集成。而Elasticsearch则是一款强大的搜索和分析引擎,能够提供快速的全文搜索和实时数据分析。 FOSElasticaBundle 的强大优势在于它为开发者提供了一种简单而强大的方式来集成Elasticsearch。它允许开发者定义Elasticsearch索引、映射和文档类型,执行高级搜索操作以及与Elasticsearch进行交互,无需深入研究Elasticsearch的复杂性。同时,FOSElasticaBundle还提供了数据同步功能,可以与Symfony应用程序的数据库进行集成,并实现与Elasticsearch索引的同步更新。 这样,当应用程序中的数据发生变化时,Elasticsearch索引会自动更新以反映最新的数据状态,从而确保搜索结果的准确性和实时性。通过将Symfony 5.4与FOSElasticaBundle与Elasticsearch结合使用,开发者能够轻松构建具有强大搜索功能和实时数据分析能力的Web应用程序,提升应用程序的性能、可扩展性和用户体验。 ## 版本选择与安装 ### PHP版本和 Elasticsearch 版本的关系 FOSElasticaBundle 版本的选择 需要参照你所使用的php版本,Symfony版本,以及 Elasticsearch版本。详情阅读 FOSElasticaBundle 的依赖声明。 我们大概可以了解其对应关系如下 ![](https://phpzlc.com//upload/6553011c22902.png) 这里的关系图,对于php的版本是卡死的,原因有以下两点 1. 版本之间语法存在差异,高版本无法适配低版本,对应包内的代码也基本上使用标注的最新特性,故不要通过忽略php版本的方式强行安装。 2. 由于 [elasticsearch/elasticsearch](https://packagist.org/packages/elasticsearch/elasticsearch) 组件php版本和 Elasticsearch 版本的关联是强关系,且不向下兼容。 以上可知,php的版本决定了你可以使用的 Elasticsearch软件的版本, 而好消息的 Elasticsearch 5 也支持关联度的排序,所以基本功能可以满足。 ### Symfony版本的支持 按照版本的关系图,我们发现 Symfony 5.4 是可以支持到 elasticsearch 7 的, 但这里有个情况 Symfony 5.3 的php版本要求是 `"php": ">=7.2.5"`, 那么就会出现一个问题,因为一般是项目开发到一定阶段才会使用 elasticsearch, 那么如果你和我一样,发现当前 php 版本是 7.3 你该如何抉择了? 是不是就用不了? 这里告诉大家可以使用,我们需要前往各个具体的 FOSElasticaBundle 版本中寻找希望。 经过寻找我们发现 ![](https://phpzlc.com//upload/6553063c6cded.png) 5.2.1 是支持 5 的, 这就让一切成为了可能。 **注意** 通过以上篇幅,我们知道了这个组件的设计对版本的约束十分严格。且一一对应。 即使我们突破了版本的问题,但是后续的开发中会发现即使标注了可以使用5,但支持的覆盖还是不够,所以会出现坑。故而在选择版本的时候,要首先做好打算。 ### 安装 ```shell composer require friendsofsymfony/elastica-bundle v5.2.1 ``` ## 配置与基本使用 安装版本之后,我们打开 `config/packages/fos_elastica.yaml` 配置文件,按照如下格式进行配置即可 ```yaml # Read the documentation: https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/doc/setup.md fos_elastica: clients: default: { url: '%env(ELASTICSEARCH_URL)%' } indexes: app_book: settings: analysis: analyzer: ik_analyzer: ##定义分词器 type: custom tokenizer: ik_max_word types: ## 一个types 里面只要定义一个type 不要定义多个 以后的es 不支持type的概念 book: properties: searchKeyword: type: text analyzer: ik_analyzer ## 使用分词器 isDel: ~ ## 定义字段,全匹配 releaseStatus: ~ ## 定义字段,全匹配 sortValue: ##排序字段,全匹配, 必须定义为 integer 不然会报错 type: integer readNum: type: integer persistence: driver: orm model: App\Entity\Book listener: ~ ## 表示监听orm操作 repository: App\ElasticRepository\Book ## 绑定到自定义的Repository类上 ``` ### 配置文件的说明 1. `ELASTICSEARCH_URL` 的格式 * 不带密码 ``` ELASTICSEARCH_URL=http://ip:9200/ ``` * 带密码 ``` #ELASTICSEARCH_URL=http://username:password@ip:9200/ ``` ### Repository类的定义 在项目src中创建一个目录 `ElasticRepository` 然后创建类 内容示例如下 ```php <?php namespace App\ElasticRepository; use FOS\ElasticaBundle\Repository; use Elastica\Query; use Elastica\Query\BoolQuery; use Elastica\Query\Match; use Elastica\Query\Term; class BookEsRepository extends Repository { public function search($searchKeyword, $page = 1, $limit = 12) { // 创建 BoolQuery 对象 $boolQuery = new BoolQuery(); // 根据标题添加查询条件 if (!empty($searchKeyword)) { $matchQuery = new Match(); $matchQuery->setFieldQuery('searchKeyword', $searchKeyword); $boolQuery->addMust($matchQuery); } $termQuery = new Term(); $termQuery->setTerm('releaseStatus', 2); $boolQuery->addMust($termQuery); $termQuery = new Term(); $termQuery->setTerm('isDel', false); $boolQuery->addMust($termQuery); // 创建查询对象并设置 BoolQuery $query = new Query(); $query->setQuery($boolQuery); $query->addSort([ '_score' => [ 'order' => 'desc' ], 'sortValue' => [ 'order' => 'asc' ], 'readNum' => [ 'order' => 'asc' ], ]); // 执行查询并返回 Pagerfanta 对象 $pagerfanta = $this->findPaginated($query); // 设置分页参数 $pagerfanta->setCurrentPage($page); $pagerfanta->setMaxPerPage($limit); // 获取查询结果的总数 $totalResults = $pagerfanta->getNbResults(); // 获取当前页面的结果 $currentPageResults = $pagerfanta->getCurrentPageResults(); // 返回包含总数和当前页结果的数组 return [ 'total' => $totalResults, 'results' => $currentPageResults ]; } } ``` ### 数据初始化与重置 配置完毕使用该命令,会将mysql的数据库里面的数据同步的es里面。如果es里面已经有了则覆盖。 ``` php bin/console fos:elastica:populate ``` ## 检索代码示例 ```php <?php use FOS\ElasticaBundle\Manager\RepositoryManagerInterface; class BetaController extends AbstractController { public function beta(RepositoryManagerInterface $repositoryManager) { dump($repositoryManager->getRepository(Book::class)->search('不是第一名'));exit; } } ``` ## 监听ORM操作同步 当你将 ``` listener: ~ ## 表示监听orm操作 ``` 这样设置的时候,则数据发生变更的时候,会自动向es服务器发送同步的数据。 这里有两个问题 1. 必须是orm操作才会触发。 2. 当es崩溃的时候,同步的时候会报错,导致程序奔溃,并且同步操作会造成接口的执行时间变长。(所以只适合简单应用) ## 监听ORM异步队列同步 同步操作稳定性和效率都有着致命的缺陷,我们可以采取异步消息队列的方式来同步 具体使用方案参照文档 [Doctrine queue listener](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/v5.2.1/doc/cookbook/doctrine-queue-listener.md) ### 安装 ``` composer require enqueue/elastica-bundle:^0.10.0 ``` 这里具体的版本按照 FOSElasticaBundle 的版本来确定。具体看依赖说明。 **注意** 上文中提到版本的坑在这里就出现了。由于我是用的php7.3 所以只能使用 FOSElasticaBundle 5, FOSElasticaBundle 5 支持的orm版本只支持到了 Symfony 4 的程度, 这就导致这个组件无法正常使用,但是由于版本的限制,我们只能安装这个版本。 所以我安装完10版本之后,将11版本的源码包下载替换进去项目,并且由于11版本支持的 FOSElasticaBundle 6, 我们需要修改源码使其 适配 FOSElasticaBundle 5。修改之后即可正常使用了。 **如果需要这部分代码,可以加入技术群。** **这只是因为版本带来的坑,FOSElasticaBundle本身还是很可以的** ### 配置 配置文件 config/packages/enqueue.yaml 参考如下 ```yaml enqueue: default: transport: '%env(resolve:ENQUEUE_DSN)%' client: null extensions: doctrine_clear_identity_map_extension: true ## 这里为了更新的时候清理 doctrine 缓存, 不设置的话部分同步无法成功。 enqueue_elastica: transport: '%enqueue.default_transport%' doctrine: queue_listeners: - index_name: app_book model_class: App\Entity\Book ``` ### 说明 1 . ENQUEUE_DSN 说明 配置消息队列服务,我这里使用的rides作为消息队列中间件。 格式为: ``` ENQUEUE_DSN=redis://:password@:6379/0 ``` ### 启动进程 ```shell php bin/console enqueue:consume --setup-broker -vvv ``` ## 维护队列进程 消息队列需要手动启动,并且由于es软件的问题也会导致消息队列进程关闭,所以简单的一条命令是无法面对生产环境的,所以我们需要使得消息队列后台运行,开机自启动,失败重启,日志记录。 我们采取的方式是定义成服务(Centos 系统) ```shell sudo vim /etc/systemd/system/esenqueueconsume.service ``` ```ini``` [Unit] Description=Es Enqueue Consume After=network.target [Service] ExecStart=/bin/sh -c 'php bin/console enqueue:consume -vvv >> var/log/enqueue-consume.log 2>&1' Restart=always User=用户名 Group=用户组 Environment=PATH=/usr/bin:/usr/local/bin Environment=NODE_ENV=production WorkingDirectory=设置成自己的项目目录 [Install] WantedBy=multi-user.target ``` 服务系统重载 ```shell sudo systemctl daemon-reload ``` ```shell sudo systemctl start esenqueueconsume.service sudo systemctl stop esenqueueconsume.service ``` 开机自启 ```shell sudo systemctl enable esenqueueconsume.service ``` ## 停用词词典 [https://gitcode.com/mirrors/goto456/stopwords/blob/master/README.md](https://gitcode.com/mirrors/goto456/stopwords/blob/master/README.md) ## 相关链接 [elastic.co](elastic.co) [elasticsearch/elasticsearch](https://packagist.org/packages/elasticsearch/elasticsearch) [friendsofsymfony/elastica-bundle](https://packagist.org/packages/friendsofsymfony/elastica-bundle) [enqueue/elastica-bundle](https://packagist.org/packages/enqueue/elastica-bundle) 帮助PHPZlc项目! 与任何开源项目一样, 贡献代码 或 文档 是最常见的帮助方式, 但我们也有广泛的 赞助机会。 2 赞赏 加入技术群 评论 去登录