1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
opensearch.php
См. документацию.
1<?php
3
5{
6 public $arForumTopics = [];
7 private $errorText = '';
8 private $errorNumber = 0;
9 public $tags = '';
10 public $SITE_ID = '';
11 public $connectionString = '';
12 protected $user = '';
13 protected $password = '';
14 public $indexName = '';
15 public $siteAnalyzerMap = [];
16
17 public function connect($connectionString, $user = '', $password = '', $indexName = '', $ignoreErrors = false, $siteAnalyzerMap = '')
18 {
19 global $APPLICATION;
20
21 if (!preg_match('/^[a-zA-Z0-9_-]+$/', $indexName))
22 {
23 if ($ignoreErrors)
24 {
25 $APPLICATION->ThrowException(GetMessage('SEARCH_OPENSEARCH_CONN_ERROR_INDEX_NAME'));
26 }
27 else
28 {
29 throw new \Bitrix\Main\Db\ConnectionException('OpenSearch connect error', GetMessage('SEARCH_OPENSEARCH_CONN_ERROR_INDEX_NAME'));
30 }
31 return false;
32 }
33
34 $error = '';
35 $server = new Bitrix\Main\Web\HttpClient([
36 'disableSslVerification' => true,
37 ]);
38
39 $server->setAuthorization($user, $password);
40 $strJson = $server->get($connectionString);
41 if ($server->getStatus() !== 200 || !is_array(json_decode($strJson, true)))
42 {
43 $error = $strJson;
44 if ($ignoreErrors)
45 {
46 $APPLICATION->ThrowException(GetMessage('SEARCH_OPENSEARCH_CONN_ERROR', ['#ERRSTR#' => $error]));
47 }
48 else
49 {
50 throw new \Bitrix\Main\Db\ConnectionException('OpenSearch connect error', GetMessage('SEARCH_OPENSEARCH_CONN_ERROR', ['#ERRSTR#' => $error]));
51 }
52 return false;
53 }
54
55 $this->connectionString = $connectionString;
56 $this->user = $user;
57 $this->password = $password;
58
59 $this->siteAnalyzerMap = $siteAnalyzerMap ?: [];
60 if (!$this->siteAnalyzerMap)
61 {
62 $langs = CLang::GetList();
63 while ($site = $langs->Fetch())
64 {
65 $analyzer = COption::GetOptionString('search', 'opensearch_analyzer_' . $site['ID']);
66 if (!$analyzer)
67 {
68 $analyzer = array_search($site['LANGUAGE_ID'], static::getLanguageAnalyzers()) ?: 'english';
69 }
70 $this->siteAnalyzerMap[$site['ID']] = $analyzer;
71 }
72 }
73
74 if ($ignoreErrors)
75 {
76 foreach ($this->siteAnalyzerMap as $siteId => $analyzer)
77 {
78 if (!$this->checkIndexTemplate($indexName, $siteId, $analyzer))
79 {
80 $APPLICATION->ThrowException(GetMessage('SEARCH_OPENSEARCH_CONN_ERROR', ['#ERRSTR#' => $this->getError()]));
81 return false;
82 }
83 }
84 }
85
86 $this->indexName = $indexName;
87 $this->connectionIndex = $connectionIndex;
88
89 return true;
90 }
91
92 // https://opensearch.org/docs/latest/analyzers/supported-analyzers/index/
93 public static function getLanguageAnalyzers()
94 {
95 return [
96 'arabic' => 'ar',
97 'armenian' => '',
98 'basque' => '',
99 'bengali' => '',
100 'brazilian' => 'br',
101 'bulgarian' => '',
102 'catalan' => '',
103 'czech' => '',
104 'danish' => '',
105 'dutch' => '',
106 'english' => 'en',
107 'estonian' => '',
108 'finnish' => '',
109 'french' => 'fr',
110 'galician' => '',
111 'german' => 'de',
112 'greek' => '',
113 'hindi' => '',
114 'hungarian' => '',
115 'indonesian' => 'ms',
116 'irish' => '',
117 'italian' => 'it',
118 'latvian' => '',
119 'lithuanian' => 'lt',
120 'norwegian' => '',
121 'persian' => '',
122 'portuguese' => '',
123 'romanian' => '',
124 'russian' => 'ru',
125 'sorani' => '',
126 'spanish' => 'la',
127 'swedish' => '',
128 'thai' => 'th',
129 'turkish' => 'tr',
130 ];
131 }
132
133 protected $version = 27;
134 protected function checkIndexTemplate($indexName, $siteId, $analyzer, $adminNotify = true)
135 {
136 $templateName = $indexName . '-' . $siteId. '-template';
137 $result = $this->query('GET', '/_index_template/' . $templateName);
138 if (!$result && $this->errorNumber !== 404)
139 {
140 return false;
141 }
142
143 $versionMatch = (
144 isset($result['index_templates'][0]['index_template']['version'])
145 && $result['index_templates'][0]['index_template']['version'] === $this->version
146 );
147
148 $analyzerMatch = true;
149 if ($analyzer)
150 {
151 $analyzerMatch = (
152 isset($result['index_templates'][0]['index_template']['template']['mappings']['properties']['body']['analyzer'])
153 && $result['index_templates'][0]['index_template']['template']['mappings']['properties']['body']['analyzer'] === $analyzer
154 );
155 }
156 else
157 {
158 $analyzerMatch = !isset($result['index_templates'][0]['index_template']['template']['mappings']['properties']['body']['analyzer']);
159 }
160
161 $updateNeeded = !$versionMatch || !$analyzerMatch;
162
163 if ($updateNeeded)
164 {
165 $result = $this->query('DELETE', '/_index_template/' . $templateName);
166 }
167
168 if ($this->errorNumber === 404 || $updateNeeded)
169 {
170 $template = [
171 'mappings' => [
172 'properties' => [
173 'id' => [
174 'type' => 'integer',
175 ],
176 'date_change' => [
177 'type' => 'date',
178 'format' => 'date_time_no_millis',
179 ],
180 'date_change_ts' => [
181 'type' => 'integer',
182 ],
183 'module_id' => [
184 'type' => 'keyword',
185 ],
186 'item_id' => [
187 'type' => 'keyword',
188 ],
189 'custom_rank' => [
190 'type' => 'integer',
191 ],
192 // user_id
193 // entity_type_id
194 // entity_id
195 // url
196 'title' => [
197 'type' => 'text',
198 ],
199 'body' => [
200 'type' => 'text',
201 ],
202 // tags
203 'param1' => [
204 'type' => 'keyword',
205 ],
206 'param2' => [
207 'type' => 'keyword',
208 ],
209 // upd
210 'date_from' => [
211 'type' => 'date',
212 'format' => 'date_time_no_millis',
213 ],
214 'date_from_ts' => [
215 'type' => 'integer',
216 ],
217 'date_to' => [
218 'type' => 'date',
219 'format' => 'date_time_no_millis',
220 ],
221 'date_to_ts' => [
222 'type' => 'integer',
223 ],
224 'tag' => [
225 'type' => 'keyword', // array
226 ],
227 'xright' => [ // Can not use right here because sql plugin makes it uppercase
228 'type' => 'keyword', // array
229 ],
230 'param' => [
231 'properties' => [],
232 'dynamic' => true,
233 ],
234 ]
235 ]
236 ];
237
238 if ($analyzer)
239 {
240 $template['mappings']['properties']['body']['analyzer'] = $analyzer;
241 }
242
243 $result = $this->query('PUT', '/_index_template/' . $templateName, [
244 'index_patterns' => [
245 $indexName . '-' . $siteId,
246 ],
247 'version' => $this->version,
248 'template' => $template
249 ]);
250
251 if ($adminNotify)
252 {
253 $error = [
254 'MESSAGE' => GetMessage("SEARCH_OPENSEARCH_REINDEX", ['#LINK#' => '/bitrix/admin/search_reindex.php?lang=' . LANGUAGE_ID]),
255 'TAG' => 'SEARCH_REINDEX',
256 'MODULE_ID' => 'SEARCH',
257 'NOTIFY_TYPE' => CAdminNotify::TYPE_ERROR,
258 ];
260 }
261
262 return $result;
263 }
264
265 return true;
266 }
267
268 static $siteIdChecked = [];
269 protected function checkTemplateBySiteId($siteId)
270 {
271 global $CACHE_MANAGER;
272
273 if (isset(static::$siteIdChecked[$siteId]))
274 {
275 return;
276 }
277 static::$siteIdChecked[$siteId] = true;
278
279 $cacheId = 'opensearch-template-' . $siteId;
280 if ($CACHE_MANAGER->Read(CACHED_opensearch_template, $cacheId, 'opensearch'))
281 {
282 return;
283 }
284
285 if (isset($this->siteAnalyzerMap[$siteId]))
286 {
287 $this->checkIndexTemplate($this->indexName, $siteId, $this->siteAnalyzerMap[$siteId], false);
288 }
289
290 $CACHE_MANAGER->Set($cacheId, true);
291 }
292
293 public function truncate()
294 {
295 $this->query('DELETE', '/' . $this->indexName . '-*?expand_wildcards=all');
296 }
297
298 public function deleteById($ID)
299 {
300 foreach ($this->siteAnalyzerMap as $siteId => $_)
301 {
302 $this->query('DELETE', '/' . $this->indexName . '-' . $siteId . '/_doc/' . intval($ID));
303 }
304 }
305
306 public function replace($ID, $arFields)
307 {
308 $DB = CDatabase::GetModuleConnection('search');
309
310 if (array_key_exists('~DATE_CHANGE', $arFields))
311 {
312 $arFields['DATE_CHANGE'] = $arFields['~DATE_CHANGE'];
313 unset($arFields['~DATE_CHANGE']);
314 }
315 elseif (array_key_exists('LAST_MODIFIED', $arFields))
316 {
317 $arFields['DATE_CHANGE'] = $arFields['LAST_MODIFIED'];
318 unset($arFields['LAST_MODIFIED']);
319 }
320 elseif (array_key_exists('DATE_CHANGE', $arFields))
321 {
322 $arFields['DATE_CHANGE'] = $DB->FormatDate($arFields['DATE_CHANGE'], 'DD.MM.YYYY HH:MI:SS', CLang::GetDateFormat());
323 }
324
325 $DATE_FROM = intval(MakeTimeStamp($arFields['DATE_FROM']));
326 $DATE_TO = intval(MakeTimeStamp($arFields['DATE_TO']));
327 $DATE_CHANGE = intval(MakeTimeStamp($arFields['DATE_CHANGE']));
328
329 $BODY = ($arFields['TITLE'] ? CSearch::KillEntities($arFields['TITLE']) . "\n" : '')
331 . ($arFields['TAGS'] ? "\n" . $arFields['TAGS'] : '')
332 ;
333
334 $sites = $this->sites($arFields['SITE_ID']);
335 foreach ($this->siteAnalyzerMap as $siteId => $_)
336 {
337 if (in_array($siteId, $sites))
338 {
339 $doc = [
340 'id' => $ID,
341 'date_change' => $DATE_CHANGE ? date('c', $DATE_CHANGE) : null,
342 'date_change_ts' => $DATE_CHANGE ? $DATE_CHANGE - CTimeZone::GetOffset() : 0,
343 'module_id' => $arFields['MODULE_ID'],
344 'item_id' => $arFields['ITEM_ID'],
345 'custom_rank' => intval($arFields['CUSTOM_RANK']),
346 'title' => $arFields['TITLE'],
347 'body' => $BODY,
348 'param1' => $arFields['PARAM1'],
349 'param2' => $arFields['PARAM2'],
350 'date_from' => $DATE_FROM ? date('c', $DATE_FROM) : null,
351 'date_from_ts' => $DATE_FROM ? $DATE_FROM - CTimeZone::GetOffset() : 0,
352 'date_to' => $DATE_TO ? date('c', $DATE_TO) : null,
353 'date_to_ts' => $DATE_TO ? $DATE_TO - CTimeZone::GetOffset() : 0,
354 'tag' => $this->tags($arFields['SITE_ID'], $arFields['TAGS']),
355 'xright' => $this->rights($arFields['PERMISSIONS']),
356 'param' => $this->params($arFields['PARAMS']),
357 ];
359 $result = $this->query('PUT', '/' . $this->indexName . '-' . $siteId . '/_doc/' . intval($ID), $doc);
360 if (!$result)
361 {
362 throw new \Bitrix\Main\Db\SqlQueryException('OpenSearch index error', $this->getError(), '');
363 }
364 }
365 else
366 {
367 $result = $this->query('DELETE', '/' . $this->indexName . '-' . $siteId . '/_doc/' . intval($ID));
368 }
369 }
370 }
371
372 public function update($ID, $arFields)
373 {
374 $DB = CDatabase::GetModuleConnection('search');
375 $ID = intval($ID);
376
377 $arUpdate = [];
378 $bReplace = array_key_exists('TITLE', $arFields)
379 || array_key_exists('BODY', $arFields)
380 || array_key_exists('MODULE_ID', $arFields)
381 || array_key_exists('ITEM_ID', $arFields)
382 || array_key_exists('PARAM1', $arFields)
383 || array_key_exists('PARAM2', $arFields)
384 ;
385
386 if (array_key_exists('~DATE_CHANGE', $arFields))
387 {
388 $arFields['DATE_CHANGE'] = $arFields['~DATE_CHANGE'];
389 unset($arFields['~DATE_CHANGE']);
390 }
391 elseif (array_key_exists('LAST_MODIFIED', $arFields))
392 {
393 $arFields['DATE_CHANGE'] = $arFields['LAST_MODIFIED'];
394 unset($arFields['LAST_MODIFIED']);
395 }
396 elseif (array_key_exists('DATE_CHANGE', $arFields))
397 {
398 $arFields['DATE_CHANGE'] = $DB->FormatDate($arFields['DATE_CHANGE'], 'DD.MM.YYYY HH:MI:SS', CLang::GetDateFormat());
399 }
400
401 if (array_key_exists('DATE_CHANGE', $arFields))
402 {
403 $DATE_CHANGE = intval(MakeTimeStamp($arFields['DATE_CHANGE']));
404 $arUpdate['date_change'] = $DATE_CHANGE > 0 ? date('c', $DATE_CHANGE - CTimeZone::GetOffset()) : null;
405 $arUpdate['date_change_ts'] = $DATE_CHANGE > 0 ? $DATE_CHANGE : 0;
406 }
407
408 if (array_key_exists('DATE_FROM', $arFields))
409 {
410 $DATE_FROM = intval(MakeTimeStamp($arFields['DATE_FROM']));
411 $arUpdate['date_from'] = $DATE_FROM > 0 ? date('c', $DATE_FROM - CTimeZone::GetOffset()) : null;
412 $arUpdate['date_from_ts'] = $DATE_FROM > 0 ? $DATE_FROM : 0;
413 }
414
415 if (array_key_exists('DATE_TO', $arFields))
416 {
417 $DATE_TO = intval(MakeTimeStamp($arFields['DATE_TO']));
418 $arUpdate['date_to'] = $DATE_TO > 0 ? date('c', $DATE_TO - CTimeZone::GetOffset()) : null;
419 $arUpdate['date_to_ts'] = $DATE_TO > 0 ? $DATE_TO : 0;
420 }
421
422 if (array_key_exists('CUSTOM_RANK', $arFields))
423 {
424 $arUpdate['custom_rank'] = $arFields['CUSTOM_RANK'] > 0 ? intval($arFields['CUSTOM_RANK']) : 0;
425 }
426
427 if (array_key_exists('TAGS', $arFields))
428 {
429 $arUpdate['tag'] = $this->tags($arFields['SITE_ID'], $arFields['TAGS']);
430 }
431
432 if (array_key_exists('PERMISSIONS', $arFields))
433 {
434 $arUpdate['xright'] = $this->rights($arFields['PERMISSIONS']);
435 }
436
437 if (array_key_exists('PARAMS', $arFields))
438 {
439 $arUpdate['param'] = $this->params($arFields['PARAMS']);
440 }
441
442 if (array_key_exists('SITE_ID', $arFields))
443 {
444 $sites = $this->sites($arFields['SITE_ID']);
445 }
446 else
447 {
448 $sites = [];
449 $dbSites = $DB->Query('SELECT * from b_search_content_site WHERE SEARCH_CONTENT_ID=' . $ID);
450 while ($site = $dbSites->fetch())
451 {
452 $sites[$site['SITE_ID']] = $site['SITE_ID'];
453 }
454 }
455
456 if (!empty($arUpdate) && !$bReplace)
457 {
458 foreach ($this->siteAnalyzerMap as $siteId => $_)
459 {
460 if (in_array($siteId, $sites))
461 {
462 $result = $this->query('POST', '/' . $this->indexName . '-' . $siteId . '/_update/' . intval($ID), [
463 'doc' => $arUpdate,
464 ]);
465 }
466 else
467 {
468 $result = $this->query('DELETE', '/' . $this->indexName . '-' . $siteId . '/_doc/' . intval($ID));
469 }
470 }
471 }
472 elseif ($bReplace)
473 {
474 $dbItem = $DB->Query('SELECT * FROM b_search_content WHERE ID = ' . $ID);
475 $searchItem = $dbItem->fetch();
476 if ($searchItem)
477 {
478 $arTags = [];
479 $dbTags = $DB->Query('SELECT * from b_search_tags WHERE SEARCH_CONTENT_ID=' . $ID);
480 while ($tag = $dbTags->fetch())
481 {
482 $arTags[] = $tag['NAME'];
483 }
484 $searchItem['TAGS'] = $arTags ? implode(',', $arTags) : null;
485
486 $searchItem['PERMISSIONS'] = [];
487 $dbRights = $DB->Query('SELECT * from b_search_content_right WHERE SEARCH_CONTENT_ID=' . $ID);
488 while ($right = $dbRights->fetch())
489 {
490 $searchItem['PERMISSIONS'][] = $right['GROUP_CODE'];
491 }
492
493 $searchItem['SITE_ID'] = [];
494 $dbSites = $DB->Query('SELECT * from b_search_content_site WHERE SEARCH_CONTENT_ID=' . $ID);
495 while ($site = $dbSites->fetch())
496 {
497 $searchItem['SITE_ID'][$site['SITE_ID']] = $site['URL'];
498 }
499
500 $searchItem['PARAMS'] = [];
501 $dbParams = $DB->Query('SELECT * from b_search_content_param WHERE SEARCH_CONTENT_ID=' . $ID);
502 while ($param = $dbParams->fetch())
503 {
504 $searchItem['PARAMS'][$param['PARAM_NAME']][] = $param['PARAM_VALUE'];
505 }
506
507 $this->replace($ID, $searchItem);
508 }
509 }
510 }
511
512 function tags($arLID, $sContent)
513 {
514 $tags = [];
515 if (is_array($arLID))
516 {
517 foreach ($arLID as $site_id => $url)
518 {
519 $arTags = tags_prepare($sContent, $site_id);
520 foreach ($arTags as $tag)
521 {
522 $tags[$tag] = $tag;
523 }
524 }
525 }
526
527 return array_values($tags);
528 }
529
530 function rights($arRights)
531 {
532 $rights = [];
533 if (is_array($arRights))
534 {
535 foreach ($arRights as $group_id)
536 {
537 if (is_numeric($group_id))
538 {
539 $rights[$group_id] = 'G' . intval($group_id);
540 }
541 else
542 {
543 $rights[$group_id] = $group_id;
544 }
545 }
546 }
547
548 return array_values($rights);
549 }
550
551 function sites($arSites)
552 {
553 $sites = [];
554 if (is_array($arSites))
555 {
556 foreach ($arSites as $site_id => $url)
557 {
559 }
560 }
561 else
562 {
564 }
565
566 return array_values($sites);
567 }
568
570 {
571 $params = [];
572 if (is_array($arParams))
573 {
574 foreach ($arParams as $k1 => $v1)
575 {
576 $name = trim($k1);
577 if ($name != '')
578 {
579 if (!is_array($v1))
580 {
581 $v1 = [$v1];
582 }
583
584 foreach ($v1 as $v2)
585 {
586 $value = trim($v2);
587 if ($value != '')
588 {
589 $params[$name] = $value;
590 }
591 }
592 }
593 }
594 }
595
596 return $params;
597 }
598
599 public function getErrorText()
600 {
601 return $this->errorText;
602 }
603
604 public function getErrorNumber()
605 {
606 return $this->errorNumber;
607 }
608
609 public function search($arParams, $aSort, $aParamsEx, $bTagsCloud)
610 {
611 $DB = CDatabase::GetModuleConnection('search');
612
613 $result = [];
614 $this->errorText = '';
615 $this->errorNumber = 0;
616
617 $this->tags = trim($arParams['TAGS']);
618
619 $limit = 0;
620 if (is_array($aParamsEx) && isset($aParamsEx['LIMIT']))
621 {
622 $limit = intval($aParamsEx['LIMIT']);
623 unset($aParamsEx['LIMIT']);
624 }
625 if ($limit <= 0)
626 {
627 $limit = intval(COption::GetOptionInt('search', 'max_result_size'));
628 }
629 if ($limit <= 0)
630 {
631 $limit = 500;
632 }
633
634 $offset = 0;
635 if (is_array($aParamsEx) && isset($aParamsEx['OFFSET']))
636 {
637 $offset = intval($aParamsEx['OFFSET']);
638 unset($aParamsEx['OFFSET']);
639 }
640
641 if (is_array($aParamsEx) && !empty($aParamsEx))
642 {
643 $aParamsEx['LOGIC'] = 'OR';
644 $arParams[] = $aParamsEx;
645 }
646
647 $this->SITE_ID = $arParams['SITE_ID'];
648 unset($arParams['SITE_ID']);
649
650 $query = [
651 '_source' => false,
652 'query' => [
653 'bool' => [
654 'must' => [
655 ],
656 ],
657 ],
658 'from' => $offset,
659 'size' => $limit,
660 ];
661
662 $strQuery = trim($arParams['QUERY']);
663 if ($strQuery != '')
664 {
665 $query['query']['bool']['must'][] = [
666 'match' => [
667 'body' => [
668 'query' => $strQuery,
669 ],
670 ],
671 ];
672 }
673
674 $rights = $this->CheckPermissions();
675 if ($rights)
676 {
677 $query['query']['bool']['filter'] = [
678 'bool' => [
679 'must' => [
680 [
681 'terms' => [
682 'xright' => $rights,
683 ],
684 ],
685 ],
686 ],
687 ];
688 }
689
690 $arWhere = $this->prepareFilter($arParams);
691 if ($arWhere)
692 {
693 $query['query']['bool']['must'][] = $arWhere;
694 }
695
696 if ($strQuery || $this->tags || $bTagsCloud)
697 {
698 if ($bTagsCloud)
699 {
700 $query['size'] = 0; // we don't need hits
701 $query['aggregations'] = [
702 'tags' => [
703 'terms' => [
704 'field' => 'tag',
705 'size' => $limit,
706 ],
707 'aggregations' => [
708 'max_dc' => [
709 'max' => [
710 'field' => 'date_change_ts',
711 ],
712 ],
713 ],
714 ],
715 ];
716
717 $r = $this->query('GET', '/' . $this->indexName . '-' . $this->SITE_ID . '/_search', $query);
718 if (!$r)
719 {
720 throw new \Bitrix\Main\Db\SqlQueryException('OpenSearch select error', $this->getError(), $sql);
721 }
722 else
723 {
724 if (
725 is_array($r)
726 && isset($r['aggregations'])
727 && is_array($r['aggregations'])
728 && isset($r['aggregations']['tags'])
729 && is_array($r['aggregations']['tags'])
730 )
731 {
732 foreach ($r['aggregations']['tags']['buckets'] as $searcBucket)
733 {
734 $result[] = [
735 'NAME' => $searcBucket['key'],
736 'CNT' => $searcBucket['doc_count'],
737 'FULL_DATE_CHANGE' => ConvertTimeStamp($searcBucket['max_dc']['value'] + CTimeZone::GetOffset(), 'FULL'),
738 'DATE_CHANGE' => ConvertTimeStamp($searcBucket['max_dc']['value'] + CTimeZone::GetOffset(), 'SHORT'),
739 ];
740 }
741 }
742 }
743 }
744 else
745 {
746 $order = $this->__PrepareSort($order);
747 if ($order)
748 {
749 $query['sort'] = $order;
750 }
751
752 $query['fields'] = ['id', 'module_id', 'param2'];
753
754 $r = $this->query('GET', '/' . $this->indexName . '-' . $this->SITE_ID . '/_search', $query);
755 if (!$r)
756 {
757 throw new \Bitrix\Main\Db\SqlQueryException('OpenSearch select error', $this->getError(), json_encode($query));
758 }
759 else
760 {
761 $this->arForumTopics = [];
762 if (
763 is_array($r)
764 && isset($r['hits'])
765 && is_array($r['hits'])
766 && isset($r['hits']['hits'])
767 && is_array($r['hits']['hits'])
768 )
769 {
770 foreach ($r['hits']['hits'] as $searchHit)
771 {
772 if ($searchHit['fields']['module_id'][0] == 'FORUM')
773 {
774 if (array_key_exists($searchHit['fields']['param2'][0], $this->arForumTopics))
775 {
776 continue;
777 }
778 $this->arForumTopics[$searchHit['fields']['param2'][0]] = true;
779 }
780 $result[] = [
781 'ID' => $searchHit['fields']['id'][0],
782 ];
783 }
784 }
785 }
786 }
787 }
788 else
789 {
790 $this->errorText = GetMessage('SEARCH_ERROR3');
791 $this->errorNumber = 3;
792 }
793
794 return $result;
795 }
796
797 function searchTitle($phrase = '', $arPhrase = [], $nTopCount = 5, $arParams = [], $bNotFilter = false, $order = '')
798 {
799 $query = [
800 '_source' => false,
801 'fields' => ['id'],
802 'query' => [
803 'bool' => [
804 'must' => [
805 [
806 'match_phrase_prefix' => [
807 'title' => $phrase,
808 ],
809 ],
810 ],
811 ],
812 ],
813 'size' => $nTopCount,
814 ];
815
817 if (is_array($arParams) && isset($arParams['SITE_ID']))
818 {
819 $site_id = $arParams['SITE_ID'];
820 unset($arParams['SITE_ID']);
821 }
822
823 $arWhere = $this->prepareFilter($arParams);
824 if ($arWhere)
825 {
826 $query['query']['bool']['must'][] = $arWhere;
827 }
828
829 $rights = $this->CheckPermissions();
830 if ($rights)
831 {
832 $query['query']['bool']['filter'] = [
833 'bool' => [
834 'must' => [
835 [
836 'terms' => [
837 'xright' => $rights,
838 ],
839 ],
840 ],
841 ],
842 ];
843 }
844
845 $order = $this->__PrepareSort($order);
846 if ($order)
847 {
848 $query['sort'] = $order;
849 }
850
851 $r = $this->query('GET', '/' . $this->indexName . '-' . $site_id . '/_search', $query);
852 if (!$r)
853 {
854 throw new \Bitrix\Main\Db\SqlQueryException('OpenSearch query error', $this->getError(), json_encode($query));
855 }
856 else
857 {
858 $result = [];
859 if (
860 is_array($r)
861 && isset($r['hits'])
862 && is_array($r['hits'])
863 && isset($r['hits']['hits'])
864 && is_array($r['hits']['hits'])
865 )
866 {
867 foreach ($r['hits']['hits'] as $searchHit)
868 {
869 $result[] = $searchHit['fields']['id'][0];
870 }
871 }
872
873 return $result;
874 }
875 }
876
878 {
879 return new CSearchOpenSearchFormatter($this);
880 }
881
882 function filterField($field, $value, $logic = 'should')
883 {
884 $DB = CDatabase::GetModuleConnection('search');
885 $arWhere = [];
886
887 if (is_array($value))
888 {
889 if (!empty($value))
890 {
891 $arWhere = [
892 'bool' => [
893 $logic => [
894 ]
895 ]
896 ];
897 foreach ($value as $i => $v)
898 {
899 $arWhere['bool'][$logic][] = [
900 'term' => [
901 $field => [
902 'value' => $v
903 ]
904 ]
905 ];
906 }
907 }
908 }
909 else
910 {
911 if ($value !== false)
912 {
913 $arWhere = [
914 'bool' => [
915 $logic => [
916 'term' => [
917 $field => [
918 'value' => $value
919 ]
920 ]
921 ]
922 ]
923 ];
924 }
925 }
926
927 return $arWhere;
928 }
929
931 {
932 $DB = CDatabase::GetModuleConnection('search');
933
934 if (!is_array($arFilter))
935 {
936 $arFilter = [];
937 }
938
939 if (array_key_exists('LOGIC', $arFilter) && $arFilter['LOGIC'] == 'OR')
940 {
941 $logic = 'should';
942 unset($arFilter['LOGIC']);
943 }
944 else
945 {
946 $logic = 'must';
947 }
948
949 $arWhere = [
950 'bool' => [
951 $logic => [
952 ]
953 ]
954 ];
955
956 foreach ($arFilter as $field => $val)
957 {
958 $field = mb_strtoupper($field);
959 if (
960 is_array($val)
961 && count($val) == 1
962 && $field !== 'URL'
963 && $field !== 'PARAMS'
964 && !is_numeric($field)
965 )
966 {
967 $val = $val[0];
968 }
969
970 switch ($field)
971 {
972 case 'ITEM_ID':
973 case '=ITEM_ID':
974 $cond = $this->filterField('item_id', $val);
975 if ($cond)
976 {
977 array_push($arWhere['bool'][$logic], $cond);
978 }
979 break;
980 case '!ITEM_ID':
981 if (
982 $val !== false
983 && !is_array($val)
984 )
985 {
986 array_push($arWhere['bool'][$logic], $this->filterField('item_id', $val, 'must_not'));
987 }
988 break;
989 case 'MODULE_ID':
990 case '=MODULE_ID':
991 if ($val !== false && $val !== 'no')
992 {
993 $cond = $this->filterField('module_id', $val);
994 if ($cond)
995 {
996 array_push($arWhere['bool'][$logic], $cond);
997 }
998 }
999 break;
1000 case '!MODULE_ID':
1001 case '!=MODULE_ID':
1002 if (
1003 $val !== false
1004 && !is_array($val)
1005 )
1006 {
1007 array_push($arWhere['bool'][$logic], $this->filterField('module_id', $val, 'must_not'));
1008 }
1009 break;
1010 case 'PARAM1':
1011 case '=PARAM1':
1012 $cond = $this->filterField('param1', $val);
1013 if ($cond)
1014 {
1015 array_push($arWhere['bool'][$logic], $cond);
1016 }
1017 break;
1018 case '!PARAM1':
1019 case '!=PARAM1':
1020 if (
1021 $val !== false
1022 && !is_array($val)
1023 )
1024 {
1025 array_push($arWhere['bool'][$logic], $this->filterField('param1', $val, 'must_not'));
1026 }
1027 break;
1028 case 'PARAM2':
1029 case '=PARAM2':
1030 $cond = $this->filterField('param2', $val);
1031 if ($cond)
1032 {
1033 array_push($arWhere['bool'][$logic], $cond);
1034 }
1035 break;
1036 case '!PARAM2':
1037 case '!=PARAM2':
1038 if (
1039 $val !== false
1040 && !is_array($val)
1041 )
1042 {
1043 array_push($arWhere['bool'][$logic], $this->filterField('param2', $val, 'must_not'));
1044 }
1045 break;
1046 case 'DATE_CHANGE':
1047 case '>=DATE_CHANGE':
1048 if ($val <> '')
1049 {
1050 $ts = MakeTimeStamp($val) - CTimeZone::GetOffset();
1051 $arWhere['bool'][$logic][] = [
1052 'range' => [
1053 'date_change_ts' => [
1054 'from' => $ts,
1055 'to' => null,
1056 'include_lower' => true,
1057 'include_upper' => true,
1058 ]
1059 ]
1060 ];
1061 }
1062 break;
1063 case '<=DATE_CHANGE':
1064 if ($val <> '')
1065 {
1066 $ts = MakeTimeStamp($val) - CTimeZone::GetOffset();
1067 $arWhere['bool'][$logic][] = [
1068 'range' => [
1069 'date_change_ts' => [
1070 'from' => null,
1071 'to' => $ts,
1072 'include_lower' => true,
1073 'include_upper' => true,
1074 ]
1075 ]
1076 ];
1077 }
1078 break;
1079 case 'CHECK_DATES':
1080 if ($val == 'Y')
1081 {
1082 $ts = time() - CTimeZone::GetOffset();
1083 $arWhere['bool'][$logic][] = [
1084 'bool' => [
1085 'must' => [
1086 [
1087 'bool' => [
1088 'should' => [
1089 [
1090 'term' => [
1091 'date_from_ts' => [
1092 'value' => 0,
1093 ]
1094 ]
1095 ],
1096 [
1097 'range' => [
1098 'date_from_ts' => [
1099 'from' => null,
1100 'to' => $ts,
1101 'include_lower' => true,
1102 'include_upper' => true,
1103 ]
1104 ]
1105 ],
1106 ]
1107 ]
1108 ],
1109 [
1110 'bool' => [
1111 'should' => [
1112 [
1113 'term' => [
1114 'date_to_ts' => [
1115 'value' => 0,
1116 ]
1117 ]
1118 ],
1119 [
1120 'range' => [
1121 'date_to_ts' => [
1122 'from' => $ts,
1123 'to' => null,
1124 'include_lower' => true,
1125 'include_upper' => true,
1126 ]
1127 ]
1128 ],
1129 ]
1130 ]
1131 ],
1132 ]
1133 ]
1134 ];
1135 }
1136 break;
1137 case 'TAGS':
1138 $arTags = explode(',', $val);
1139 foreach ($arTags as $i => &$strTag)
1140 {
1141 $strTag = trim($strTag, " \n\r\t\"");
1142 if ($strTag == '')
1143 {
1144 unset($arTags[$i]);
1145 }
1146 }
1147 unset($strTag);
1148
1149 $cond = $this->filterField('tag', $arTags, 'must');
1150 if ($cond)
1151 {
1152 array_push($arWhere['bool'][$logic], $cond);
1153 }
1154 break;
1155 case 'PARAMS':
1156 if (is_array($val))
1157 {
1158 $params = [];
1159 foreach ($this->params($val) as $key => $value)
1160 {
1161 $params[] = [
1162 'term' => [
1163 'param.' . $key => [
1164 'value' => $value,
1165 ]
1166 ]
1167 ];
1168 }
1169 if ($params)
1170 {
1171 array_push($arWhere['bool'][$logic], [
1172 'bool' => [
1173 'must' => $params
1174 ]
1175 ]);
1176 }
1177 }
1178 break;
1179 case 'URL': //TODO
1180 case 'QUERY':
1181 case 'LIMIT':
1182 case 'USE_TF_FILTER':
1183 break;
1184 default:
1185 if (is_numeric($field) && is_array($val))
1186 {
1187 $subFilter = $this->prepareFilter($val);
1188 if ($subFilter)
1189 {
1190 $arWhere['bool'][$logic][] = $subFilter;
1191 }
1192 }
1193 else
1194 {
1195 //AddMessage2Log("field: $field; val: ".print_r($val, 1));
1196 }
1197 break;
1198 }
1199 }
1200
1201 if (!$arWhere['bool'][$logic])
1202 {
1203 return [];
1204 }
1205
1206 return $arWhere;
1207 }
1208
1210 {
1211 global $USER;
1212 $DB = CDatabase::GetModuleConnection('search');
1213
1214 $arResult = [];
1215
1216 if (!$USER->IsAdmin())
1217 {
1218 if ($USER->GetID() > 0)
1219 {
1221 $rs = $DB->Query('SELECT GROUP_CODE FROM b_search_user_right WHERE USER_ID = ' . $USER->GetID());
1222 while ($ar = $rs->Fetch())
1223 {
1224 $arResult[] = $ar['GROUP_CODE'];
1225 }
1226 }
1227 else
1228 {
1229 $arResult[] = 'G2';
1230 }
1231 }
1232
1233 return $this->rights($arResult);
1234 }
1235
1236 function __PrepareSort($aSort = [])
1237 {
1238 $arOrder = [];
1239 if (!is_array($aSort))
1240 {
1241 $aSort = [$aSort => 'ASC'];
1242 }
1243
1244 $this->flagsUseRatingSort = 0;
1245 foreach ($aSort as $key => $ord)
1246 {
1247 $ord = mb_strtoupper($ord) <> 'ASC' ? 'desc' : 'asc';
1248 $key = mb_strtoupper($key);
1249 switch ($key)
1250 {
1251 case 'ID':
1252 case 'DATE_CHANGE':
1253 case 'MODULE_ID':
1254 case 'ITEM_ID':
1255 case 'CUSTOM_RANK':
1256 case 'PARAM1':
1257 case 'PARAM2':
1258 case 'DATE_FROM':
1259 case 'DATE_TO':
1260 $arOrder[mb_strtolower($key)] = ['order' => $ord];
1261 break;
1262 case 'RANK':
1263 $arOrder['_score'] = ['order' => $ord];
1264 break;
1265 }
1266 }
1267
1268 if (count($arOrder) == 0)
1269 {
1270 return $this->__PrepareSort([
1271 'CUSTOM_RANK' => 'DESC',
1272 'RANK' => 'DESC',
1273 'DATE_CHANGE' => 'DESC',
1274 ]);
1275 }
1276
1277 return $arOrder;
1278 }
1279
1280 public function query($verb, $url, $params = [])
1281 {
1282 $this->errorText = '';
1283 $this->errorNumber = 0;
1284
1285 $result = false;
1286
1287 $server = new Bitrix\Main\Web\HttpClient([
1288 'disableSslVerification' => true,
1289 ]);
1290
1291 if ($this->user)
1292 {
1293 $server->setAuthorization($this->user, $this->password);
1294 }
1295
1296 if ($params)
1297 {
1298 $server->setHeader('Content-Type', 'application/json');
1299 }
1300 $server->query($verb, $this->connectionString . $url, $params ? json_encode($params) : false);
1301 if (
1302 $server->getStatus() === 200
1303 || $server->getStatus() === 201
1304 )
1305 {
1306 $result = json_decode($server->getResult(), true);
1307 }
1308 // AddMessage2Log([$verb . ' ' . $url, $server->getStatus(), $result ?: $server->getResult()]);
1309 if (!is_array($result))
1310 {
1311 if ($server->getStatus())
1312 {
1313 $this->errorText = $server->getResult();
1314 $this->errorNumber = $server->getStatus();
1315 }
1316 else
1317 {
1318 $errors = $server->getError();
1319 $this->errorText = $errors ? implode(' ', $errors) : '-1';
1320 }
1321 }
1322 elseif (isset($result['error']))
1323 {
1324 $this->errorText = $result['error'];
1325 $this->errorNumber = $result['status'];
1326 }
1327
1328 return $result;
1329 }
1330
1331 public function getError()
1332 {
1333 if ($this->errorText)
1334 {
1335 $result = '[' . $this->errorNumber . '] ' . $this->errorText;
1336 }
1337 else
1338 {
1339 $result = '';
1340 }
1341
1342 return $result;
1343 }
1344}
1345
1347{
1349 private $search = null;
1350
1351 function __construct($search)
1352 {
1353 $this->search = $search;
1354 }
1355
1356 function format($r)
1357 {
1358 if ($r)
1359 {
1360 if (array_key_exists('CNT', $r))
1361 {
1362 return $r;
1363 }
1364 elseif (array_key_exists('ID', $r))
1365 {
1366 return $this->formatRow($r);
1367 }
1368 }
1369 }
1370
1371 function formatRow($r)
1372 {
1373 $DB = CDatabase::GetModuleConnection('search');
1374 $ID = intval($r['ID']);
1375
1376 if ($this->search->SITE_ID)
1377 {
1378 $rs = $DB->Query('
1379 select
1380 sc.ID
1381 ,sc.MODULE_ID
1382 ,sc.ITEM_ID
1383 ,sc.TITLE
1384 ,sc.TAGS
1385 ,sc.BODY
1386 ,sc.PARAM1
1387 ,sc.PARAM2
1388 ,sc.UPD
1389 ,sc.DATE_FROM
1390 ,sc.DATE_TO
1391 ,sc.URL
1392 ,sc.CUSTOM_RANK
1393 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE') . ' as FULL_DATE_CHANGE
1394 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE', 'SHORT') . ' as DATE_CHANGE
1395 ,scsite.SITE_ID
1396 ,scsite.URL SITE_URL
1397 ,sc.USER_ID
1398 from b_search_content sc
1399 INNER JOIN b_search_content_site scsite ON sc.ID=scsite.SEARCH_CONTENT_ID
1400 where ID = ' . $ID . "
1401 and scsite.SITE_ID = '" . $DB->ForSql($this->search->SITE_ID) . "'
1402 ");
1403 }
1404 else
1405 {
1406 $rs = $DB->Query('
1407 select
1408 sc.ID
1409 ,sc.MODULE_ID
1410 ,sc.ITEM_ID
1411 ,sc.TITLE
1412 ,sc.TAGS
1413 ,sc.BODY
1414 ,sc.PARAM1
1415 ,sc.PARAM2
1416 ,sc.UPD
1417 ,sc.DATE_FROM
1418 ,sc.DATE_TO
1419 ,sc.URL
1420 ,sc.CUSTOM_RANK
1421 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE') . ' as FULL_DATE_CHANGE
1422 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE', 'SHORT') . ' as DATE_CHANGE
1423 ,\'\' as SITE_ID
1424 from b_search_content sc
1425 where ID = ' . $ID . '
1426 ');
1427 }
1428
1429 $r = $rs->Fetch();
1430
1431 return $r;
1432 }
1433}
$arParams
Определения access_dialog.php:21
$dbSites
Определения options.php:14
$arSites
Определения options.php:15
global $APPLICATION
Определения include.php:80
xml version
Определения yandex.php:67
$arResult
Определения generate_coupon.php:16
const TYPE_ERROR
Определения admin_notify.php:8
static Add($arFields)
Определения admin_notify.php:22
static KillEntities($str)
Определения search.php:1663
Определения full_text.php:105
Определения full_text.php:4
__construct($search)
Определения opensearch.php:1351
Определения opensearch.php:5
params($arParams)
Определения opensearch.php:569
checkTemplateBySiteId($siteId)
Определения opensearch.php:269
searchTitle($phrase='', $arPhrase=[], $nTopCount=5, $arParams=[], $bNotFilter=false, $order='')
Определения opensearch.php:797
$version
Определения opensearch.php:133
getErrorNumber()
Определения opensearch.php:604
update($ID, $arFields)
Определения opensearch.php:372
getError()
Определения opensearch.php:1331
rights($arRights)
Определения opensearch.php:530
tags($arLID, $sContent)
Определения opensearch.php:512
prepareFilter($arFilter)
Определения opensearch.php:930
getErrorText()
Определения opensearch.php:599
$connectionString
Определения opensearch.php:11
$tags
Определения opensearch.php:9
filterField($field, $value, $logic='should')
Определения opensearch.php:882
$user
Определения opensearch.php:12
static getLanguageAnalyzers()
Определения opensearch.php:93
$password
Определения opensearch.php:13
$SITE_ID
Определения opensearch.php:10
getRowFormatter()
Определения opensearch.php:877
connect($connectionString, $user='', $password='', $indexName='', $ignoreErrors=false, $siteAnalyzerMap='')
Определения opensearch.php:17
checkIndexTemplate($indexName, $siteId, $analyzer, $adminNotify=true)
Определения opensearch.php:134
sites($arSites)
Определения opensearch.php:551
$arForumTopics
Определения opensearch.php:6
__PrepareSort($aSort=[])
Определения opensearch.php:1236
$indexName
Определения opensearch.php:14
$siteAnalyzerMap
Определения opensearch.php:15
static $siteIdChecked
Определения opensearch.php:268
deleteById($ID)
Определения opensearch.php:298
truncate()
Определения opensearch.php:293
replace($ID, $arFields)
Определения opensearch.php:306
query($verb, $url, $params=[])
Определения opensearch.php:1280
CheckPermissions()
Определения opensearch.php:1209
search($arParams, $aSort, $aParamsEx, $bTagsCloud)
Определения opensearch.php:609
static CheckCurrentUserGroups()
Определения user.php:27
$sites
Определения clear_component_cache.php:15
global $CACHE_MANAGER
Определения clear_component_cache.php:7
$right
Определения options.php:8
$arFields
Определения dblapprove.php:5
$template
Определения file_edit.php:49
$result
Определения get_property_values.php:14
$arPhrase
Определения get_search.php:37
$query
Определения get_search.php:11
if($ajaxMode) $ID
Определения get_user.php:27
$errors
Определения iblock_catalog_edit.php:74
global $DB
Определения cron_frame.php:29
global $USER
Определения csv_new_run.php:40
$siteId
Определения ajax.php:8
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
GetMessage($name, $aReplace=null)
Определения tools.php:3397
MakeTimeStamp($datetime, $format=false)
Определения tools.php:538
$name
Определения menu_edit.php:35
$order
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$ar
Определения options.php:199
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$val
Определения options.php:1793
$langs
Определения options.php:141
tags_prepare($sText, $site_id=false)
Определения tags.php:4
$site_id
Определения sonet_set_content_view.php:9
const SITE_ID
Определения sonet_set_content_view.php:12
$error
Определения subscription_card_product.php:20
$rs
Определения action.php:82
$arFilter
Определения user_search.php:106
$rights
Определения options.php:4
$url
Определения iframe.php:7
$site
Определения yandex_run.php:614