BxElsApi.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup ElasticSearch ElasticSearch
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. define('BX_ELASTICSEARCH_LOG', 1);
  12. /**
  13. * ElasticSearch API
  14. */
  15. class BxElsApi extends BxDol
  16. {
  17. static $TYPE = '_doc';
  18. protected $_oModule;
  19. protected $_sError = false;
  20. protected $_iTimeout = 30;
  21. protected $_sApiUrl = 'http://localhost:9200';
  22. protected $_aOprs = array('AND' => array('AG' => 'bool', 'FN' => 'filter'), 'OR' => array('AG' => 'dis_max', 'FN' => 'queries'));
  23. public function __construct(&$oModule)
  24. {
  25. $this->_oModule = $oModule;
  26. $CNF = &$this->_oModule->_oConfig->CNF;
  27. $this->_sApiUrl = getParam($CNF['PARAM_API_URL']);
  28. }
  29. public function searchSimple($sIndex, $sType, $sTerm)
  30. {
  31. if ($sType) {
  32. $sQuery = "/{$sIndex}@{$sType}/_search";
  33. return $this->api($sQuery, array('query' => array('simple_query_string' => array('query' => $sTerm))), 'post');
  34. }
  35. else {
  36. $this->log('ERROR: searchSimple - search without specifying particular module(type) isn\'t supported', '', BX_LOG_ERR);
  37. }
  38. }
  39. public function searchExtended($sIndex, $sType, $aCondition, $aSelection = array())
  40. {
  41. if ($sType) {
  42. $sQuery = "/{$sIndex}@{$sType}/_search";
  43. $aQuery = array('query' => $this->_prepareConditionsGroup($aCondition));
  44. if(!empty($aSelection))
  45. $aQuery = array_merge($aQuery, $aSelection);
  46. return $this->api($sQuery, $aQuery, 'post');
  47. }
  48. else {
  49. $this->log('ERROR: searchExtended - search without specifying particular module(type) isn\'t supported', BX_LOG_ERR);
  50. }
  51. }
  52. protected function _prepareConditionsGroup($aGrp)
  53. {
  54. if(!isset($aGrp['grp']) || !(bool)$aGrp['grp'])
  55. return $this->_prepareConditionsSingle($aGrp);
  56. $aResult = array();
  57. if(!isset($aGrp['opr'], $aGrp['cnds']) || empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
  58. return $aResult;
  59. $aOpr = $this->_aOprs[$aGrp['opr']];
  60. $aResult[$aOpr['AG']] = array($aOpr['FN'] => array());
  61. foreach($aGrp['cnds'] as $aCnd) {
  62. $sMethod = '_prepareConditions' . (isset($aCnd['grp']) && (bool)$aCnd['grp'] ? 'Group' : 'Single');
  63. $aResultCnd = $this->$sMethod($aCnd);
  64. if(!empty($aResultCnd))
  65. $aResult[$aOpr['AG']][$aOpr['FN']][] = $aResultCnd;
  66. }
  67. return $aResult;
  68. }
  69. protected function _prepareConditionsSingle($aCnd)
  70. {
  71. if(!isset($aCnd['val']) && !(isset($aCnd['val1']) || isset($aCnd['val2'])))
  72. return array();
  73. $bTp = !empty($aCnd['tp']);
  74. $bFld = !empty($aCnd['fld']);
  75. $bOprIn = !empty($aCnd['opr']) && $aCnd['opr'] == 'IN';
  76. if($bTp)
  77. $sType = $aCnd['tp'];
  78. else if($bFld) {
  79. if($bOprIn)
  80. $sType = 'terms';
  81. else
  82. $sType = 'term';
  83. }
  84. else
  85. $sType = 'simple_query_string';
  86. $aQuery = array();
  87. switch ($sType) {
  88. case 'term':
  89. case 'terms':
  90. $aQuery = array($aCnd['fld'] => $aCnd['val']);
  91. break;
  92. case 'range':
  93. $aOpr = array();
  94. if(!empty($aCnd['opr1']) && isset($aCnd['val1']))
  95. $aOpr[$aCnd['opr1']] = $aCnd['val1'];
  96. if(!empty($aCnd['opr2']) && isset($aCnd['val2']))
  97. $aOpr[$aCnd['opr2']] = $aCnd['val2'];
  98. $aQuery = array($aCnd['fld'] => $aOpr);
  99. break;
  100. case 'simple_query_string':
  101. $aQuery = array('query' => $aCnd['val']);
  102. break;
  103. }
  104. return array($sType => $aQuery);
  105. }
  106. public function getData($sIndex, $sType, $iContentId)
  107. {
  108. return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId");
  109. }
  110. public function indexData($sIndex, $sType, $iContentId, $aData)
  111. {
  112. return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", $aData, 'put');
  113. }
  114. public function updateData($sIndex, $sType, $iContentId, $aData)
  115. {
  116. return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", $aData, 'post');
  117. }
  118. public function deleteData($sIndex, $sType, $iContentId)
  119. {
  120. return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", array(), 'delete');
  121. }
  122. public function getErrorMsg()
  123. {
  124. return $this->_sError;
  125. }
  126. public function api($sAction, $aData = array(), $sMetod = 'get', $bJsonResponse = true)
  127. {
  128. if (!$this->_sApiUrl) {
  129. $this->_sError = 'Host isn\'t defined';
  130. return null;
  131. }
  132. if (!$sAction) {
  133. $this->_sError = 'Action isn\'t defined';
  134. return null;
  135. }
  136. $sUrl = bx_append_url_params($this->_sApiUrl . $sAction, array(
  137. 'format' => 'json',
  138. 'human' => false,
  139. ));
  140. $sHttpCode = 200;
  141. $s = $this->curl($sUrl, $aData, $sMetod, $sHttpCode);
  142. $this->log($s, '', BX_LOG_DEBUG);
  143. if ($sHttpCode >= 300 || $sHttpCode < 200) {
  144. $this->_sError = 'Action('.$sAction.') returned status - ' . $sHttpCode;
  145. return null;
  146. }
  147. // return JSON response
  148. if ($s && $bJsonResponse) {
  149. if (null === ($a = json_decode($s, true))) {
  150. $this->_sError = 'Can not decode response';
  151. return null;
  152. }
  153. return $a;
  154. }
  155. return $s;
  156. }
  157. protected function curl ($sUrl, $aData = array(), $sMethod = 'get', &$sHttpCode = null, $aBasicAuth = array())
  158. {
  159. $rConnect = curl_init();
  160. curl_setopt($rConnect, CURLOPT_USERAGENT, 'UNA ' . BX_DOL_VERSION);
  161. curl_setopt($rConnect, CURLOPT_URL, $sUrl);
  162. curl_setopt($rConnect, CURLOPT_HEADER, NULL === $sHttpCode ? false : true);
  163. curl_setopt($rConnect, CURLOPT_RETURNTRANSFER, 1);
  164. curl_setopt($rConnect, CURLOPT_VERBOSE, 0);
  165. curl_setopt($rConnect, CURLOPT_HEADER, 0);
  166. curl_setopt($rConnect, CURLOPT_CONNECTTIMEOUT, $this->_iTimeout);
  167. curl_setopt($rConnect, CURLOPT_TIMEOUT, $this->_iTimeout);
  168. if (!ini_get('open_basedir'))
  169. curl_setopt($rConnect, CURLOPT_FOLLOWLOCATION, 1);
  170. if ($aBasicAuth)
  171. curl_setopt($rConnect, CURLOPT_USERPWD, $aBasicAuth['user'] . ':' . $aBasicAuth['password']);
  172. if ('get' != $sMethod)
  173. curl_setopt($rConnect, CURLOPT_CUSTOMREQUEST, strtoupper($sMethod));
  174. if ('post' == $sMethod)
  175. curl_setopt($rConnect, CURLOPT_POST, true);
  176. if ($aData) {
  177. curl_setopt($rConnect, CURLOPT_POSTFIELDS, json_encode($aData));
  178. curl_setopt($rConnect, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  179. }
  180. $sResult = curl_exec($rConnect);
  181. if (NULL !== $sHttpCode)
  182. $sHttpCode = curl_getinfo($rConnect, CURLINFO_HTTP_CODE);
  183. return $sResult;
  184. }
  185. protected function log ($mixed, $sProvider = '', $iLevel = BX_LOG_ERR)
  186. {
  187. if (!defined('BX_ELASTICSEARCH_LOG') || !constant('BX_ELASTICSEARCH_LOG'))
  188. return;
  189. if (is_array($mixed))
  190. $mixed = var_export($mixed, true);
  191. else if (is_object($mixed))
  192. $mixed = json_encode($mixed);
  193. bx_log('bx_elasticsearch', $mixed, $iLevel);
  194. }
  195. }
  196. /** @} */