1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
sphinx.php
См. документацию.
1<?php
3
5{
6 public $arForumTopics = [];
7 public $db = false;
8 private static $fields = [
9 'title' => 'field',
10 'body' => 'field',
11 'module_id' => 'uint',
12 'module' => 'string',
13 'item_id' => 'uint',
14 'item' => 'string',
15 'param1_id' => 'uint',
16 'param1' => 'string',
17 'param2_id' => 'uint',
18 'param2' => 'string',
19 'date_change' => 'timestamp',
20 'date_from' => 'timestamp',
21 'date_to' => 'timestamp',
22 'custom_rank' => 'uint',
23 'tags' => 'mva',
24 'right' => 'mva',
25 'site' => 'mva',
26 'param' => 'mva',
27 ];
28 private static $typesMap = [
29 'timestamp' => 'rt_attr_timestamp',
30 'string' => 'rt_attr_string',
31 'bigint' => 'rt_attr_bigint',
32 'uint' => 'rt_attr_uint',
33 'field' => 'rt_field',
34 'mva' => 'rt_attr_multi',
35 ];
36 private $errorText = '';
37 private $errorNumber = 0;
38 public $tags = '';
39 public $query = '';
40 public $SITE_ID = '';
41 public $connectionIndex = '';
42 public $indexName = '';
43
44 public function connect($connectionIndex, $indexName = '', $ignoreErrors = false)
45 {
46 global $APPLICATION;
47
48 if (!preg_match('/^[a-zA-Z0-9_]+$/', $indexName))
49 {
50 if ($ignoreErrors)
51 {
52 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_ERROR_INDEX_NAME'));
53 }
54 else
55 {
56 throw new \Bitrix\Main\Db\ConnectionException('Sphinx connect error', GetMessage('SEARCH_SPHINX_CONN_ERROR_INDEX_NAME'));
57 }
58 return false;
59 }
60
61 if (!$this->canConnect())
62 {
63 if ($ignoreErrors)
64 {
65 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_EXT_IS_MISSING'));
66 }
67 else
68 {
69 throw new \Bitrix\Main\Db\ConnectionException('Sphinx connect error', GetMessage('SEARCH_SPHINX_CONN_EXT_IS_MISSING'));
70 }
71 return false;
72 }
73
74 $error = '';
75 $this->db = $this->internalConnect($connectionIndex, $error);
76 if (!$this->db)
77 {
78 if ($ignoreErrors)
79 {
80 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_ERROR', ['#ERRSTR#' => $error]));
81 }
82 else
83 {
84 throw new \Bitrix\Main\Db\ConnectionException('Sphinx connect error', GetMessage('SEARCH_SPHINX_CONN_ERROR', ['#ERRSTR#' => $error]));
85 }
86 return false;
87 }
88
89 if ($ignoreErrors)
90 {
91 $result = $this->query('SHOW TABLES');
92 if (!$result)
93 {
94 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_ERROR', ['#ERRSTR#' => $this->getError()]));
95 return false;
96 }
97
98 if ($indexName == '')
99 {
100 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_NO_INDEX'));
101 return false;
102 }
103
104 $indexType = '';
105 while ($res = $this->fetch($result))
106 {
107 if ($indexName === $res['Index'])
108 {
109 $indexType = $res['Type'];
110 }
111 }
112
113 if ($indexType == '')
114 {
115 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_INDEX_NOT_FOUND'));
116 return false;
117 }
118
119 if ($indexType != 'rt')
120 {
121 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_CONN_INDEX_WRONG_TYPE'));
122 return false;
123 }
124
125 $indexColumns = [];
126 $result = $this->query('DESCRIBE `' . $indexName . '`');
127 if (!$result)
128 {
129 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_DESCR_ERROR', ['#ERRSTR#' => $this->getError()]));
130 return false;
131 }
132
133 while ($res = $this->fetch($result))
134 {
135 $indexColumns[$res['Field']] = $res['Type'];
136 }
137
138 $missed = [];
139 foreach (self::$fields as $name => $type)
140 {
141 if (!isset($indexColumns[$name]) || $indexColumns[$name] !== $type)
142 {
143 $missed[] = self::$typesMap[$type] . ' = ' . $name;
144 }
145 }
146
147 if (!empty($missed))
148 {
149 $APPLICATION->ThrowException(GetMessage('SEARCH_SPHINX_NO_FIELDS', ['#FIELD_LIST#' => implode(', ', $missed)]));
150 return false;
151 }
152 }
153
154 $this->indexName = $indexName;
155 $this->connectionIndex = $connectionIndex;
156
157 return true;
158 }
159
160 public function truncate()
161 {
162 $this->query('truncate rtindex ' . $this->indexName);
163 $this->connect($this->connectionIndex, $this->indexName);
164 }
165
166 public function deleteById($ID)
167 {
168 $this->query('delete from ' . $this->indexName . ' where id = ' . intval($ID));
169 }
170
171 public function replace($ID, $arFields)
172 {
173 $DB = CDatabase::GetModuleConnection('search');
174
175 if (array_key_exists('~DATE_CHANGE', $arFields))
176 {
177 $arFields['DATE_CHANGE'] = $arFields['~DATE_CHANGE'];
178 unset($arFields['~DATE_CHANGE']);
179 }
180 elseif (array_key_exists('LAST_MODIFIED', $arFields))
181 {
182 $arFields['DATE_CHANGE'] = $arFields['LAST_MODIFIED'];
183 unset($arFields['LAST_MODIFIED']);
184 }
185 elseif (array_key_exists('DATE_CHANGE', $arFields))
186 {
187 $arFields['DATE_CHANGE'] = $DB->FormatDate($arFields['DATE_CHANGE'], 'DD.MM.YYYY HH:MI:SS', CLang::GetDateFormat());
188 }
189
190 $DATE_FROM = intval(MakeTimeStamp($arFields['DATE_FROM']));
191 if ($DATE_FROM > 0)
192 {
193 $DATE_FROM -= CTimeZone::GetOffset();
194 }
195 $DATE_TO = intval(MakeTimeStamp($arFields['DATE_TO']));
196 if ($DATE_TO > 0)
197 {
198 $DATE_TO -= CTimeZone::GetOffset();
199 }
200 $DATE_CHANGE = intval(MakeTimeStamp($arFields['DATE_CHANGE']));
201 if ($DATE_CHANGE > 0)
202 {
203 $DATE_CHANGE -= CTimeZone::GetOffset();
204 }
205
206 $BODY = CSearch::KillEntities($arFields['BODY']) . "\r\n" . $arFields['TAGS'];
207
208 $sql = '
209 REPLACE INTO ' . $this->indexName . ' (
210 id
211 ,module_id
212 ,module
213 ,item_id
214 ,item
215 ,param1_id
216 ,param1
217 ,param2_id
218 ,param2
219 ,date_change
220 ,date_from
221 ,date_to
222 ,custom_rank
223 ,tags
224 ,right
225 ,site
226 ,param
227 ,title
228 ,body
229 ) VALUES (
230 ' . $ID . '
231 ,' . sprintf('%u', crc32($arFields['MODULE_ID'])) . "
232 ,'" . $this->Escape($arFields['MODULE_ID']) . "'
233 ," . sprintf('%u', crc32($arFields['ITEM_ID'])) . "
234 ,'" . $this->Escape($arFields['ITEM_ID']) . "'
235 ," . sprintf('%u', crc32($arFields['PARAM1'])) . "
236 ,'" . $this->Escape($arFields['PARAM1']) . "'
237 ," . sprintf('%u', crc32($arFields['PARAM2'])) . "
238 ,'" . $this->Escape($arFields['PARAM2']) . "'
239 ," . $DATE_CHANGE . '
240 ,' . $DATE_FROM . '
241 ,' . $DATE_TO . '
242 ,' . intval($arFields['CUSTOM_RANK']) . '
243 ,(' . $this->tags($arFields['SITE_ID'], $arFields['TAGS']) . ')
244 ,(' . $this->rights($arFields['PERMISSIONS']) . ')
245 ,(' . $this->sites($arFields['SITE_ID']) . ')
246 ,(' . $this->params($arFields['PARAMS']) . ")
247 ,'" . $this->Escape($arFields['TITLE']) . "'
248 ,'" . $this->Escape($BODY) . "'
249 )
250 ";
251 $result = $this->query($sql);
252 if ($result)
253 {
254 $this->tagsRegister($arFields['SITE_ID'], $arFields['TAGS']);
255 }
256 else
257 {
258 throw new \Bitrix\Main\Db\SqlQueryException('Sphinx select error', $this->getError(), $sql);
259 }
260 }
261
262 public function update($ID, $arFields)
263 {
264 $DB = CDatabase::GetModuleConnection('search');
265 $ID = intval($ID);
266
267 $arUpdate = [];
268 $bReplace = array_key_exists('TITLE', $arFields)
269 || array_key_exists('BODY', $arFields)
270 || array_key_exists('MODULE_ID', $arFields)
271 || array_key_exists('ITEM_ID', $arFields)
272 || array_key_exists('PARAM1', $arFields)
273 || array_key_exists('PARAM2', $arFields)
274 ;
275
276 if (array_key_exists('~DATE_CHANGE', $arFields))
277 {
278 $arFields['DATE_CHANGE'] = $arFields['~DATE_CHANGE'];
279 unset($arFields['~DATE_CHANGE']);
280 }
281 elseif (array_key_exists('LAST_MODIFIED', $arFields))
282 {
283 $arFields['DATE_CHANGE'] = $arFields['LAST_MODIFIED'];
284 unset($arFields['LAST_MODIFIED']);
285 }
286 elseif (array_key_exists('DATE_CHANGE', $arFields))
287 {
288 $arFields['DATE_CHANGE'] = $DB->FormatDate($arFields['DATE_CHANGE'], 'DD.MM.YYYY HH:MI:SS', CLang::GetDateFormat());
289 }
290
291 if (array_key_exists('DATE_CHANGE', $arFields))
292 {
293 $DATE_CHANGE = intval(MakeTimeStamp($arFields['DATE_CHANGE']));
294 if ($DATE_CHANGE > 0)
295 {
296 $DATE_CHANGE -= CTimeZone::GetOffset();
297 }
298 $arUpdate['date_change'] = $DATE_CHANGE;
299 }
300
301 if (array_key_exists('DATE_FROM', $arFields))
302 {
303 $DATE_FROM = intval(MakeTimeStamp($arFields['DATE_FROM']));
304 if ($DATE_FROM > 0)
305 {
306 $DATE_FROM -= CTimeZone::GetOffset();
307 }
308 $arUpdate['date_from'] = $DATE_FROM;
309 }
310
311 if (array_key_exists('DATE_TO', $arFields))
312 {
313 $DATE_TO = intval(MakeTimeStamp($arFields['DATE_TO']));
314 if ($DATE_TO > 0)
315 {
316 $DATE_TO -= CTimeZone::GetOffset();
317 }
318 $arUpdate['date_to'] = $DATE_TO;
319 }
320
321 if (array_key_exists('CUSTOM_RANK', $arFields))
322 {
323 $arUpdate['custom_rank'] = '' . intval($arFields['CUSTOM_RANK']) . '';
324 }
325
326 if (array_key_exists('TAGS', $arFields))
327 {
328 $arUpdate['tags'] = '(' . $this->tags($arFields['SITE_ID'], $arFields['TAGS']) . ')';
329 }
330
331 if (array_key_exists('PERMISSIONS', $arFields))
332 {
333 $arUpdate['right'] = '(' . $this->rights($arFields['PERMISSIONS']) . ')';
334 }
335
336 if (array_key_exists('SITE_ID', $arFields))
337 {
338 $arUpdate['site'] = '(' . $this->sites($arFields['SITE_ID']) . ')';
339 }
340
341 if (array_key_exists('PARAMS', $arFields))
342 {
343 $arUpdate['param'] = '(' . $this->params($arFields['PARAMS']) . ')';
344 }
345
346 if (!empty($arUpdate) && !$bReplace)
347 {
348 foreach ($arUpdate as $columnName => $sqlValue)
349 {
350 $arUpdate[$columnName] = $columnName . '=' . $sqlValue;
351 }
352
353 $this->query('
354 UPDATE ' . $this->indexName . ' SET
355 ' . implode(', ', $arUpdate) . '
356 WHERE id = ' . $ID
357 );
358 }
359 elseif ($bReplace)
360 {
361 $dbItem = $DB->Query('
362 SELECT
363 *
364 ,' . $DB->DateToCharFunction('DATE_CHANGE') . ' as LAST_MODIFIED
365 ,' . $DB->DateToCharFunction('DATE_FROM') . ' as DATE_FROM
366 ,' . $DB->DateToCharFunction('DATE_TO') . ' as DATE_TO
367 FROM b_search_content
368 WHERE ID = ' . $ID
369 );
370 $searchItem = $dbItem->fetch();
371 if ($searchItem)
372 {
373 $dbTags = $DB->Query('SELECT * from b_search_tags WHERE SEARCH_CONTENT_ID=' . $ID);
374 while ($tag = $dbTags->fetch())
375 {
376 $searchItem['TAGS'] .= $tag['NAME'] . ',';
377 }
378
379 $dbRights = $DB->Query('SELECT * from b_search_content_right WHERE SEARCH_CONTENT_ID=' . $ID);
380 while ($right = $dbRights->fetch())
381 {
382 $searchItem['PERMISSIONS'][] = $right['GROUP_CODE'];
383 }
384
385 $dbSites = $DB->Query('SELECT * from b_search_content_site WHERE SEARCH_CONTENT_ID=' . $ID);
386 while ($site = $dbSites->fetch())
387 {
388 $searchItem['SITE_ID'][$site['SITE_ID']] = $site['URL'];
389 }
390
391 $dbParams = $DB->Query('SELECT * from b_search_content_param WHERE SEARCH_CONTENT_ID=' . $ID);
392 while ($param = $dbParams->fetch())
393 {
394 $searchItem['PARAMS'][$param['PARAM_NAME']][] = $param['PARAM_VALUE'];
395 }
396
397 $this->replace($ID, $searchItem);
398 }
399 }
400 }
401
402 function tags($arLID, $sContent)
403 {
404 $tags = [];
405 if (is_array($arLID))
406 {
407 foreach ($arLID as $site_id => $url)
408 {
409 $arTags = tags_prepare($sContent, $site_id);
410 foreach ($arTags as $tag)
411 {
412 $tags[] = sprintf('%u', crc32($tag));
413 }
414 }
415 }
416 return implode(',', $tags);
417 }
418
419 function tagsRegister($arLID, $sContent)
420 {
421 $DB = CDatabase::GetModuleConnection('search');
422 static $tagMap = [];
423
424 if (is_array($arLID))
425 {
426 foreach ($arLID as $site_id => $url)
427 {
428 $arTags = tags_prepare($sContent, $site_id);
429 foreach ($arTags as $tag)
430 {
431 $tag_id = sprintf('%u', crc32($tag));
432 if ($tag_id > 0x7FFFFFFF)
433 {
434 $tag_id = -(0xFFFFFFFF - $tag_id + 1);
435 }
436
437 if (!isset($tagMap[$tag_id]))
438 {
439 $rs = $DB->Query('select * from b_search_tags where SEARCH_CONTENT_ID=' . $tag_id . " AND SITE_ID='??'");
440 $tagMap[$tag_id] = $rs->fetch();
441 if (!$tagMap[$tag_id])
442 {
443 $DB->Query('insert into b_search_tags values (' . $tag_id . ", '??', '" . $DB->ForSql($tag) . "')");
444 }
445 }
446 }
447 }
448 }
449 }
450
451 function tagsFromArray($arTags)
452 {
453 $tags = [];
454 if (is_array($arTags))
455 {
456 foreach ($arTags as $tag)
457 {
458 $tags[] = sprintf('%u', crc32($tag));
459 }
460 }
461 return implode(',', $tags);
462 }
463
464 function rights($arRights)
465 {
466 $rights = [];
467 if (is_array($arRights))
468 {
469 foreach ($arRights as $group_id)
470 {
471 if (is_numeric($group_id))
472 {
473 $rights[$group_id] = sprintf('%u', crc32('G' . intval($group_id)));
474 }
475 else
476 {
477 $rights[$group_id] = sprintf('%u', crc32($group_id));
478 }
479 }
480 }
481 return implode(',', $rights);
482 }
483
484 function sites($arSites)
485 {
486 $sites = [];
487 if (is_array($arSites))
488 {
489 foreach ($arSites as $site_id => $url)
490 {
491 $sites[$site_id] = sprintf('%u', crc32($site_id));
492 }
493 }
494 else
495 {
496 $sites[$arSites] = sprintf('%u', crc32($arSites));
497 }
498 return implode(',', $sites);
499 }
500
502 {
503 $params = [];
504 if (is_array($arParams))
505 {
506 foreach ($arParams as $k1 => $v1)
507 {
508 $name = trim($k1);
509 if ($name != '')
510 {
511 if (!is_array($v1))
512 {
513 $v1 = [$v1];
514 }
515
516 foreach ($v1 as $v2)
517 {
518 $value = trim($v2);
519 if ($value != '')
520 {
521 $params[] = sprintf('%u', crc32(urlencode($name) . '=' . urlencode($value)));
522 }
523 }
524 }
525 }
526 }
527 return implode(',', $params);
528 }
529
530 public function getErrorText()
531 {
532 return $this->errorText;
533 }
534
535 public function getErrorNumber()
536 {
537 return $this->errorNumber;
538 }
539
540 public function search($arParams, $aSort, $aParamsEx, $bTagsCloud)
541 {
542 $result = [];
543 $this->errorText = '';
544 $this->errorNumber = 0;
545
546 $this->tags = trim($arParams['TAGS']);
547
548 $limit = 0;
549 if (is_array($aParamsEx) && isset($aParamsEx['LIMIT']))
550 {
551 $limit = intval($aParamsEx['LIMIT']);
552 unset($aParamsEx['LIMIT']);
553 }
554
555 $offset = 0;
556 if (is_array($aParamsEx) && isset($aParamsEx['OFFSET']))
557 {
558 $offset = intval($aParamsEx['OFFSET']);
559 unset($aParamsEx['OFFSET']);
560 }
561
562 if (is_array($aParamsEx) && !empty($aParamsEx))
563 {
564 $aParamsEx['LOGIC'] = 'OR';
565 $arParams[] = $aParamsEx;
566 }
567
568 $this->SITE_ID = $arParams['SITE_ID'];
569
570 $arWhere = [];
571 $cond1 = implode("\n\t\t\t\t\t\tand ", $this->prepareFilter($arParams, true));
572
573 $rights = $this->CheckPermissions();
574 if ($rights)
575 {
576 $arWhere[] = 'right in (' . $rights . ')';
577 }
578
579 $strQuery = trim($arParams['QUERY']);
580 if ($strQuery != '')
581 {
582 $arWhere[] = "MATCH('" . $this->Escape($strQuery) . "')";
583 $this->query = $strQuery;
584 }
585
586 if ($cond1 != '')
587 {
588 $arWhere[] = 'cond1 = 1';
589 }
590
591 if ($strQuery || $this->tags || $bTagsCloud)
592 {
593 if ($limit <= 0)
594 {
595 $limit = intval(COption::GetOptionInt('search', 'max_result_size'));
596 }
597
598 if ($limit <= 0)
599 {
600 $limit = 500;
601 }
602
603 $ts = time() - CTimeZone::GetOffset();
604 if ($bTagsCloud)
605 {
606 $sql = '
607 select groupby() tag_id
608 ,count(*) cnt
609 ,max(date_change) dc_tmp
610 ,if(date_to, date_to, ' . $ts . ') date_to_nvl
611 ,if(date_from, date_from, ' . $ts . ') date_from_nvl
612 ' . ($cond1 != '' ? ',' . $cond1 . ' as cond1' : '') . '
613 from ' . $this->indexName . '
614 where ' . implode("\nand\t", $arWhere) . '
615 group by tags
616 order by cnt desc
617 limit 0, ' . $limit . '
618 option max_matches = ' . $limit . '
619 ';
620
621 $DB = CDatabase::GetModuleConnection('search');
622 $startTime = microtime(true);
623
624 $r = $this->query($sql);
625
626 if ($DB->ShowSqlStat)
627 {
628 $DB->addDebugQuery($sql, microtime(true) - $startTime);
629 }
630
631 if (!$r)
632 {
633 throw new \Bitrix\Main\Db\SqlQueryException('Sphinx select error', $this->getError(), $sql);
634 }
635 else
636 {
637 while ($res = $this->fetch($r))
638 {
639 $result[] = $res;
640 }
641 }
642 }
643 else
644 {
645 $sql = '
646 select id
647 ,item
648 ,param1
649 ,param2
650 ,module_id
651 ,param2_id
652 ,date_change
653 ,custom_rank
654 ,weight() as rank
655 ' . ($cond1 != '' ? ',' . $cond1 . ' as cond1' : '') . '
656 ,if(date_to, date_to, ' . $ts . ') date_to_nvl
657 ,if(date_from, date_from, ' . $ts . ') date_from_nvl
658 from ' . $this->indexName . '
659 where ' . implode("\nand\t", $arWhere) . '
660 ' . $this->__PrepareSort($aSort) . '
661 limit ' . $offset . ', ' . $limit . '
662 option max_matches = ' . ($offset + $limit) . '
663 ';
664
665 $DB = CDatabase::GetModuleConnection('search');
666 $startTime = microtime(true);
667
668 $r = $this->query($sql);
669
670 if ($DB->ShowSqlStat)
671 {
672 $DB->addDebugQuery($sql, microtime(true) - $startTime);
673 }
674
675 if (!$r)
676 {
677 throw new \Bitrix\Main\Db\SqlQueryException('Sphinx select error', $this->getError(), $sql);
678 }
679 else
680 {
681 $forum = sprintf('%u', crc32('forum'));
682 while ($res = $this->fetch($r))
683 {
684 if ($res['module_id'] == $forum)
685 {
686 if (array_key_exists($res['param2_id'], $this->arForumTopics))
687 {
688 continue;
689 }
690 $this->arForumTopics[$res['param2_id']] = true;
691 }
692 $result[] = $res;
693 }
694 }
695 }
696 }
697 else
698 {
699 $this->errorText = GetMessage('SEARCH_ERROR3');
700 $this->errorNumber = 3;
701 }
702
703 return $result;
704 }
705
706 function searchTitle($phrase = '', $arPhrase = [], $nTopCount = 5, $arParams = [], $bNotFilter = false, $order = '')
707 {
708 $sqlWords = [];
709 foreach (array_reverse($arPhrase, true) as $word => $pos)
710 {
711 $word = $this->Escape($word);
712 if (empty($sqlWords) && !preg_match("/[\\n\\r \\t]$/", $phrase))
713 {
714 $sqlWords[] = $word . '*';
715 }
716 else
717 {
718 $sqlWords[] = $word;
719 }
720 }
721 $match = '@title ' . implode(' ', array_reverse($sqlWords));
722
723 $checkDates = false;
724 if (array_key_exists('CHECK_DATES', $arParams))
725 {
726 if ($arParams['CHECK_DATES'] == 'Y')
727 {
728 $checkDates = true;
729 }
730 unset($arParams['CHECK_DATES']);
731 }
732
733 $arWhere = $this->prepareFilter($arParams);
734
735 $cond1 = '';
736 if (isset($arWhere['cond1']))
737 {
738 $cond1 = $arWhere['cond1'];
739 unset($arWhere['cond1']);
740 }
741
742 $ts = time() - CTimeZone::GetOffset();
743 if ($checkDates)
744 {
745 $arWhere[] = 'date_from_nvl <= ' . $ts;
746 $arWhere[] = 'date_to_nvl >= ' . $ts;
747 }
748
749 $rights = $this->CheckPermissions();
750 if ($rights)
751 {
752 $arWhere[] = 'right in (' . $rights . ')';
753 }
754
755 $arWhere[] = 'site = ' . sprintf('%u', crc32(SITE_ID));
756 $arWhere[] = "match('" . $match . "')";
757
758 $sql = '
759 select id
760 ,weight() as rank
761 ' . ($cond1 != '' ? ',' . $cond1 . ' as cond1' : '') . '
762 ,if(date_to, date_to, ' . $ts . ') date_to_nvl
763 ,if(date_from, date_from, ' . $ts . ') date_from_nvl
764 from ' . $this->indexName . '
765 where ' . implode("\nand\t", $arWhere) . '
766 ' . ($cond1 != '' ? ' and cond1 = ' . intval(!$bNotFilter) : '') . '
767 ' . $this->__PrepareSort($order) . '
768 limit 0, ' . $nTopCount . '
769 option max_matches = ' . $nTopCount . '
770 ';
771
772 $DB = CDatabase::GetModuleConnection('search');
773 $startTime = microtime(true);
774
775 $r = $this->query($sql);
776
777 if ($DB->ShowSqlStat)
778 {
779 $DB->addDebugQuery($sql, microtime(true) - $startTime);
780 }
781
782 if (!$r)
783 {
784 throw new \Bitrix\Main\Db\SqlQueryException('Sphinx select error', $this->getError(), $sql);
785 }
786 else
787 {
788 $result = [];
789 while ($res = $this->fetch($r))
790 {
791 $result[] = $res['id'];
792 }
793 return $result;
794 }
795 }
796
798 {
799 return new CSearchSphinxFormatter($this);
800 }
801
802 function filterField($field, $value, $inSelect)
803 {
804 $arWhere = [];
805 if (is_array($value))
806 {
807 if (!empty($value))
808 {
809 $s = '';
810 if ($inSelect)
811 {
812 foreach ($value as $i => $v)
813 {
814 $s .= ',' . sprintf('%u', crc32($v));
815 }
816 $arWhere[] = 'in(' . $field . ' ' . $s . ')';
817 }
818 else
819 {
820 foreach ($value as $i => $v)
821 {
822 $s .= ($s ? ' or ' : '') . $field . ' = ' . sprintf('%u', crc32($v));
823 }
824 $arWhere[] = '(' . $s . ')';
825 }
826 }
827 }
828 else
829 {
830 if ($value !== false)
831 {
832 $arWhere[] = $field . ' = ' . sprintf('%u', crc32($value));
833 }
834 }
835 return $arWhere;
836 }
837
838 function prepareFilter($arFilter, $inSelect = false)
839 {
840 $arWhere = [];
841 if (!is_array($arFilter))
842 {
843 $arFilter = [];
844 }
845
846 $orLogic = false;
847 if (array_key_exists('LOGIC', $arFilter))
848 {
849 $orLogic = ($arFilter['LOGIC'] == 'OR');
850 unset($arFilter['LOGIC']);
851 }
852
853 foreach ($arFilter as $field => $val)
854 {
855 $field = mb_strtoupper($field);
856 if (
857 is_array($val)
858 && count($val) == 1
859 && $field !== 'URL'
860 && $field !== 'PARAMS'
861 && !is_numeric($field)
862 )
863 {
864 $val = $val[0];
865 }
866
867 switch ($field)
868 {
869 case 'ITEM_ID':
870 case '=ITEM_ID':
871 $arWhere = array_merge($arWhere, $this->filterField('item_id', $val, $inSelect));
872 break;
873 case '!ITEM_ID':
874 if ($val !== false)
875 {
876 $arWhere[] = 'item_id <> ' . sprintf('%u', crc32($val));
877 }
878 break;
879 case 'MODULE_ID':
880 case '=MODULE_ID':
881 if ($val !== false && $val !== 'no')
882 {
883 $arWhere[] = 'module_id = ' . sprintf('%u', crc32($val));
884 }
885 break;
886 case 'PARAM1':
887 case '=PARAM1':
888 $arWhere = array_merge($arWhere, $this->filterField('param1_id', $val, $inSelect));
889 break;
890 case '!PARAM1':
891 case '!=PARAM1':
892 if ($val !== false)
893 {
894 $arWhere[] = 'param1_id <> ' . sprintf('%u', crc32($val));
895 }
896 break;
897 case 'PARAM2':
898 case '=PARAM2':
899 $arWhere = array_merge($arWhere, $this->filterField('param2_id', $val, $inSelect));
900 break;
901 case '!PARAM2':
902 case '!=PARAM2':
903 if ($val !== false)
904 {
905 $arWhere[] = 'param2_id <> ' . sprintf('%u', crc32($val));
906 }
907 break;
908 case 'DATE_CHANGE':
909 if ($val <> '')
910 {
911 $arWhere[] = 'date_change >= ' . intval(MakeTimeStamp($val) - CTimeZone::GetOffset());
912 }
913 break;
914 case '<=DATE_CHANGE':
915 if ($val <> '')
916 {
917 $arWhere[] = 'date_change <= ' . intval(MakeTimeStamp($val) - CTimeZone::GetOffset());
918 }
919 break;
920 case '>=DATE_CHANGE':
921 if ($val <> '')
922 {
923 $arWhere[] = 'date_change >= ' . intval(MakeTimeStamp($val) - CTimeZone::GetOffset());
924 }
925 break;
926 case 'SITE_ID':
927 if ($val !== false)
928 {
929 if ($inSelect)
930 {
931 $arWhere[] = 'in(site, ' . sprintf('%u', crc32($val)) . ')';
932 }
933 else
934 {
935 $arWhere[] = 'site = ' . sprintf('%u', crc32($val));
936 }
937 }
938 break;
939 case 'CHECK_DATES':
940 if ($val == 'Y')
941 {
942 $ts = time() - CTimeZone::GetOffset();
943 if ($inSelect)
944 {
945 $arWhere[] = 'if(date_from, date_from, ' . $ts . ') <= ' . $ts;
946 $arWhere[] = 'if(date_to, date_to, ' . $ts . ') >= ' . $ts;
947 }
948 else
949 {
950 $arWhere[] = 'date_from_nvl <= ' . $ts;
951 $arWhere[] = 'date_to_nvl >= ' . $ts;
952 }
953 }
954 break;
955 case 'TAGS':
956 $arTags = explode(',', $val);
957 foreach ($arTags as $i => &$strTag)
958 {
959 $strTag = trim($strTag, " \n\r\t\"");
960 if ($strTag == '')
961 {
962 unset($arTags[$i]);
963 }
964 }
965 unset($strTag);
966
967 $arWhere = array_merge($arWhere, $this->filterField('tags', $arTags, $inSelect));
968 break;
969 case 'PARAMS':
970 if (is_array($val))
971 {
972 $params = $this->params($val);
973 if ($params != '')
974 {
975 if ($inSelect)
976 {
977 $arWhere[] = 'in(param, ' . $params . ')';
978 }
979 else
980 {
981 foreach (explode(',', $params) as $param)
982 {
983 $arWhere[] = 'param = ' . $param;
984 }
985 }
986 }
987 }
988 break;
989 case 'URL': //TODO
990 case 'QUERY':
991 case 'LIMIT':
992 case 'USE_TF_FILTER':
993 break;
994 default:
995 if (is_numeric($field) && is_array($val))
996 {
997 $subFilter = $this->prepareFilter($val, true);
998 if (!empty($subFilter))
999 {
1000 if (isset($subFilter['cond1']))
1001 {
1002 $arWhere['cond1'][] = '(' . implode(')and(', $subFilter) . ')';
1003 }
1004 else
1005 {
1006 $arWhere[] = '(' . implode(')and(', $subFilter) . ')';
1007 }
1008 }
1009 }
1010 else
1011 {
1012 //AddMessage2Log("field: $field; val: ".print_r($val, 1));
1013 }
1014 break;
1015 }
1016 }
1017
1018 if (isset($arWhere['cond1']))
1019 {
1020 $arWhere['cond1'] = '(' . implode(')and(', $arWhere['cond1']) . ')';
1021 }
1022
1023 if ($orLogic && !empty($arWhere))
1024 {
1025 $arWhere = [
1026 'cond1' => '(' . implode(')or(', $arWhere) . ')'
1027 ];
1028 }
1029
1030 return $arWhere;
1031 }
1032
1034 {
1035 global $USER;
1036 $DB = CDatabase::GetModuleConnection('search');
1037
1038 $arResult = [];
1039
1040 if (!$USER->IsAdmin())
1041 {
1042 if ($USER->GetID() > 0)
1043 {
1045 $rs = $DB->Query('SELECT GROUP_CODE FROM b_search_user_right WHERE USER_ID = ' . $USER->GetID());
1046 while ($ar = $rs->Fetch())
1047 {
1048 $arResult[] = $ar['GROUP_CODE'];
1049 }
1050 }
1051 else
1052 {
1053 $arResult[] = 'G2';
1054 }
1055 }
1056
1057 return $this->rights($arResult);
1058 }
1059
1060 function __PrepareSort($aSort = [])
1061 {
1062 $arOrder = [];
1063 if (!is_array($aSort))
1064 {
1065 $aSort = [$aSort => 'ASC'];
1066 }
1067
1068 $this->flagsUseRatingSort = 0;
1069 foreach ($aSort as $key => $ord)
1070 {
1071 $ord = mb_strtoupper($ord) <> 'ASC' ? 'DESC' : 'ASC';
1072 $key = mb_strtolower($key);
1073 switch ($key)
1074 {
1075 case 'date_change':
1076 case 'custom_rank':
1077 case 'id':
1078 case 'param1':
1079 case 'param2':
1080 case 'date_from':
1081 case 'date_to':
1082 $arOrder[] = $key . ' ' . $ord;
1083 break;
1084 case 'item_id':
1085 $arOrder[] = 'item ' . $ord;
1086 break;
1087 case 'module_id':
1088 $arOrder[] = 'module ' . $ord;
1089 break;
1090 case 'rank':
1091 $arOrder[] = 'rank ' . $ord;
1092 break;
1093 }
1094 }
1095
1096 if (count($arOrder) == 0)
1097 {
1098 $arOrder[] = 'custom_rank DESC';
1099 $arOrder[] = 'rank DESC';
1100 $arOrder[] = 'date_change DESC';
1101 }
1102
1103 return ' ORDER BY ' . implode(', ',$arOrder);
1104 }
1105
1106 public function Escape($str)
1107 {
1108 static $search = [
1109 '\\',
1110 "'",
1111 '/',
1112 ')',
1113 '(',
1114 '$',
1115 '~',
1116 '!',
1117 '@',
1118 '^',
1119 '-',
1120 '|',
1121 '<',
1122 "\x0",
1123 '=',
1124 ];
1125 static $replace = [
1126 '\\\\',
1127 "\\'",
1128 '\\\\/',
1129 '\\\\)',
1130 '\\\\(',
1131 '\\\\$',
1132 '\\\\~',
1133 '\\\\!',
1134 '\\\\@',
1135 '\\\\^',
1136 '\\\\-',
1137 '\\\\|',
1138 '\\\\<',
1139 ' ',
1140 ' ',
1141 ];
1142
1143 $str = str_replace($search, $replace, $str);
1144
1145 $stat = count_chars($str, 1);
1146 if (isset($stat[ord('"')]) && $stat[ord('"')] % 2 === 1)
1147 {
1148 $str = str_replace('"', '\\\"', $str);
1149 }
1150
1151 return $str;
1152 }
1153
1154 public function Escape2($str)
1155 {
1156 static $search = [
1157 '\\',
1158 "'",
1159 '"',
1160 "\x0",
1161 ];
1162 static $replace = [
1163 '\\\\',
1164 "\\'",
1165 '\\\\"',
1166 ' ',
1167 ];
1168 return str_replace($search, $replace, $str);
1169 }
1170
1171 protected function canConnect()
1172 {
1173 return function_exists('mysqli_connect');
1174 }
1175
1177 {
1178 $error = '';
1179 if (function_exists('mysqli_connect'))
1180 {
1181 $result = mysqli_init();
1182
1183 if (mb_strpos($connectionIndex, ':') !== false)
1184 {
1185 list($host, $port) = explode(':', $connectionIndex, 2);
1186 $port = intval($port);
1187 }
1188 else
1189 {
1191 $port = 0;
1192 }
1193
1194 if ($port > 0)
1195 {
1196 if (!$result->real_connect($host, '', '', '', $port))
1197 {
1198 $error = mysqli_connect_error();
1199 $result = false;
1200 }
1201 }
1202 else
1203 {
1204 if (!$result->real_connect($host, '', '', ''))
1205 {
1206 $error = mysqli_connect_error();
1207 $result = false;
1208 }
1209 }
1210 }
1211 else
1212 {
1213 $result = false;
1214 $error = 'No MySql connection function has been found.';
1215 }
1216
1217 return $result;
1218 }
1219
1220 public function query($query)
1221 {
1222 if (is_object($this->db))
1223 {
1224 $result = $this->db->query($query);
1225 }
1226 else
1227 {
1228 $result = false;
1229 }
1230 return $result;
1231 }
1232
1233 public function fetch($queryResult)
1234 {
1235 if (is_object($this->db))
1236 {
1237 $result = mysqli_fetch_assoc($queryResult);
1238 }
1239 else
1240 {
1241 $result = false;
1242 }
1243 return $result;
1244 }
1245
1246 public function getError()
1247 {
1248 if (is_object($this->db))
1249 {
1250 $result = '[' . $this->db->errno . '] ' . $this->db->error;
1251 }
1252 else
1253 {
1254 $result = '';
1255 }
1256 return $result;
1257 }
1258}
1259
1261{
1263 private $sphinx = null;
1264
1265 function __construct($sphinx)
1266 {
1267 $this->sphinx = $sphinx;
1268 }
1269
1270 function format($r)
1271 {
1272 if ($r)
1273 {
1274 if (array_key_exists('tag_id', $r))
1275 {
1276 return $this->formatTagsRow($r);
1277 }
1278 elseif (array_key_exists('id', $r))
1279 {
1280 return $this->formatRow($r);
1281 }
1282 }
1283 }
1284
1285 function formatTagsRow($r)
1286 {
1287 $DB = CDatabase::GetModuleConnection('search');
1288
1289 $tag_id = $r['tag_id'];
1290 if ($tag_id > 0x7FFFFFFF)
1291 {
1292 $tag_id = -(0xFFFFFFFF - $tag_id + 1);
1293 }
1294
1295 $rs = $DB->Query('
1296 select
1297 st.NAME
1298 from b_search_tags st
1299 where st.SEARCH_CONTENT_ID = ' . $tag_id . "
1300 and st.SITE_ID = '??'
1301 ");
1302
1303 $rt = $rs->Fetch();
1304 if ($rt)
1305 {
1306 $rt['NAME'] = htmlspecialcharsex($rt['NAME']);
1307 $rt['CNT'] = $r['cnt'];
1308 $rt['FULL_DATE_CHANGE'] = ConvertTimeStamp($r['dc_tmp'] + CTimeZone::GetOffset(), 'FULL');
1309 $rt['DATE_CHANGE'] = ConvertTimeStamp($r['dc_tmp'] + CTimeZone::GetOffset(), 'SHORT');
1310 }
1311 return $rt;
1312 }
1313
1314 function formatRow($r)
1315 {
1316 $DB = CDatabase::GetModuleConnection('search');
1317 if ($this->sphinx->SITE_ID)
1318 {
1319 $rs = $DB->Query('
1320 select
1321 sc.ID
1322 ,sc.MODULE_ID
1323 ,sc.ITEM_ID
1324 ,sc.TITLE
1325 ,sc.TAGS
1326 ,sc.BODY
1327 ,sc.PARAM1
1328 ,sc.PARAM2
1329 ,sc.UPD
1330 ,sc.DATE_FROM
1331 ,sc.DATE_TO
1332 ,sc.URL
1333 ,sc.CUSTOM_RANK
1334 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE') . ' as FULL_DATE_CHANGE
1335 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE', 'SHORT') . ' as DATE_CHANGE
1336 ,scsite.SITE_ID
1337 ,scsite.URL SITE_URL
1338 ,sc.USER_ID
1339 from b_search_content sc
1340 INNER JOIN b_search_content_site scsite ON sc.ID=scsite.SEARCH_CONTENT_ID
1341 where ID = ' . $r['id'] . "
1342 and scsite.SITE_ID = '" . $DB->ForSql($this->sphinx->SITE_ID) . "'
1343 ");
1344 }
1345 else
1346 {
1347 $rs = $DB->Query('
1348 select
1349 sc.ID
1350 ,sc.MODULE_ID
1351 ,sc.ITEM_ID
1352 ,sc.TITLE
1353 ,sc.TAGS
1354 ,sc.BODY
1355 ,sc.PARAM1
1356 ,sc.PARAM2
1357 ,sc.UPD
1358 ,sc.DATE_FROM
1359 ,sc.DATE_TO
1360 ,sc.URL
1361 ,sc.CUSTOM_RANK
1362 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE') . ' as FULL_DATE_CHANGE
1363 ,' . $DB->DateToCharFunction('sc.DATE_CHANGE', 'SHORT') . ' as DATE_CHANGE
1364 ,\'\' as SITE_ID
1365 from b_search_content sc
1366 where ID = ' . $r['id'] . '
1367 ');
1368 }
1369 $r = $rs->Fetch();
1370 if ($r)
1371 {
1372 $r['TITLE_FORMATED'] = $this->buildExcerpts(htmlspecialcharsex($r['TITLE']));
1373 $r['TITLE_FORMATED_TYPE'] = 'html';
1374 $r['TAGS_FORMATED'] = tags_prepare($r['TAGS'], SITE_ID);
1375 $r['BODY_FORMATED'] = $this->buildExcerpts(htmlspecialcharsex($r['BODY']));
1376 $r['BODY_FORMATED_TYPE'] = 'html';
1377 }
1378 return $r;
1379 }
1380
1381 public function buildExcerpts($str)
1382 {
1383 $sql = "CALL SNIPPETS(
1384 '" . $this->sphinx->Escape2($str) . "'
1385 ,'" . $this->sphinx->Escape($this->sphinx->indexName) . "'
1386 ,'" . $this->sphinx->Escape($this->sphinx->query . ' ' . $this->sphinx->tags) . "'
1387 ,500 as limit
1388 ,1 as query_mode
1389 )";
1390 $result = $this->sphinx->query($sql);
1391
1392 if ($result)
1393 {
1394 $res = $this->sphinx->fetch($result);
1395 if ($res)
1396 {
1397 return $res['snippet'];
1398 }
1399 else
1400 {
1401 return '';
1402 }
1403 }
1404 else
1405 {
1406 throw new \Bitrix\Main\Db\SqlQueryException('Sphinx select error', $this->sphinx->getError(), $sql);
1407 }
1408 }
1409}
$arParams
Определения access_dialog.php:21
$type
Определения options.php:106
$dbSites
Определения options.php:14
$arSites
Определения options.php:15
global $APPLICATION
Определения include.php:80
$arResult
Определения generate_coupon.php:16
static KillEntities($str)
Определения search.php:1663
Определения full_text.php:105
Определения full_text.php:4
format($r)
Определения sphinx.php:1270
buildExcerpts($str)
Определения sphinx.php:1381
formatTagsRow($r)
Определения sphinx.php:1285
formatRow($r)
Определения sphinx.php:1314
__construct($sphinx)
Определения sphinx.php:1265
Определения sphinx.php:5
params($arParams)
Определения sphinx.php:501
searchTitle($phrase='', $arPhrase=[], $nTopCount=5, $arParams=[], $bNotFilter=false, $order='')
Определения sphinx.php:706
getErrorNumber()
Определения sphinx.php:535
$db
Определения sphinx.php:7
update($ID, $arFields)
Определения sphinx.php:262
getError()
Определения sphinx.php:1246
rights($arRights)
Определения sphinx.php:464
internalConnect($connectionIndex, &$error)
Определения sphinx.php:1176
tags($arLID, $sContent)
Определения sphinx.php:402
getErrorText()
Определения sphinx.php:530
$tags
Определения sphinx.php:38
prepareFilter($arFilter, $inSelect=false)
Определения sphinx.php:838
canConnect()
Определения sphinx.php:1171
Escape2($str)
Определения sphinx.php:1154
$SITE_ID
Определения sphinx.php:40
getRowFormatter()
Определения sphinx.php:797
connect($connectionIndex, $indexName='', $ignoreErrors=false)
Определения sphinx.php:44
Escape($str)
Определения sphinx.php:1106
sites($arSites)
Определения sphinx.php:484
$arForumTopics
Определения sphinx.php:6
fetch($queryResult)
Определения sphinx.php:1233
tagsFromArray($arTags)
Определения sphinx.php:451
__PrepareSort($aSort=[])
Определения sphinx.php:1060
$indexName
Определения sphinx.php:42
query($query)
Определения sphinx.php:1220
tagsRegister($arLID, $sContent)
Определения sphinx.php:419
deleteById($ID)
Определения sphinx.php:166
truncate()
Определения sphinx.php:160
filterField($field, $value, $inSelect)
Определения sphinx.php:802
replace($ID, $arFields)
Определения sphinx.php:171
CheckPermissions()
Определения sphinx.php:1033
search($arParams, $aSort, $aParamsEx, $bTagsCloud)
Определения sphinx.php:540
$query
Определения sphinx.php:39
$connectionIndex
Определения sphinx.php:41
static CheckCurrentUserGroups()
Определения user.php:27
$sites
Определения clear_component_cache.php:15
$startTime
Определения sync.php:69
$right
Определения options.php:8
$str
Определения commerceml2.php:63
$arFields
Определения dblapprove.php:5
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$arPhrase
Определения get_search.php:37
if($ajaxMode) $ID
Определения get_user.php:27
$host
Определения .description.php:9
global $DB
Определения cron_frame.php:29
global $USER
Определения csv_new_run.php:40
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
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