Query.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. <?php namespace App\Servers\ElasticSearch\Lib;
  2. use Illuminate\Pagination\LengthAwarePaginator;
  3. use Illuminate\Pagination\Paginator;
  4. /**
  5. * ES Query
  6. *
  7. * @author 刘相欣
  8. *
  9. */
  10. class Query{
  11. // 查询属性
  12. private $query = ['index'=>'','type'=>'','body'=>[]];
  13. private $client = null;
  14. private $searchResult = null;
  15. // 分组参数
  16. private $groupBy = [];
  17. // 排序参数
  18. private $orderBy = [];
  19. /**
  20. * 指定索引
  21. *
  22. * @param \Elasticsearch\Client $client ES链接
  23. * @param String $index 索引名称
  24. * @param String $type 类型
  25. * @return $this
  26. *
  27. */
  28. public function __construct($client,$index,$type='_doc'){
  29. // 终端
  30. $this->client = $client;
  31. // 指定索引
  32. $this->query['index'] = $index;
  33. $this->query['type'] = $type;
  34. // 链式操作
  35. return $this;
  36. }
  37. /**
  38. * 忽略错误
  39. *
  40. * @param Array $ignore 忽略的错误代码
  41. *
  42. * @return $this
  43. */
  44. public function ignore( array $ignore=[400,401,403,404,408,409]){
  45. // 更新忽略错误
  46. $this->query['client'] = ['ignore' => $ignore];
  47. // 链式操作
  48. return $this;
  49. }
  50. /**
  51. * 组合查询
  52. *
  53. * @param Array $column 查询条件 [["field","operator","value"]] | ["field"=>"value"]
  54. * @param String $searchType 查询类型,and | or
  55. *
  56. * @return $this
  57. */
  58. public function where($column,$searchType='and'){
  59. // 统一小写
  60. $searchType = strtolower($searchType);
  61. // 如果是关联数组
  62. if( count(array_filter(array_keys($column), 'is_string')) > 0 ) {
  63. // 索引数组,循环处理
  64. foreach ($column as $field=>$value) {
  65. // 调用插入
  66. $searchType == 'or' ? $this->should($field,$value,'=') : $this->must($field,$value,'=');
  67. }
  68. // 返回当前链接
  69. return $this;
  70. }
  71. // 索引数组,循环处理
  72. foreach ($column as $value) {
  73. // 切割数组
  74. list($field,$operator,$value) = $value;
  75. // 统一小写
  76. $operator = strtolower($operator);
  77. // 如果有or 调用的是 should
  78. if( $searchType == 'or' ) {
  79. // 调用或查询
  80. $this->should($field,$value,$operator);
  81. // 跳出本次循环
  82. continue;
  83. }
  84. // 判断操作符号,= in like,调用must
  85. if( in_array($operator,['in','like','=','exists']) ) $this->must($field,$value,$operator);
  86. // 判断操作符号,!= / not in / 调用的是 must_not
  87. if( in_array($operator,['!=','not in','<>','not exists','not between']) ) $this->must_not($field,$value,$operator);
  88. // 判断操作符号,> < BETWEEN 调用的是 filter
  89. if( in_array($operator,['>=','<=','<','>','between']) ) $this->filter($field,$value,$operator);
  90. }
  91. // 链式操作
  92. return $this;
  93. }
  94. /**
  95. * Or查询
  96. *
  97. * @param String $field 查询字段
  98. * @param String $value 查询值
  99. * @param String $operator 操作符 = in like < > !=
  100. *
  101. *
  102. * @return $this
  103. */
  104. public function should($field,$value,$operator){
  105. // 如果没有对应的参数,赋值一个空数组,避免插入参数报错
  106. if( !isset($this->query['body']['query']['bool']['should']) ) $this->query['body']['query']['bool']['should'] = [];
  107. // 赋值
  108. $this->query['body']['query']['bool']['should'][] = $this->operatorToType($field,$value,$operator);
  109. // 链式操作
  110. return $this;
  111. }
  112. /**
  113. * And查询
  114. *
  115. * @param String $field 查询字段
  116. * @param String $value 查询值
  117. * @param String $operator 操作符 = in like < > !=
  118. *
  119. *
  120. * @return $this
  121. */
  122. public function must($field,$value,$operator){
  123. // 如果没有对应的参数,赋值一个空数组,避免插入参数报错
  124. if( !isset($this->query['body']['query']['bool']['must']) ) $this->query['body']['query']['bool']['must'] = [];
  125. // 赋值
  126. $this->query['body']['query']['bool']['must'][] = $this->operatorToType($field,$value,$operator);
  127. // 链式操作
  128. return $this;
  129. }
  130. /**
  131. * NOT IN 与 != 查询
  132. *
  133. * @param String $field 查询字段
  134. * @param String $value 查询值
  135. * @param String $operator 操作符 = in like < > !=
  136. *
  137. *
  138. * @return $this
  139. */
  140. public function must_not($field,$value,$operator){
  141. // 如果没有对应的参数,赋值一个空数组,避免插入参数报错
  142. if( !isset($this->query['body']['query']['bool']['must_not']) ) $this->query['body']['query']['bool']['must_not'] = [];
  143. // 赋值
  144. $this->query['body']['query']['bool']['must_not'][] = $this->operatorToType($field,$value,$operator);
  145. // 链式操作
  146. return $this;
  147. }
  148. /**
  149. * 字段筛选
  150. *
  151. * @param String $field 查询字段
  152. * @param String $value 查询值
  153. * @param String $operator 操作符 = in like < > !=
  154. *
  155. *
  156. * @return $this
  157. */
  158. public function filter($field,$value,$operator){
  159. // 如果没有对应的参数,赋值一个空数组,避免插入参数报错
  160. if( !isset($this->query['body']['query']['bool']['filter']) ) $this->query['body']['query']['bool']['filter'] = [];
  161. // 赋值
  162. $this->query['body']['query']['bool']['filter'][] = $this->operatorToType($field,$value,$operator);
  163. // 链式操作
  164. return $this;
  165. }
  166. /**
  167. * 处理操作符转换查询类型
  168. *
  169. * @param String $field 查询字段
  170. * @param String $value 查询值
  171. * @param String $operator 操作符 = in like < > !=
  172. *
  173. */
  174. private function operatorToType($field,$value,$operator){
  175. // 转换大小写
  176. $operator = strtolower($operator);
  177. // in = <> !=
  178. if( in_array($operator,['in','=','<>','!=','not in']) ) return is_array($value) ? ['terms'=>[$field=>$value]] : ['term'=>[$field=>$value]];
  179. // 存在不存在
  180. if( in_array($operator,['exists','not exists']) ) return ['exists'=>['field'=>$field]];
  181. // like
  182. if( $operator == 'like' ) return ['match'=>[$field=>$value]];
  183. // 区间、等于
  184. if( $operator == '>' ) return ['range'=>[$field=>['gt'=>$value]]];
  185. if( $operator == '<' ) return ['range'=>[$field=>['lt'=>$value]]];
  186. if( $operator == '>=' ) return ['range'=>[$field=>['gte'=>$value]]];
  187. if( $operator == '<=' ) return ['range'=>[$field=>['lte'=>$value]]];
  188. if( $operator == 'between' ) {
  189. // 如果是区间,数值切割
  190. $value = is_array($value) ? $value : explode(',',$value);
  191. // 排序,避免区间错误
  192. sort($value);
  193. // 返回结果
  194. return ['range'=>[$field=>['gte'=>$value[0],'lte'=>$value[1]]]];
  195. }
  196. if( $operator=='not between' ) {
  197. // 如果是区间,数值切割
  198. $value = is_array($value) ? $value : explode(',',$value);
  199. // 排序,避免区间错误
  200. sort($value);
  201. // 返回结果
  202. return ['range'=>[$field=>['gte'=>$value[0],'lte'=>$value[1]]]];
  203. }
  204. // 默认是等于
  205. return ['term'=>[$field=>$value]];
  206. }
  207. /**
  208. * 排序
  209. *
  210. * @param String $column 查询字段
  211. * @param String $direction 排序规则 asc | desc
  212. *
  213. * @return $this
  214. */
  215. public function orderBy($column,$direction='asc'){
  216. // 分组
  217. $this->orderBy[] = ['column'=>$column,'direction'=>$direction];
  218. // 获取结果数据
  219. return $this;
  220. }
  221. /**
  222. * 倒序排序
  223. *
  224. * @param String $column 查询字段,排序规则desc
  225. *
  226. * @return $this
  227. */
  228. public function orderByDesc($column){
  229. // 分组
  230. $this->orderBy[] = ['column'=>$column,'direction'=>'desc'];
  231. // 链式操作
  232. return $this;
  233. }
  234. /**
  235. * 排序数据
  236. *
  237. */
  238. private function orderByToParam(){
  239. // 没有分组
  240. if( !$this->groupBy ){
  241. // 如果没有对应的参数,赋值一个空数组,避免插入参数报错
  242. if( !isset($this->query['body']['sort']) ) $this->query['body']['sort'] = [];
  243. // 循环分组参数
  244. foreach ( $this->orderBy as $key=>$value ) {
  245. // 增加排序
  246. $this->query['body']['sort'][] = [$value['column']=>['order'=>$value['direction']]];
  247. // 删除已添加的
  248. unset($this->orderBy[$key]);
  249. }
  250. }
  251. // 返回数据
  252. return $this;
  253. }
  254. /**
  255. * 分组
  256. *
  257. * @param String $column 分组字段
  258. * @param String $alias 分组字段设置别名
  259. *
  260. * @return $this
  261. */
  262. public function groupBy($column='id',$alias=''){
  263. // 分组
  264. $this->groupBy[] = ['column'=>$column,'alias'=>$alias];
  265. // 获取结果数据
  266. return $this;
  267. }
  268. /**
  269. * 分组数据
  270. *
  271. */
  272. private function groupByToParam(){
  273. // 循环分组参数
  274. foreach ( $this->groupBy as $key=>$value ) {
  275. // 分组追加
  276. $aggs = $this->paramsPush($value['column'],$value['alias'],$this->query['body'],'terms');
  277. // // 组合条件
  278. $this->query['body'] = $aggs;
  279. // 删除已添加的
  280. unset($this->groupBy[$key]);
  281. }
  282. // 取消分组数据
  283. $this->orderBy = [];
  284. // 返回结果
  285. return $this;
  286. }
  287. /**
  288. * 获取字段
  289. * @param Array $columns 查询字段
  290. *
  291. * @return $this
  292. */
  293. public function select($columns=[]){
  294. // 获取对应的字段
  295. if( $columns ) $this->query['body']['_source'] = $columns;
  296. // 链式操作
  297. return $this;
  298. }
  299. /**
  300. * 页码获取
  301. * @param Int $size 每页条数
  302. * @param Int $page 页码,大于0
  303. *
  304. * @return $this
  305. */
  306. public function page($size,$page = 0){
  307. // 每页条数
  308. $this->query['body']['size'] = $size;
  309. // 如果有页码
  310. if( $page > 0 ) $this->query['body']['from'] = ($page-1) * $size;
  311. // 链式操作
  312. return $this;
  313. }
  314. /**
  315. * 页码获取
  316. * @param Int $size 每页条数
  317. *
  318. * @return $this
  319. */
  320. public function limit($size){
  321. // 每页条数
  322. $this->query['body']['size'] = $size;
  323. // 链式操作
  324. return $this;
  325. }
  326. /**
  327. * 执行搜索
  328. *
  329. */
  330. private function search(){
  331. // 追加排序
  332. $this->orderByToParam();
  333. // 追加分组
  334. $this->groupByToParam();
  335. // 执行搜索后返回结果并添加到属性
  336. $this->searchResult = $this->client->search($this->query);
  337. // 返回结果
  338. return $this->searchResult;
  339. }
  340. /**
  341. * 获取列表
  342. *
  343. * @param Array $columns 查询字段
  344. *
  345. */
  346. public function get($columns=[]){
  347. // 指定字段的话,添加参数
  348. if( $columns ) $this->select($columns);
  349. // 执行搜索并获取结果
  350. $result = $this->search();
  351. // 获取结果
  352. $result = isset($result['hits']['hits']) ? array_column($result['hits']['hits'],'_source') : [];
  353. // 清除参数与搜索结果
  354. $this->cleanQuery();$this->cleanSearchResult();
  355. // 返回结果
  356. return $result;
  357. }
  358. /**
  359. * 获取列表
  360. *
  361. * @param Int $perPage 每页数量
  362. * @param Array $columns 查询字段
  363. *
  364. * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
  365. *
  366. */
  367. public function paginate($perPage = 15, $columns = [], $pageName = 'page', $page = null){
  368. // 指定字段的话,添加参数
  369. if( $columns ) $this->select($columns);
  370. // 页码
  371. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  372. // 只获取一条
  373. $this->page($perPage,$page);
  374. // 执行搜索并获取结果
  375. $result = $this->search();
  376. // 总数
  377. $total = isset($result['hits']['total']['value']) ? $result['hits']['total']['value'] : 0;
  378. // 获取结果
  379. $result = isset($result['hits']['hits']) ? array_column($result['hits']['hits'],'_source') : [];
  380. // 构建分页
  381. $paginator = new LengthAwarePaginator($result,$total,$perPage, $page, ['path'=>Paginator::resolveCurrentPath(),'pageName'=>$pageName]);
  382. // 清除参数与搜索结果
  383. $this->cleanQuery();$this->cleanSearchResult();
  384. // 返回结果
  385. return $paginator;
  386. }
  387. /**
  388. * 获取一条数据
  389. *
  390. * @param Array $columns 查询字段
  391. *
  392. * @return Mixed 结果
  393. *
  394. */
  395. public function first($columns=[]){
  396. // 只获取一条
  397. $this->page(1,1);
  398. // 指定字段的话,添加参数
  399. if( $columns ) $this->select($columns);
  400. // 执行搜索并获取结果
  401. $result = $this->search();
  402. // 获取结果
  403. $result = isset($result['hits']['hits']) ? array_column($result['hits']['hits'],'_source') : [];
  404. // 清除参数与搜索结果
  405. $this->cleanQuery();$this->cleanSearchResult();
  406. // 返回一条
  407. return isset($result[0]) ? $result[0] : [];
  408. }
  409. /**
  410. * 分组追加
  411. *
  412. * @param String $column 分组字段
  413. * @param String $alias 分组字段设置别名
  414. *
  415. * @return $this
  416. *
  417. */
  418. private function paramsPush($column,$alias,$aggs,$type){
  419. // 定义参数
  420. $params = [$type=>['field'=>$column]];
  421. // 第一层增加
  422. if( !empty($this->query['body']['size']) && $type == 'terms' ) $params[$type]['size'] = $this->query['body']['size'];
  423. // 排序
  424. foreach ( $this->orderBy as $key => $value) {
  425. // 同一个字段使用排序
  426. if( $type == 'terms' ){
  427. // 添加排序
  428. $params[$type]['order'] = [$value['column']=>$value['direction']];
  429. // 已用的排序删除
  430. unset($this->orderBy[$key]);
  431. }
  432. }
  433. // 先判断是否有参数
  434. if( isset($aggs['aggregations']) ){
  435. // 如果有参数,判断是否有分组
  436. foreach ( $aggs['aggregations'] as $key => $value) {
  437. // 如果有分组,获取下一层
  438. if( $value ) {
  439. // 追加到数据
  440. $temp = $this->paramsPush($column,$alias,$value,$type);
  441. // 如果有值
  442. if( isset($temp['aggregations']) ) $aggs['aggregations'][$key] = $temp;
  443. }else{
  444. // 组合数据
  445. $aggs['aggregations'][($alias?$alias:$column)] = $params;
  446. }
  447. }
  448. }else{
  449. // 没条件参数的,直接新增一条
  450. $aggs['aggregations'][($alias?$alias:$column)] = $params;
  451. }
  452. // 返回结果
  453. return $aggs;
  454. }
  455. /**
  456. * 聚合数据处理
  457. *
  458. * @param Array $result 最终结果
  459. * @param String $column 结果字段
  460. *
  461. * @return mixed
  462. */
  463. private function aggsDataResult($result,$column){
  464. // 判断是否是聚合数据,如果值单个统计,返回
  465. if( isset($result[$column]['value']) ) return (int) $result[$column]['value'];
  466. // 针对stats,返回多个结果
  467. if( isset($result[$column]['sum']) && isset($result[$column]['count']) && isset($result[$column]['avg']) ) return $result[$column];
  468. // 没有桶数据,返回空数组
  469. if( !array_column($result,'buckets') ) return [];
  470. // 列表
  471. return $this->bucketsList($result);
  472. }
  473. /**
  474. * 聚合桶数据递归处理
  475. * @param Array $buckets 最终结果
  476. * @param String $data 临时存储数据
  477. * @param String $list 列表数据
  478. */
  479. private function bucketsList($buckets,$data=[],&$list=[]){
  480. // 循环数据
  481. foreach ( $buckets as $key => $value ) {
  482. // 如果还有下一层
  483. if( isset($value['buckets']) ) {
  484. foreach ($value['buckets'] as $vv) {
  485. // 获取字段值
  486. $data[$key] = $vv['key'];
  487. // 继续下一层
  488. $list = $this->bucketsList($vv,$data,$list);
  489. }
  490. }
  491. // 不是值的数据
  492. if( !isset($value['value'] ) && !isset($value['sum']) ) continue;
  493. // 获取数据
  494. if( isset($value['value']) ) $data[$key] = $value['value'];
  495. // 获取数据
  496. if( isset($value['sum']) ) $data[$key] = $value;
  497. // 追加数据
  498. $list[] = $data;
  499. }
  500. // 返回结果
  501. return $list;
  502. }
  503. /**
  504. * 聚合数据处理
  505. *
  506. * @param String $column 统计字段
  507. * @param String $alias 统计字段设置别名
  508. *
  509. * @return mixed
  510. */
  511. private function aggsDataHandler($column){
  512. // 查询结果
  513. $result = $this->search();
  514. // 判断是否有结果
  515. if( empty($result['aggregations']) ) return [];
  516. // 进行查询
  517. $result = $this->aggsDataResult($result['aggregations'],$column);
  518. // 清除参数与搜索结果
  519. $this->cleanQuery();$this->cleanSearchResult();
  520. // 返回结果
  521. return $result;
  522. }
  523. /**
  524. * 统计个数
  525. *
  526. * @param String $column 统计字段
  527. * @param String $alias 统计字段设置别名
  528. *
  529. * @return mixed
  530. */
  531. public function count($column='id',$alias=''){
  532. // 追加分组
  533. $this->groupByToParam();
  534. // 组合条件
  535. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'cardinality');
  536. $this->query['body']['size'] = 0;
  537. // 执行搜索并获取结果
  538. $result = $this->aggsDataHandler($alias?$alias:$column);
  539. // 获取结果数据
  540. return $result;
  541. }
  542. /**
  543. * 最大数据
  544. * @param String $column 统计字段
  545. * @param String $alias 统计字段设置别名
  546. * @return mixed
  547. *
  548. */
  549. public function max($column,$alias=''){
  550. // 追加分组
  551. $this->groupByToParam();
  552. // 组合条件
  553. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'max');
  554. $this->query['body']['size'] = 0;
  555. // 执行搜索并获取结果
  556. $result = $this->aggsDataHandler($alias?$alias:$column);
  557. // 获取结果数据
  558. return $result;
  559. }
  560. /**
  561. * 最小数
  562. * @param String $column 统计字段
  563. * @param String $alias 统计字段设置别名
  564. * @return mixed
  565. */
  566. public function min($column,$alias=''){
  567. // 追加分组
  568. $this->groupByToParam();
  569. // 组合条件
  570. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'min');
  571. $this->query['body']['size'] = 0;
  572. // 执行搜索并获取结果
  573. $result = $this->aggsDataHandler($alias?$alias:$column);
  574. // 获取结果数据
  575. return $result;
  576. }
  577. /**
  578. * 平均数
  579. * @param String $column 统计字段
  580. * @param String $alias 统计字段设置别名
  581. * @return mixed
  582. */
  583. public function avg($column,$alias=''){
  584. // 追加分组
  585. $this->groupByToParam();
  586. // 组合条件
  587. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'avg');
  588. $this->query['body']['size'] = 0;
  589. // 执行搜索并获取结果
  590. $result = $this->aggsDataHandler($alias?$alias:$column);
  591. // 获取结果数据
  592. return $result;
  593. }
  594. /**
  595. * 计算总数
  596. * @param String $column 统计字段
  597. * @param String $alias 统计字段设置别名
  598. * @return mixed
  599. */
  600. public function sum($column,$alias=''){
  601. // 追加分组
  602. $this->groupByToParam();
  603. // 组合条件
  604. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'sum');
  605. $this->query['body']['size'] = 0;
  606. // 执行搜索并获取结果
  607. $result = $this->aggsDataHandler($alias?$alias:$column);
  608. // 获取结果数据
  609. return $result;
  610. }
  611. /**
  612. * 聚合组合
  613. * @param String $column 统计字段
  614. * @param String $alias 统计字段设置别名
  615. * @return mixed
  616. */
  617. public function stats($column,$alias=''){
  618. // 追加分组
  619. $this->groupByToParam();
  620. // 组合条件
  621. $this->query['body'] = $this->paramsPush($column,$alias,$this->query['body'],'stats');
  622. $this->query['body']['size'] = 0;
  623. // 执行搜索并获取结果
  624. $result = $this->aggsDataHandler($alias?$alias:$column);
  625. // 获取结果数据
  626. return $result;
  627. }
  628. /**
  629. * 查询指定ID数据
  630. *
  631. * @param Int $id ID
  632. *
  633. * @return array
  634. *
  635. */
  636. public function find($id){
  637. // 去除查询参数
  638. unset($this->query['body']);
  639. // 拼接参数
  640. $this->query['id'] = $id;
  641. // 执行搜索并获取结果
  642. $result = $this->client->get($this->query);
  643. // 未查询到数据
  644. if( !isset($result['found']) || !$result['found'] ) return [];
  645. // 清除参数与搜索结果
  646. $this->cleanQuery();$this->cleanSearchResult();
  647. // 返回一条
  648. return $result['_source'];
  649. }
  650. /**
  651. * SQL查询
  652. *
  653. * @param String $query 查询sql语句
  654. * @param String $format 简短标头如json,yaml,smile,cbor,txt,csv,tsv
  655. * @param Array $body 更多参数
  656. * String $cursor 游标
  657. * Int $fetch_size 分页条数
  658. */
  659. public function sql($query,$body=[],$format='json'){
  660. // 简短标头,如json,yaml,smile,cbor,txt,csv,tsv
  661. $params = ['format'=>$format];
  662. // 查询sql语句
  663. $params['body']['query'] = $query;
  664. // 游标
  665. if( !empty($body['cursor']) ) $params['body']['cursor'] = $body['cursor'];
  666. // 条数
  667. if( !empty($body['fetch_size']) ) $params['body']['fetch_size'] = $body['fetch_size'];
  668. // 查询结果
  669. $result = $this->client->sql()->query($params);
  670. // 最终数据
  671. $data = [];
  672. // 循环获取数据
  673. foreach ($result['rows'] as $row) {
  674. // 重新赋值
  675. $temp = [];
  676. // 匹配字段
  677. foreach ($row as $key => $value) {
  678. // 获取字段
  679. $temp[$result['columns'][$key]['name']] = $value;
  680. }
  681. // 获取数据
  682. $data[] = $temp;
  683. }
  684. // 获取数据
  685. $data = ['data'=>$data];
  686. // 获取数据
  687. if( !empty($body['fetch_size']) ) $data['cursor'] = $result['cursor'];
  688. // 进行查询
  689. return $data;
  690. }
  691. /**
  692. * SQL查询分页
  693. *
  694. */
  695. public function sqlPaginate($query,$body=[],$format='json',$perPage = 15,$pageName = 'page', $page = null){
  696. // 查询数据
  697. $data = $this->sql($query,$body,$format)['data'];
  698. // 总数
  699. $total = count($data);
  700. // 页码
  701. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  702. // 从哪里开始
  703. $from = ($page-1) * $perPage;
  704. // 截取数组的一部分
  705. $data = array_slice($data,$from,$perPage);
  706. // 构建分页
  707. $paginator = new LengthAwarePaginator($data,$total,$perPage, $page, ['path'=>Paginator::resolveCurrentPath(),'pageName'=>$pageName]);
  708. // 返回结果
  709. return $paginator;
  710. }
  711. /**
  712. * SQL查询分页
  713. *
  714. */
  715. public function sqlFirst($query,$body=[],$format='json'){
  716. // 获取总积分
  717. $data = $this->sql($query,$body,$format);
  718. // 只获取一条
  719. $data = array_shift($data['data']);
  720. // 返回结果
  721. return $data;
  722. }
  723. /**
  724. * SQL查询分页
  725. *
  726. */
  727. public function sqlValue($query,$body=[],$format='json'){
  728. // 获取总积分
  729. $data = $this->sql($query,$body,$format);
  730. // 只获取一条
  731. $data = array_shift($data['data']);
  732. // 取值
  733. $data = array_shift($data);
  734. // 返回结果
  735. return $data;
  736. }
  737. /**
  738. * SQL转DSL查询
  739. *
  740. * @param String $query 查询sql语句
  741. *
  742. */
  743. public function translate($query){
  744. // 查询sql语句
  745. $params['body']['query'] = $query;
  746. // 查询结果
  747. $result = $this->client->sql()->translate($params);
  748. // 进行查询
  749. return $result;
  750. }
  751. /**
  752. * 添加文档
  753. *
  754. * @param Array $columns 修改字段数据
  755. * @param Mixed $id id
  756. * @return Mixed 结果
  757. *
  758. */
  759. public function insert($columns,$id=null){
  760. // 没有需要修改的数据
  761. if( !$columns ) return false;
  762. // 如果指定了ID
  763. if( !is_null($id) ) $this->query['id'] = $id;
  764. // 修改数据的字段
  765. $this->query['body'] = $columns;
  766. // 执行结果
  767. $response = $this->client->index($this->query);
  768. // 清除参数与搜索结果
  769. $this->cleanQuery();$this->cleanSearchResult();
  770. // 返回一条
  771. return $response;
  772. }
  773. /**
  774. * 删除文档
  775. *
  776. * @param Array $columns 修改字段数据
  777. * @param Mixed $id id
  778. * @return Mixed 结果
  779. *
  780. */
  781. public function delete($id=null){
  782. // 如果指定了ID
  783. if( !is_null($id) ) {
  784. // 指定ID
  785. $this->query['id'] = $id;
  786. // 去除查询参数
  787. unset($this->query['body']);
  788. }
  789. // 执行结果
  790. $response = is_null($id) ? $this->client->deleteByQuery($this->query) : $this->client->delete($this->query);
  791. // 清除参数与搜索结果
  792. $this->cleanQuery();$this->cleanSearchResult();
  793. // 返回一条
  794. return $response;
  795. }
  796. /**
  797. * 修改文档
  798. *
  799. * @param Array $columns 修改字段数据
  800. * @param Mixed $id id
  801. * @return Mixed 结果
  802. *
  803. */
  804. public function update($columns=[],$id=null){
  805. // 如果指定了ID
  806. if( !is_null($id) ) {
  807. // 指定ID
  808. $this->query['id'] = $id;
  809. // 去除查询参数
  810. $this->query['body'] = isset($this->query['body']['script']) ? ['script'=>$this->query['body']['script']] : [];
  811. }
  812. // 是否存在字段
  813. $this->query['body']['script'] = isset($this->query['body']['script']) ? $this->query['body']['script'] : '';
  814. // 拼接数据
  815. foreach ($columns as $key => $value) {
  816. // 拼接参数
  817. $this->query['body']['script'] .= 'ctx._source.'.$key.' = '.$value.';';
  818. }
  819. // 执行更新操作
  820. $result = is_null($id) ? $this->client->updateByQuery($this->query) : $this->client->update($this->query);
  821. // 清除参数与搜索结果
  822. $this->cleanQuery();$this->cleanSearchResult();
  823. // 返回一条
  824. return $result;
  825. }
  826. /**
  827. * 自增
  828. *
  829. * @param String $columns 修改字段
  830. * @param Int $step 步长
  831. *
  832. */
  833. public function increment($columns,$step=1){
  834. // 没有需要修改的数据
  835. if( !$columns ) return false;
  836. // 是否存在字段
  837. $this->query['body']['script'] = isset($this->query['body']['script']) ? $this->query['body']['script'] : '';
  838. // 拼接参数
  839. $this->query['body']['script'] .= 'ctx._source.'.$columns.' += '.$step.';';
  840. // 返回一条
  841. return $this;
  842. }
  843. /**
  844. * 自减
  845. *
  846. * @param String $columns 修改字段
  847. * @param Int $step 步长
  848. *
  849. */
  850. public function decrement($columns,$step=1){
  851. // 没有需要修改的数据
  852. if( !$columns ) return false;
  853. // 是否存在字段
  854. $this->query['body']['script'] = isset($this->query['body']['script']) ? $this->query['body']['script'] : '';
  855. // 拼接参数
  856. $this->query['body']['script'] .= 'ctx._source.'.$columns.' -= '.$step.';';
  857. // 返回一条
  858. return $this;
  859. }
  860. /**
  861. * 清除参数
  862. *
  863. * @return $this
  864. *
  865. */
  866. private function cleanQuery(){
  867. // 参数赋值空数组
  868. $this->query = ['index'=>'','type'=>'','body'=>[]];
  869. // 链式操作
  870. return $this;
  871. }
  872. /**
  873. * 清除搜索结果
  874. *
  875. * @return $this
  876. *
  877. */
  878. private function cleanSearchResult(){
  879. // 参数赋值空数组
  880. $this->searchResult = null;
  881. // 链式操作
  882. return $this;
  883. }
  884. }