1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
phraseindexsearch.php
См. документацию.
1<?php
2
3namespace Bitrix\Translate\Index;
4
5use Bitrix\Main;
6use Bitrix\Main\Application;
7use Bitrix\Main\Data\Cache;
8use Bitrix\Main\ArgumentException;
9use Bitrix\Main\DB\SqlQueryException;
10use Bitrix\Main\Localization\Loc;
11use Bitrix\Main\ORM\Data\DataManager;
12use Bitrix\Main\ORM\Fields\ExpressionField;
13use Bitrix\Main\ORM\Fields\IntegerField;
14use Bitrix\Main\ORM\Fields\FloatField;
15use Bitrix\Translate;
16use Bitrix\Translate\Index;
17
19{
20 public const SEARCH_METHOD_EXACT = 'exact';
21 public const SEARCH_METHOD_EQUAL = 'equal';
22 public const SEARCH_METHOD_CASE_SENSITIVE = 'case_sensitive';
23 public const SEARCH_METHOD_ENTRY_WORD = 'entry_word';
24 public const SEARCH_METHOD_START_WITH = 'start_with';
25 public const SEARCH_METHOD_END_WITH = 'end_with';
26
27 // Lang code by IETF BCP 47
28 private const NON_FTS = ['th', 'zh-cn', 'zh-tw', 'ja', 'ko'];
29
36 public static function query(array $params = []): Main\ORM\Query\Query
37 {
38 [, $runtime, ] = self::processParams($params);
39
41 foreach ($runtime as $field)
42 {
43 $entity->addField($field);
44 }
45
46 return new Main\ORM\Query\Query($entity);
47 }
48
49
56 public static function getCount(array $filterIn): int
57 {
58 [, $runtime, $filter] = self::processParams(['filter' => $filterIn]);
59
61 foreach ($runtime as $field)
62 {
63 $entity->addField($field);
64 }
65
67
68 $query
69 ->addSelect(new ExpressionField('CNT', 'COUNT(1)'))
70 ->setFilter($filter);
71
72 $result = $query->exec()->fetch();
73
74 return (int)$result['CNT'];
75 }
76
77
84 public static function getList(array $params): Main\ORM\Query\Result
85 {
86 [$select, $runtime, $filter] = self::processParams($params);
87
88 $executeParams = [
89 'select' => \array_merge(
90 [
91 'PATH_ID' => 'PATH_ID',
92 'PHRASE_CODE' => 'CODE',
93 'FILE_PATH' => 'PATH.PATH',
94 'TITLE' => 'PATH.NAME',
95 ],
97 ),
98 'runtime' => $runtime,
99 'filter' => $filter,
100 ];
101
102 if (isset($params['order']))
103 {
104 $executeParams['order'] = $params['order'];
105 }
106 if (isset($params['offset']))
107 {
108 $executeParams['offset'] = $params['offset'];
109 }
110 if (isset($params['limit']))
111 {
112 $executeParams['limit'] = $params['limit'];
113 }
114 if (isset($params['count_total']))
115 {
116 $executeParams['count_total'] = true;
117 }
118
119 $entityClass = self::getPathCodeEntityClass();
120
121 return $entityClass::getList($executeParams);
122 }
123
124
125
130 public static function getPathCodeEntityClass(): string
131 {
132 static $class;
133 if ($class === null)
134 {
136 $class = $entity->getDataClass();
137 }
138
139 return $class;
140 }
141
146 public static function getPathCodeEntity(): Main\ORM\Entity
147 {
148 static $entity;
149 if ($entity === null)
150 {
151 $subQuery = (new Main\ORM\Query\Query(Index\Internals\PhraseIndexTable::getEntity()))
152 ->setSelect(['PATH_ID', 'CODE'])
153 ->setGroup(['PATH_ID', 'CODE']);
154
155 $entity = Main\ORM\Entity::compileEntity(
156 'PathPhraseIndexReference',
157 [
158 'PATH_ID' => ['data_type' => 'string'],
159 'CODE' => ['data_type' => 'string'],
160 ],
161 [
162 'table_name' => '('.$subQuery->getQuery().')',
163 'namespace' => __NAMESPACE__. '\\Internals',
164 ]
165 );
166 }
167
168 return $entity;
169 }
170
177 private static function processParams(array $params): array
178 {
179 $select = [];
180 $runtime = [];
181 $filterIn = [];
182 $filterOut = [];
183 $runtimeInx = [];
184
185 if (isset($params['filter']))
186 {
187 if (\is_object($params['filter']))
188 {
189 $filterIn = clone $params['filter'];
190 }
191 else
192 {
193 $filterIn = $params['filter'];
194 }
195 }
196
197 $enabledLanguages = Translate\Config::getEnabledLanguages();
198 $languageUpperKeys = \array_combine($enabledLanguages, \array_map('mb_strtoupper', $enabledLanguages));
199
200 foreach ($languageUpperKeys as $langId => $langUpper)
201 {
202 $tbl = "{$langUpper}_LNG";
203 $alias = "{$langUpper}_LANG";
204
205 if (
206 !empty($params['select']) && in_array($alias, $params['select'])
207 || isset($params['order'], $params['order'][$alias])
208 )
209 {
210 $i = count($runtime);
211 $runtimeInx[$tbl] = $i;
212 $runtime[$i] = new Main\ORM\Fields\Relations\Reference(
213 $tbl,
214 Index\Internals\PhraseFts::getFtsEntityClass($langId),
215 Main\ORM\Query\Join::on('ref.PATH_ID', '=', 'this.PATH_ID')
216 ->whereColumn('ref.CODE', '=', 'this.CODE'),
217 ['join_type' => 'LEFT']
218 );
219
220 $runtimeInx[$alias] = $i++;
221 $runtime[$i] = new ExpressionField($alias, '%s', "{$tbl}.PHRASE");
222 $select[] = $alias;
223 }
224 }
225
226 if (!isset($filterIn['PHRASE_ENTRY']))
227 {
228 $filterIn['PHRASE_ENTRY'] = [];
229 }
230 if (!isset($filterIn['CODE_ENTRY']))
231 {
232 $filterIn['CODE_ENTRY'] = [];
233 }
234
235 // top folder
236 if (!empty($filterIn['PATH']))
237 {
238 $topIndexPath = Index\PathIndex::loadByPath($filterIn['PATH']);
239 if ($topIndexPath instanceof Index\PathIndex)
240 {
241 $filterOut['=PATH.DESCENDANTS.PARENT_ID'] = $topIndexPath->getId();//ancestor
242 }
243 unset($filterIn['PATH']);
244 }
245
246 // search by code
247 if (!empty($filterIn['INCLUDE_PHRASE_CODES']))
248 {
249 $codes = \preg_split("/[\r\n\t,; ]+/u", $filterIn['INCLUDE_PHRASE_CODES']);
250 $codes = \array_filter($codes);
251 if (\count($codes) > 0)
252 {
253 $useLike = false;
254 foreach ($codes as $code)
255 {
256 if (\mb_strpos($code, '%') !== false)
257 {
258 $useLike = true;
259 break;
260 }
261 }
262 if ($useLike)
263 {
264 $filterOut['=%CODE'] = $codes;
265 }
266 else
267 {
268 $filterOut['=CODE'] = $codes;
269 }
270 }
271 unset($filterIn['INCLUDE_PHRASE_CODES']);
272 }
273 if (!empty($filterIn['EXCLUDE_PHRASE_CODES']))
274 {
275 $codes = \preg_split("/[\r\n\t,; ]+/u", $filterIn['EXCLUDE_PHRASE_CODES']);
276 $codes = \array_filter($codes);
277 if (\count($codes) > 0)
278 {
279 $useLike = false;
280 foreach ($codes as $code)
281 {
282 if (\mb_strpos($code, '%') !== false)
283 {
284 $useLike = true;
285 break;
286 }
287 }
288 if ($useLike)
289 {
290 $filterOut["!=%CODE"] = $codes;
291 }
292 else
293 {
294 $filterOut["!=CODE"] = $codes;
295 }
296 }
297 unset($filterIn['EXCLUDE_PHRASE_CODES']);
298 }
299
300 if (!empty($filterIn['PHRASE_CODE']))
301 {
302 if (\in_array(self::SEARCH_METHOD_CASE_SENSITIVE, $filterIn['CODE_ENTRY']))
303 {
304 if (\in_array(self::SEARCH_METHOD_EQUAL, $filterIn['CODE_ENTRY']))
305 {
306 $filterOut["=CODE"] = $filterIn['PHRASE_CODE'];
307 }
308 elseif (\in_array(self::SEARCH_METHOD_START_WITH, $filterIn['CODE_ENTRY']))
309 {
310 $filterOut["=%CODE"] = $filterIn['PHRASE_CODE'].'%';
311 }
312 elseif (\in_array(self::SEARCH_METHOD_END_WITH, $filterIn['CODE_ENTRY']))
313 {
314 $filterOut["=%CODE"] = '%'.$filterIn['PHRASE_CODE'];
315 }
316 else
317 {
318 $filterOut["=%CODE"] = '%'.$filterIn['PHRASE_CODE'].'%';
319 }
320 }
321 else
322 {
323 $runtime[] = new ExpressionField('CODE_UPPER', 'UPPER(CONVERT(%s USING latin1))', 'CODE');
324 if (\in_array(self::SEARCH_METHOD_EQUAL, $filterIn['CODE_ENTRY']))
325 {
326 $filterOut['=CODE_UPPER'] = \mb_strtoupper($filterIn['PHRASE_CODE']);
327 }
328 elseif (\in_array(self::SEARCH_METHOD_START_WITH, $filterIn['CODE_ENTRY']))
329 {
330 $filterOut['=%CODE_UPPER'] = \mb_strtoupper($filterIn['PHRASE_CODE']).'%';
331 }
332 elseif (\in_array(self::SEARCH_METHOD_END_WITH, $filterIn['CODE_ENTRY']))
333 {
334 $filterOut['=%CODE_UPPER'] = '%'.\mb_strtoupper($filterIn['PHRASE_CODE']);
335 }
336 else
337 {
338 $filterOut['=%CODE_UPPER'] = '%'.\mb_strtoupper($filterIn['PHRASE_CODE']).'%';
339 }
340 }
341 }
342 unset($filterIn['PHRASE_CODE'], $filterIn['CODE_ENTRY']);
343
344 $runtime[] = new Main\ORM\Fields\Relations\Reference(
345 'PATH',
346 Index\Internals\PathIndexTable::class,
347 Main\ORM\Query\Join::on('ref.ID', '=', 'this.PATH_ID'),
348 ['join_type' => 'INNER']
349 );
350
351 $filterOut['=PATH.IS_DIR'] = 'N';
352
353 $replaceLangId = function(&$val)
354 {
355 $val = Translate\IO\Path::replaceLangId($val, '#LANG_ID#');
356 };
357 $trimSlash = function(&$val)
358 {
359 if (\mb_strpos($val, '%') === false)
360 {
361 if (Translate\IO\Path::isPhpFile($val))
362 {
363 $val = '/'. \trim($val, '/');
364 }
365 else
366 {
367 $val = '/'. \trim($val, '/'). '/%';
368 }
369 }
370 };
371
372 if (!empty($filterIn['INCLUDE_PATHS']))
373 {
374 $pathIncludes = \preg_split("/[\r\n\t,; ]+/u", $filterIn['INCLUDE_PATHS']);
375 $pathIncludes = \array_filter($pathIncludes);
376 if (\count($pathIncludes) > 0)
377 {
378 $pathPathIncludes = [];
379 $pathNameIncludes = [];
380 foreach ($pathIncludes as $testPath)
381 {
382 if (!empty($testPath) && \trim($testPath) !== '')
383 {
384 if (\mb_strpos($testPath, '/') === false)
385 {
386 $pathNameIncludes[] = $testPath;
387 }
388 else
389 {
390 $pathPathIncludes[] = $testPath;
391 }
392 }
393 }
394 if (\count($pathNameIncludes) > 0 && \count($pathPathIncludes) > 0)
395 {
396 \array_walk($pathNameIncludes, $replaceLangId);
397 \array_walk($pathPathIncludes, $replaceLangId);
398 \array_walk($pathPathIncludes, $trimSlash);
399 $filterOut[] = [
400 'LOGIC' => 'OR',
401 '%=PATH.NAME' => $pathNameIncludes,
402 '%=PATH.PATH' => $pathPathIncludes,
403 ];
404 }
405 elseif (\count($pathNameIncludes) > 0)
406 {
407 \array_walk($pathNameIncludes, $replaceLangId);
408 $filterOut[] = [
409 'LOGIC' => 'OR',
410 '%=PATH.NAME' => $pathNameIncludes,
411 '%=PATH.PATH' => $pathNameIncludes,
412 ];
413 }
414 elseif (\count($pathPathIncludes) > 0)
415 {
416 \array_walk($pathPathIncludes, $replaceLangId);
417 \array_walk($pathPathIncludes, $trimSlash);
418 $filterOut['%=PATH.PATH'] = $pathPathIncludes;
419 }
420 }
421 unset($testPath, $pathIncludes, $pathNameIncludes, $pathPathIncludes);
422 }
423 if (!empty($filterIn['EXCLUDE_PATHS']))
424 {
425 $pathExcludes = \preg_split("/[\r\n\t,; ]+/u", $filterIn['EXCLUDE_PATHS']);
426 $pathExcludes = \array_filter($pathExcludes);
427 if (\count($pathExcludes) > 0)
428 {
429 $pathPathExcludes = [];
430 $pathNameExcludes = [];
431 foreach ($pathExcludes as $testPath)
432 {
433 if (!empty($testPath) && \trim($testPath) !== '')
434 {
435 if (\mb_strpos($testPath, '/') === false)
436 {
437 $pathNameExcludes[] = $testPath;
438 }
439 else
440 {
441 $pathPathExcludes[] = $testPath;
442 }
443 }
444 }
445 if (\count($pathNameExcludes) > 0 && \count($pathPathExcludes) > 0)
446 {
447 \array_walk($pathNameExcludes, $replaceLangId);
448 \array_walk($pathPathExcludes, $replaceLangId);
449 \array_walk($pathPathExcludes, $trimSlash);
450 $filterOut[] = [
451 'LOGIC' => 'AND',
452 '!=%PATH.NAME' => $pathNameExcludes,
453 '!=%PATH.PATH' => $pathPathExcludes,
454 ];
455 }
456 elseif (\count($pathNameExcludes) > 0)
457 {
458 \array_walk($pathNameExcludes, $replaceLangId);
459 $filterOut[] = [
460 'LOGIC' => 'AND',
461 '!=%PATH.NAME' => $pathNameExcludes,
462 '!=%PATH.PATH' => $pathNameExcludes,
463 ];
464 }
465 elseif (\count($pathPathExcludes) > 0)
466 {
467 \array_walk($pathPathExcludes, $replaceLangId);
468 \array_walk($pathPathExcludes, $trimSlash);
469 $filterOut["!=%PATH.PATH"] = $pathPathExcludes;
470 }
471 }
472 unset($testPath, $pathExcludes, $pathPathExcludes, $pathNameExcludes);
473 }
474 unset($filterIn['INCLUDE_PATHS'], $filterIn['EXCLUDE_PATHS']);
475
476 // search by phrase
477 if (!empty($filterIn['PHRASE_TEXT']))
478 {
479 $langId = !empty($filterIn['LANGUAGE_ID']) ? $filterIn['LANGUAGE_ID'] : Loc::getCurrentLang();
480
481 $langUpper = $languageUpperKeys[$langId];
482 $tbl = "{$langUpper}_LNG";
483 $alias = "{$langUpper}_LANG";
484 $fieldAlias = "{$tbl}.PHRASE";
485
486 /*
487 $runtime[] = new Main\ORM\Fields\Relations\Reference(
488 $tbl,
489 Index\Internals\PhraseIndexTable::class,
490 Main\ORM\Query\Join::on('ref.PATH_ID', '=', 'this.PATH_ID')
491 ->whereColumn('ref.CODE', '=', 'this.CODE')
492 ->where('ref.LANG_ID', '=', $langId),
493 ['join_type' => 'INNER']
494 );
495 */
496
497 $i = isset($runtimeInx[$tbl]) ? $runtimeInx[$tbl] : count($runtime);
498
499 $runtime[$i] = new Main\ORM\Fields\Relations\Reference(
500 $tbl,
501 Index\Internals\PhraseFts::getFtsEntityClass($langId),
502 Main\ORM\Query\Join::on('ref.PATH_ID', '=', 'this.PATH_ID')
503 ->whereColumn('ref.CODE', '=', 'this.CODE'),
504 ['join_type' => 'INNER']
505 );
506
507 if (!isset($runtimeInx[$alias]))
508 {
509 $select[$alias] = $fieldAlias;
510 }
511 $select["{$langUpper}_FILE_ID"] = "{$tbl}.FILE_ID";
512
513 $exact = \in_array(self::SEARCH_METHOD_EXACT, $filterIn['PHRASE_ENTRY']);
514 $entry = \in_array(self::SEARCH_METHOD_ENTRY_WORD, $filterIn['PHRASE_ENTRY']);
515 $case = \in_array(self::SEARCH_METHOD_CASE_SENSITIVE, $filterIn['PHRASE_ENTRY']);
516 $start = \in_array(self::SEARCH_METHOD_START_WITH, $filterIn['PHRASE_ENTRY']);
517 $end = \in_array(self::SEARCH_METHOD_END_WITH, $filterIn['PHRASE_ENTRY']);
518 $equal = \in_array(self::SEARCH_METHOD_EQUAL, $filterIn['PHRASE_ENTRY']);
519
520 if ($exact)
521 {
522 $phraseSearch = ["={$fieldAlias}" => $filterIn['PHRASE_TEXT']];
523 }
524 else
525 {
526 $sqlHelper = Main\Application::getConnection()->getSqlHelper();
527 $str = $sqlHelper->forSql($filterIn['PHRASE_TEXT']);
528
529 $phraseSearch = [
530 'LOGIC' => 'AND'
531 ];
532
533 // use fulltext index to help like operator
534 // todo: preg_replace has bug when replaces Thai unicode Non-spacing mark
535 if (!self::disallowFtsIndex($langId))
536 {
537 $minLengthFulltextWorld = self::getFullTextMinLength();
538 $fulltextIndexSearchStr = self::prepareTextForFulltextSearch($filterIn['PHRASE_TEXT']);
539 if (\mb_strlen($fulltextIndexSearchStr) > $minLengthFulltextWorld)
540 {
541 /*
542 if ($entry)
543 {
544 // identical full text match
545 // MATCH(PHRASE) AGAINST ('+smth' IN BOOLEAN MODE)
546 //$phraseSearch["*={$fieldAlias}"] = $fulltextIndexSearchStr;
547 $fulltextIndexSearchStr = "+" . \preg_replace("/\s+/iu", " +", $fulltextIndexSearchStr);
548 $runtime[] = (new ExpressionField(
549 'PHRASE_FTS',
550 "MATCH (%s) AGAINST ('({$fulltextIndexSearchStr})')",
551 "{$fieldAlias}"
552 ))->configureValueType(FloatField::class);
553 }
554 else
555 {
556 // use fulltext index to help like operator
557 // partial full text match
558 // MATCH(PHRASE) AGAINST ('+smth*' IN BOOLEAN MODE)
559 //$phraseSearch["*{$fieldAlias}"] = $fulltextIndexSearchStr;
560 $fulltextIndexSearchStr = "+" . \preg_replace("/\s+/iu", "* +", $fulltextIndexSearchStr) . "*";
561 $runtime[] = (new ExpressionField(
562 'PHRASE_FTS',
563 "MATCH (%s) AGAINST ('({$fulltextIndexSearchStr})')",
564 "{$fieldAlias}"
565 ))->configureValueType(FloatField::class);
566 }
567 $phraseSearch[">PHRASE_FTS"] = 0;
568 */
569
570 if ($entry)
571 {
572 // identical full text match
573 // MATCH(PHRASE) AGAINST ('+smth' IN BOOLEAN MODE)
574 $phraseSearch["*={$fieldAlias}"] = $fulltextIndexSearchStr;
575 }
576 else
577 {
578 // use fulltext index to help like operator
579 // partial full text match
580 // MATCH(PHRASE) AGAINST ('+smth*' IN BOOLEAN MODE)
581 $phraseSearch["*{$fieldAlias}"] = $fulltextIndexSearchStr;
582 }
583 }
584 }
585
586 if ($equal)
587 {
588 $likeStr = "{$str}";
589 }
590 elseif ($start)
591 {
592 $likeStr = "{$str}%%";
593 }
594 elseif ($end)
595 {
596 $likeStr = "%%{$str}";
597 }
598 elseif ($entry)
599 {
600 $likeStr = "%%{$str}%%";
601 }
602 elseif (self::disallowFtsIndex($langId))
603 {
604 //todo: preg_replace has bug when replaces Thai unicode Non-spacing mark
605 $likeStr = "%%" . \preg_replace("/\s+/iu", "%%", $str) . "%%";
606 }
607 else
608 {
609 $likeStr = "%%" . \preg_replace("/\W+/iu", "%%", $str) . "%%";
610 }
611 $likeStr = str_replace('%%%%', '%%', $likeStr);
612
613 if (self::allowICURegularExpression())
614 {
615 $regStr = \preg_replace("/\s+/iu", '[[:blank:]]+', $str);
616 }
617 else
618 {
619 if ($case)
620 {
621 $regStr = \preg_replace("/\s+/iu", '[[:blank:]]+', $str);
622 }
623 else
624 {
625 $regStr = '';
626 $regChars = ['?', '*', '|', '[', ']', '(', ')', '-', '+', '.'];
627 for ($p = 0, $len = Translate\Text\StringHelper::getLength($str); $p < $len; $p++)
628 {
629 $c0 = Translate\Text\StringHelper::getSubstring($str, $p, 1);
630 if (\in_array($c0, $regChars))
631 {
632 $regStr .= "\\\\" . $c0;
633 continue;
634 }
635 $c1 = Translate\Text\StringHelper::changeCaseToLower($c0);
636 $c2 = Translate\Text\StringHelper::changeCaseToUpper($c0);
637 if ($c0 != $c1)
638 {
639 $regStr .= '(' . $c0 . '|' . $c1 . '){1}';
640 }
641 elseif ($c0 != $c2)
642 {
643 $regStr .= '(' . $c0 . '|' . $c2 . '){1}';
644 }
645 else
646 {
647 $regStr .= $c0;
648 }
649 }
650 $regStr = \preg_replace("/\s+/iu", '[[:blank:]]+', $regStr);
651 }
652 }
653 $regStr = str_replace('%', '%%', $regStr);
654
655 $regExpStart = '';
656 $regExpEnd = '';
657 if (\preg_match("/^[[:alnum:]]+/iu", $str))
658 {
659 if (self::allowICURegularExpression())
660 {
661 $regExpStart = '\\\\b';
662 }
663 else
664 {
665 $regExpStart = '[[:<:]]';
666 }
667 }
668 if (\preg_match("/[[:alnum:]]+$/iu", $str))
669 {
670 if (self::allowICURegularExpression())
671 {
672 $regExpEnd = '\\\\b';
673 }
674 else
675 {
676 $regExpEnd = '[[:>:]]';
677 }
678 }
679
680 // Exact word match
681 if ($equal)
682 {
683 $regStr = "[[:blank:]]*{$regExpStart}({$regStr}){$regExpEnd}[[:blank:]]*";
684 }
685 elseif ($start)
686 {
687 $regStr = "[[:blank:]]*{$regExpStart}({$regStr}){$regExpEnd}";
688 }
689 elseif ($end)
690 {
691 $regStr = "{$regExpStart}({$regStr}){$regExpEnd}[[:blank:]]*";
692 }
693 elseif ($entry)
694 {
695 $regStr = "[[:blank:]]*{$regExpStart}({$regStr}){$regExpEnd}[[:blank:]]*";
696 }
697
698 // regexp binary mode works not exactly we want using like binary to fix it
699 $binarySensitive = $case ? 'BINARY' : '';
700 $runtime[] = (new ExpressionField(
701 'PHRASE_LIKE',
702 "CASE WHEN %s LIKE {$binarySensitive} '{$likeStr}' THEN 1 ELSE 0 END",
703 "{$fieldAlias}"
704 ))->configureValueType(IntegerField::class);
705 $phraseSearch["=PHRASE_LIKE"] = 1;
706
707 if (self::allowICURegularExpression())
708 {
709 // c meaning case-sensitive matching
710 // i meaning case-insensitive matching
711 $regCaseSensitive = $case ? 'c' : 'i';
712 $runtime[] = (new ExpressionField(
713 'PHRASE_REGEXP',
714 "REGEXP_LIKE(%s, '{$regStr}', '{$regCaseSensitive}')",
715 "{$fieldAlias}"
716 ))->configureValueType(IntegerField::class);
717 }
718 else
719 {
720 $runtime[] = (new ExpressionField(
721 'PHRASE_REGEXP',
722 "CASE WHEN %s REGEXP '{$regStr}' THEN 1 ELSE 0 END",
723 "{$fieldAlias}"
724 ))->configureValueType(IntegerField::class);
725 }
726 $phraseSearch["=PHRASE_REGEXP"] = 1;
727 }
728
729 $filterOut[] = $phraseSearch;
730 }
731 unset($filterIn['PHRASE_ENTRY'], $filterIn['PHRASE_TEXT'], $filterIn['LANGUAGE_ID']);
732
733
734 if (!empty($filterIn['FILE_NAME']))
735 {
736 $filterOut["=%PATH.NAME"] = '%'. $filterIn['FILE_NAME']. '%';
737 unset($filterIn['FILE_NAME']);
738 }
739 if (!empty($filterIn['FOLDER_NAME']))
740 {
741 $filterOut['=%PATH.PATH'] = '%/'. $filterIn['FOLDER_NAME']. '/%';
742 unset($filterIn['FOLDER_NAME']);
743 }
744
745 foreach ($filterIn as $key => $value)
746 {
747 if (\in_array($key, ['tabId', 'FILTER_ID', 'PRESET_ID', 'FILTER_APPLIED', 'FIND']))
748 {
749 continue;
750 }
751 $filterOut[$key] = $value;
752 }
753
754 return [$select, $runtime, $filterOut];
755 }
756
763 public static function disallowFtsIndex(string $langId): bool
764 {
765 static $cache;
766 if (empty($cache))
767 {
768 $cache = [];
769
771 'select' => ['ID', 'CODE'],
772 'filter' => ['=ACTIVE' => 'Y'],
773 ]);
774 while ($row = $iterator->fetch())
775 {
776 if (!empty($row['CODE']))
777 {
778 $cache[mb_strtolower($row['ID'])] = trim(mb_strtolower($row['CODE']));
779 }
780 }
781 }
782
783 return isset($cache[$langId]) && in_array($cache[$langId], self::NON_FTS);
784 }
785
792 protected static function allowICURegularExpression(): bool
793 {
794 static $allowICURE;
795 if ($allowICURE === null)
796 {
797 $majorVersion = \mb_substr(Application::getConnection()->getVersion()[0], 0, 1);
798 $allowICURE = (int)$majorVersion >= 8;
799 }
800
801 return $allowICURE;
802 }
803
811 public static function prepareTextForFulltextSearch(string $text): string
812 {
813 $minLengthFulltextWorld = self::getFullTextMinLength();
814
815 $text = \preg_replace("/\b\w{1,{$minLengthFulltextWorld}}\b/iu", '', $text);
816
817 $stopWorlds = self::getFullTextStopWords();
818 foreach ($stopWorlds as $stopWorld)
819 {
820 $text = \preg_replace("/\b{$stopWorld}\b/iu", '', $text);
821 }
822
823 $text = \preg_replace("/^\W+/iu", '', $text);
824 $text = \preg_replace("/\W+$/iu", '', $text);
825 $text = \preg_replace("/\W+/iu", ' ', $text);
826
827 return $text;
828 }
829
834 protected static function isInnodbEngine(): bool
835 {
836 static $available;
837 if ($available === null)
838 {
839 $available = false;
840 $cache = Cache::createInstance();
841 if ($cache->initCache(3600, 'translate::isInnodbEngine'))
842 {
843 $available = (bool)$cache->getVars();
844 }
845 elseif ($cache->startDataCache())
846 {
847 try
848 {
849 $check = Application::getConnection()->query(
850 "SHOW TABLE STATUS WHERE Name = 'b_translate_phrase' AND Engine = 'InnoDB'"
851 );
852 if ($check->fetch())
853 {
854 $available = true;
855 }
856 }
857 catch (SqlQueryException $exception)
858 {}
859 $cache->endDataCache((int)$available);
860 }
861 }
862
863 return $available;
864 }
865
866
872 protected static function getFullTextStopWords(): array
873 {
874 static $worldList;
875 if ($worldList === null)
876 {
877 $minLengthFulltextWorld = self::getFullTextMinLength();
878 $worldList = [];
879 $cache = Cache::createInstance();
880 if ($cache->initCache(3600, 'translate::FullTextStopWords'))
881 {
882 $worldList = $cache->getVars();
883 }
884 elseif ($cache->startDataCache())
885 {
886 try
887 {
888 if (self::isInnodbEngine())
889 {
890 $res = Application::getConnection()->query(
891 "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD"
892 );
893 while ($row = $res->fetch())
894 {
895 if (mb_strlen($row['value']) > $minLengthFulltextWorld)
896 {
897 $worldList[] = $row['value'];
898 }
899 }
900 }
901 }
902 catch (SqlQueryException $exception)
903 {}
904 $cache->endDataCache($worldList);
905 }
906 }
907
908 return $worldList;
909 }
910
918 public static function getFullTextMinLength(): int
919 {
920 static $fullTextMinLength;
921 if ($fullTextMinLength === null)
922 {
923 $fullTextMinLength = 4;
924 $cache = Cache::createInstance();
925 if ($cache->initCache(3600, 'translate::FullTextMinLength'))
926 {
927 $fullTextMinLength = $cache->getVars();
928 }
929 elseif ($cache->startDataCache())
930 {
931 if (self::isInnodbEngine())
932 {
933 $var = 'innodb_ft_min_token_size';
934 }
935 else
936 {
937 $var = 'ft_min_word_len';
938 }
939 try
940 {
941 $res = Application::getConnection()->query("SHOW VARIABLES LIKE '{$var}'");
942 if ($row = $res->fetch())
943 {
944 $fullTextMinLength = (int)$row['Value'];
945 }
946 }
947 catch (SqlQueryException $exception)
948 {}
949 $cache->endDataCache($fullTextMinLength);
950 }
951 }
952
953 return $fullTextMinLength;
954 }
955}
Определения result.php:20
static getList(array $parameters=array())
Определения datamanager.php:431
static query(array $params=[])
Определения phraseindexsearch.php:36
static prepareTextForFulltextSearch(string $text)
Определения phraseindexsearch.php:811
static getCount(array $filterIn)
Определения phraseindexsearch.php:56
static getList(array $params)
Определения phraseindexsearch.php:84
static disallowFtsIndex(string $langId)
Определения phraseindexsearch.php:763
$str
Определения commerceml2.php:63
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$start
Определения get_search.php:9
$query
Определения get_search.php:11
$entity
$p
Определения group_list_element_edit.php:23
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$value
Определения Param.php:39
Определения ufield.php:9
Определения chain.php:3
$var
Определения payment.php:63
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$text
Определения template_pdf.php:79
$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
$iterator
Определения yandex_run.php:610