如何将 Elasticsearch 与 ZF3 集成






2.60/5 (3投票s)
在数字世界中,我们正处于机器学习阶段。我们需要以闪电般的速度完成一切。数据的存储,以我们想要的自定义格式,以及它们的可用性、稳定性,都应该以指尖的轻松程度完成,并且基础设施成本低廉。
引言
在数字世界中,我们正处于机器学习阶段。我们需要以闪电般的速度完成一切。数据的存储,以我们想要的自定义格式,以及它们的可用性、稳定性,都应该以指尖的轻松程度完成,并且基础设施成本低廉。因此,我提议用一个开源解决方案来解决这个问题,无需任何成本。
我们将使用 APACHE 作为 Web 服务器,PHP 作为脚本语言,Elasticsearch 作为 NOSQL 非结构化数据库,以及 Zend Framework 3 (ZF3) 作为开发框架。所以我们正在使用 LAMP(完全开源且不涉及任何成本)。
背景
我一直在寻找安全、健壮、松耦合、敏捷、可配置、超快数据可用并且预算可控的解决方案,因此选择了 Ubuntu 操作系统、Apache Web 服务器、PHP Web 脚本编译器以及 Elasticsearch 作为健壮的非结构化数据库。
业务价值
- 成本效益高
- 降低开发和维护成本
- 改进业务流程
使用代码
在开始之前,我假设开发环境已经搭建并运行。如果还没有,请参考下面的列表来设置环境。
- 如何部署 Ubuntu 16.04 LTS: https://tutorials.ubuntu.com/tutorial/tutorial-install-ubuntu-desktop#0
- 如何设置 LAMP: https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu
- 设置 ZF3: https://github.com/zendframework/zf3-web
- 设置 Elasticsearch: https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-elasticsearch-on-ubuntu-16-04
现在环境已经准备就绪,接下来我们将设置 ZF3 中的模块来从我们的非关系型数据库中获取记录/文档。在此之前,请先在 Elasticsearch 中插入数据(https://elastic.ac.cn/guide/en/elasticsearch/client/php-api/current/_indexing_documents.html)。
步骤 1:
将 Elasticsearch 库添加到根目录的“vendor”文件夹中。您可以从 https://github.com/elastic/elasticsearch-php 获取 Elasticsearch 库。
步骤 2
打开“composer.json”文件,并在“require”数组中添加以下代码:
"require": {
.....
"elasticsearch/elasticsearch": "^5.0",
.....
}
步骤 3
创建日志文件夹以跟踪应用程序级别的错误。因此,我们在根目录下创建“data/log”文件夹。并为“log”文件夹授予读、写和执行权限。
步骤 4
现在让我们创建 Elasticsearch 连接配置详细信息,该详细信息可在所有应用程序模块中访问。因此,打开“config/autoload”文件夹,如果不存在,则添加“local.php”文件。同一个文件将用于添加本地配置详细信息。在同一个文件中添加以下 Elasticsearch 连接配置,例如...
return[
…
'elasticsearch' => [
'connections' => [
'justbuylive' => [
'index' => “es_local”,
'host' => 'localhost',
'port' => '9200',
'scheme' => 'http'
]
],
'type'=>[
'ES_CLIENT_MASTER'=>'client_master'
]
],
..
]
步骤 5
现在是时候创建“modules”及其路由和配置了。首先,我们用名称“Api”创建自定义模块。为此,我们的文件夹结构应如下所示:
[project name]
--config
----autoload
------local.php
----module.config.php
--data
----log
--module
----Api
------config
--------module.config.php
------src
--------Controller
----------Factory
------------RetialerdataserviceControllerFactory.php
----------Plugin
------------ DefaultPlugin.php
------------Factory
------------RetialerdataserviceController.php
--------Entity
--------Listener
--------Repository
------------RetailerdataserviceRepository.php
--------Service
----------Factory
------------RetialerdataserviceManagerFactory.php
----------RetailerdataserviceManager.php
--------Module.php
--vendor
----elasticsearch
------elasticsearch
步骤 6
创建“module/Api/config/module.config.php” Api 模块配置文件和路由文件。包含以下代码...
<!--?php
/**
* @link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Api;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
return [
'doctrine' =----> [
'driver' => [
__NAMESPACE__ . '_driver' => [
'class' => AnnotationDriver::class,
'cache' => 'array',
'paths' => [__DIR__ . '/../src/Entity']
],
'orm_default' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
],
],
'orm_accesslog' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
],
],
]
],
'router' => [
'routes' => [
'retialerdataservice' => [
'type' => Literal::class,
'options' => [
'route' => '/getretailer',
'defaults' => [
'__NAMESPACE__' => 'Api\Controller',
'controller' => Controller\RetialerdataserviceController::class,
'action' => 'index',
],
],
],
'/' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'__NAMESPACE__' => 'Api\Controller',
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\RetialerdataserviceController::class => Controller\Factory\RetialerdataserviceControllerFactory::class,
],
],
'controller_plugins' => [
'invokables' => [
'DefaultPlugin' => 'Api\Controller\Plugin\DefaultPlugin',
]
],
'service_manager' => [
'factories' => [
Service\RetialerdataserviceManager::class => Service\Factory\RetialerdataserviceManagerFactory::class,
'ElasticSearch' => Service\Factory\ElasticClientFactory::class,
]
],
'view_manager' => [
'strategies' => [
'ViewJsonStrategy',
],
],
];
步骤 7
现在是时候创建控制器了,它负责执行/处理请求。“module/Api/src/Controller/RetialerdataserviceController.php”。控制器包含以下代码行...
<!--?php
/**************************************************************************
* Purpose : Manging the retailer data request and response
* Created By: Jaiswar Vipin Kumar R.
**************************************************************************/
namespace Api\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
class RetialerdataserviceController extends AbstractActionController{
/* Varaibe delclaration */
private $_objRetailerDataServiceObject;
/***************************************************************************
* Purpose : Default method get executed.
* Inputs : $pObjRetailerDataServiceManager :: Retailer Data servier
* Manager object refrence
* Return : None.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function __construct($pObjRetailerDataServiceManager){
/* Assign manager refrence */
$this--->_objRetailerDataServiceObject = $pObjRetailerDataServiceManager;
}
/***************************************************************************
* Purpose : First Operation action need to be execute to process the
* request.
* Inputs : None.
* Return : None.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function indexAction(){
/* Getting the consumer service request */
$strRequestContentArr = json_decode($this->getRequest()->getContent(), true);
$strResponseDataArr = array("status" => 0, "message" => "Invalid Requests.");
if(!empty($strRequestContentArr)){
$strResponseDataArr = $this->_objRetailerDataServiceObject->getRetailerDetails($strRequestContentArr);
}else{
$strResponseDataArr = array("status" => 0, "message" => "Invalid Requests.");
}
/* Setting data log */
$this->setLog($strResponseDataArr);
/* return the response is JSON Model Format */
return new JsonModel($strResponseDataArr);
}
/***************************************************************************
* Purpose : Setting log.
* Inputs : $pDataSet :: Data Set.
* Return : None.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function setLog($pDataSet){
/* Creating Today's log file name */
$strFileName = date('Y-m-d');
/* Setting the data log folder relative path */
$strDataLogPath = 'data/log';
/* Checking is data log exists */
if (!file_exists($strDataLogPath) && !is_dir($strDataLogPath)) {
/* if not exists then create with User - Group - Other RWX permission */
mkdir($strDataLogPath, 0777, true);
}
/* Setting the file name path */
$strFileName = $strDataLogPath."/".$strFileName.".txt";
/* Creating log data set */
$strLogData = date('Y-m-d H:i:s')."\n----".json_encode($pDataSet)."\n";
/* writing in the log file */
file_put_contents($strFileName, $strLogData, FILE_APPEND | LOCK_EX);
/* removed used variables */
unset($strLogData, $strFileName, $strDataLogPath);
}
}
步骤 8
现在创建控制器的工厂类,该类有助于将“Service Manager”集成到控制器中,处理业务逻辑,并通过 _invoking() 自动执行方法返回所需输出。导航到“module/Api/src/Controller/Factory/RetialerdataserviceControllerFactory.php”,工厂控制器包含以下代码行...
<?php
namespace Api\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Api\Controller\RetialerdataserviceController;
use Api\Service\RetialerdataserviceManager;
class RetialerdataserviceControllerFactory implements FactoryInterface{
public function __invoke(ContainerInterface $container,$requestedName,array $options = null){
$retialerdataserviceManager = $container->get(RetialerdataserviceManager::class);
// Instantiate the controller and inject dependencies
return new RetialerdataserviceController($retialerdataserviceManager);
}
}
?>
步骤 9
现在创建全局方法,以面向对象的方式简化操作。现在我们使用名称“module/Api/src/Controller/Plugin/ DefaultPlugin.php”来创建插件,这是一个全局资源,在 Api 模块中可用,包含以下代码行...
<?php
namespace Api\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\View\Model\JsonModel;
use Zend\Config\Factory;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
class DefaultPlugin extends AbstractPlugin{
/*****************************************************************************************/
/* Purpose : Creating the result set as array response.
* Inputs : $pObjResultSet :: Result Set,
* : $pIntResultSetType :: 0: MYSQL, 1: Elasticsearch, 2: Postgrays
* Returns : Result set array.
* CreatedBy: Jaiswar VIpin Kumar R.
/*****************************************************************************************/
public function getResultset($pObjResultSet = null, $pIntResultSetType = 0){
/* Variable initialization */
$strReturnArr = array();
$blnRecordExists = false;
$objResultSetContainer = null;
/* Based on the data source type checkig the result set */
switch($pIntResultSetType){
/* MYSQL */
case 0:
/* if result set not empty then do needful */
if(!empty($pObjResultSet)){
/* Value overridding */
$blnRecordExists = true;
$objResultSetContainer = $pObjResultSet;
}
break;
/* Elasticsearch */
case 1:
/* if result set not empty then do needful */
if(isset($pObjResultSet['hits']['total']) && ($pObjResultSet['hits']['total'] > 0)){
/* Value overridding */
$blnRecordExists = true;
$objResultSetContainer = $pObjResultSet['hits']['hits'];
}
break;
/* Postgray */
case 2:
break;
}
/* if record exists then do needful */
if($blnRecordExists){
/* iterating the loop */
foreach($objResultSetContainer as $objResultSetContainerKey => $objResultSetContainerValue){
/* Based on the data source type checkig the result set */
switch($pIntResultSetType){
/* MYSQL */
case 0:
/* Setting the value in array format */
$strReturnArr[] = (array)$objResultSetContainerValue;
break;
/* Elasticsearch */
case 1:
/* Setting the value in array format */
$strReturnArr[] = $objResultSetContainerValue['_source'];
break;
/* Postgray */
case 2:
break;
}
}
}
/* removed used variabled */
unset($blnRecordExists, $objResultSetContainer);
/* return the result set */
return $strReturnArr;
}
}
第 10 步
现在创建存储库类来处理请求,同一个存储库类将从服务管理器类调用。此类包含业务处理。现在创建用于处理“module/Api/src/Repository/RetailerdataserviceRepository.php”的存储库。类包含以下代码行...
<?php
/**************************************************************************
* Purpose : Manging the retailer DML / ORM operation
* Created By: Jaiswar Vipin Kumar R.
**************************************************************************/
namespace Api\Repository;
use Api\Entity\ClientMaster;
use Api\Controller\Plugin\DefaultPlugin;
use Doctrine\ORM\EntityRepository;
use Elasticsearch\ClientBuilder;
use Elasticsearch\Transport;
use Zend\View\Model\JsonModel;
class RetailerdataserviceRepository extends EntityRepository{
private $_strEntityArr = array();
/***************************************************************************
* Purpose : Get the retailer details.
* Inputs : None.
* Return : Retailer Details.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function getRetailerDetailsFromElastica($pStrFilterArr){
/* Variable initialization */
$strReturnArr = array();
try{
/* Getting the local file refrence object */
$objElasticConfiguration = DefaultPlugin::getLocalCofigurationByKey('elasticsearch');
/* if configuration is not set then do needful */
if(empty($objElasticConfiguration)){
/* return error message */
return array("status" => 0, "message" => "Elasticsearch Configuration is not set");
/* Stop the execution */
exit;
}
/* Getting ES connection objects */
$objESSettings = $objElasticConfiguration->connections->justbuylive;
/* Creating ES hosts array */
$strHostArray[] = $objESSettings->host.':'.$objESSettings->port;
/* Creating Elasticssearch Objects */
$esClientObj = ClientBuilder::create()->setHosts($strHostArray)->setRetries(2)->build();
/* Get ES filter */
$strSearchFilterArr = $this->_getFilterCritriaElasticsearch($pStrFilterArr);
/* Filter Array */
$strFilterArr = [
'index' => $objElasticConfiguration->connections->justbuylive->index,
'type' => $objElasticConfiguration->type->ES_CLIENT_MASTER,
'body' => []
];
$strFilterArr['body']['query']['bool']['filter'] = $strSearchFilterArr;
/* removed used variables */
unset($objElasticConfiguration, $objESSettings, $strHostArray, $strSearchFilterArr);
/* Fetching resule set and creating the return result set */
$strReturnArr = DefaultPlugin::getResultset($esClientObj->search($strFilterArr), 1);
/* removed used variables */
unset($esClientObj);
} catch (Exception $ex) {
/* return error message */
return array("status" => 0, "message" => $ex->getMessage());
/* Stop the execution */
exit;
}
/* return resultset */
return $strReturnArr;
}
}
第 11 步
现在创建服务管理器,为 API 模块提供服务作为工作单元。现在创建用于处理“module/Api/src/Service/RetailerdataserviceManager.php”的存储库。类包含以下代码行...
<?php
/**************************************************************************
* Purpose : Retailer Data Service Manager to process the configuration
* DQL and user request and return the appropriate response.
* Created By: Jaiswar Vipin Kumar R.
**************************************************************************/
namespace Api\Service;
use Api\Entity\ClientMaster;
use Exception;
use Zend\Config\Factory;
class RetailerdataserviceManager{
/**
* Doctrine entity manager.
* @var Doctrine\ORM\EntityManager
*/
/* varaible initialization */
private $_entityManager;
/***************************************************************************
* Purpose : Default method get executed in the manager
* Inputs : $pObjRetailerDataServiceEntityManager :: Retailer Data servier
* Manager object refrence
* Return : None.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function __construct($pObjRetailerDataServiceEntityManager) {
$this->_entityManager = $pObjRetailerDataServiceEntityManager;
}
/***************************************************************************
* Purpose : Get the requested retailer details.
* Inputs : $pStrRequestArr :: Customer request array.
* Return : Retailer data set is any.
* Created By : Jaiswar Vipin Kumar R.
****************************************************************************/
public function getRetailerDetails($pStrRequestArr){
/* Return the Retailer details */
return $this->_entityManager->getRepository(ClientMaster::class)->getRetailerDetailsFromElastica($pStrRequestArr);
}
}
第 12 步
现在创建服务管理器工厂类,它实际上作为工作单元,并帮助服务管理器完成模块。现在创建用于处理“module/Api/src/Service/Factory/ RetialerdataserviceManagerFactory.php”的工厂。类包含以下代码行...
<?php
/***************************************************************************************************
* Purpose : This is the factory class for ConfigManager service. The purpose of the factory
* is to instantiate the service and pass it dependencies (inject dependencies)
* Created By: Jaiswar Vipin Kumar R.
***************************************************************************************************/
namespace Api\Service\Factory;
use Interop\Container\ContainerInterface;
use Api\Service\RetailerdataserviceManager;
class RetialerdataserviceManagerFactory{
/***************************************************************************************************
* Purpose : This method creates the ConfigManager service and returns its instance.
* Inputs : $container :: Contianer Interface instant object,
* : $requestedName :: Request handller Name
* : $options :: Configuration Variables
* Returns : Service configuration manager object
***************************************************************************************************/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
/* Getting the ORM connection configuration value */
$entityManager = $container->get('doctrine.entitymanager.orm_default');
/* Return the retailer manager insetance */
return new RetailerdataserviceManager($entityManager);
}
}
注意:我没有添加实体类“ClientMaster”,您可以创建自己的 ORM 实体类并执行它。
编码愉快!!!
关注点
- 业务价值
- 运营价值
- 节省成本
- 可复用的