1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
search.php
См. документацию.
1<?php
2IncludeModuleLangFile($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/classes/general/main.php");
4
6{
8
9 const TABLE_NAME = "b_ticket_search";
10
11 public static function getSql($query)
12 {
13 if (!static::isIndexExists() || !static::CheckModule())
14 {
15 return false;
16 }
17
18 global $DB;
19
20 $whereIn = '';
21 $having = '';
22
23 $words = array_keys(stemming($query, LANGUAGE_ID));
24
25 if (count($words))
26 {
27 $whereInAll = array();
28
29 foreach ($words as $word)
30 {
31 $whereInAll[] = "'".$DB->ForSql($word)."'";
32 }
33
34 $whereIn = 'TS.SEARCH_WORD IN ('.join(', ', $whereInAll).')';
35
36 if (count($words) > 1)
37 {
38 $having = 'SUM(CASE WHEN '.$whereIn.' THEN 1 ELSE 0 END) = '.count($words);
39 }
40 }
41
42 return array('WHERE' => $whereIn, 'HAVING' => $having);
43 }
44
48 function GetFilterQuery($q, $idName, $titleName, $messageName, &$error)
49 {
50 if(!self::CheckModule()) return "";
52 $res = self::PrepareQuery($res, $idName, $titleName, $messageName, $error);
53 return $res;
54 }
55
59 function PrepareQuery($q, $idName, $titleName, $messageName, &$error)
60 {
61 global $DB;
62 $state = 0;
63 $sRes = "";
64 $n = 0;
65 $error = "";
66 $errorno = 0;
67 $i = 0;
68 $quote = "";
69 $inQuoteS = "";
70 $arrQ = explode(" ", $q);
71 foreach($arrQ as $k => $t)
72 {
73 $t = trim($t);
74 if($t == '')
75 {
76 continue;
77 }
78 switch ($state)
79 {
80 case 0:
81 if(($t == "||") || ($t == "&&") || ($t == ")"))
82 {
83 $error = GetMessage("FILTER_ERROR2") . " " . $t;
84 $errorno = 2;
85 break 2;
86 }
87 elseif($t == "!")
88 {
89 $state = 0;
90 $sRes .= " NOT";
91 }
92 elseif($t == "(")
93 {
94 $n++;
95 $state = 0;
96 $sRes .= " (";
97 }
98 elseif(($t == '"') || ($t == "'"))
99 {
100 $quote = $t;
101 $state = 2;
102 }
103 else
104 {
105 $state = 1;
106 $sRes .= self::GetSQLfilter($t, $idName, $titleName, $messageName);
107 }
108 break;
109
110 case 1:
111 if(($t == "||") || ($t == "&&"))
112 {
113 $state = 0;
114 if($t == "||") $sRes .= " OR";
115 else $sRes .= " AND";
116 }
117 elseif($t == ")")
118 {
119 $n--;
120 $state = 1;
121 $sRes .= ")";
122 }
123 else
124 {
125 $error = GetMessage("FILTER_ERROR2") . " " . $t;
126 $errorno = 2;
127 break 2;
128 }
129 break;
130 case 2:
131 if($t == $quote)
132 {
133 $state = 1;
134 $inQuoteS = "%" . str_replace("^", " ", $inQuoteS) . "%";
135 $sRes .= "\n ($titleName LIKE '" . $DB->ForSql($inQuoteS) . "' OR $messageName LIKE '" . $DB->ForSql($inQuoteS) . "')";
136 $inQuoteS = "";
137 $quote = "";
138 }
139 elseif(($t != "'") && ($t != '"'))
140 {
141 $inQuoteS .= $t;
142 }
143 break;
144 }
145 }
146
147 if(($errorno == 0) && ($n != 0))
148 {
149 $error = GetMessage("FILTER_ERROR1");
150 $errorno = 1;
151 }
152 if(($errorno == 0) && ($quote != ""))
153 {
154 $error = GetMessage("FILTER_ERROR4");
155 $errorno = 3;
156 }
157 if($errorno > 0)
158 {
159 return null;
160 }
161 return $sRes;
162 }
163
167 function ParseQ($q)
168 {
169 $q = trim($q);
170 if($q == '')
171 {
172 return '';
173 }
174 $q=self::ParseStr($q, "^");
175
176 $q = str_replace(
177 array("&" , "|" , "~" , "(" , ")", '"', "'"),
178 array(" && ", " || ", " ! ", " (", ") ", ' " ', " ' "),
179 $q
180 );
181 $q="($q)";
182 $q = preg_replace("/\\s+/u", " ", $q);
183
184 return $q;
185 }
186
190 function ParseStr($qwe, $default_op = "&")
191 {
192 $qwe=trim($qwe);
193
194 $qwe=preg_replace("/\\s{0,}\\+ {0,}/", "&", $qwe);
195
196 $qwe=preg_replace("/\\s{0,}([()|~]) {0,}/", "\\1", $qwe);
197
198 $qwe=preg_replace("/(\\s{1,}|\\&\\|{1,}|\\|\\&{1,})/", $default_op, $qwe);
199
200 // remove unnesessary boolean operators
201 $qwe=preg_replace("/\\|+/", "|", $qwe);
202 $qwe=preg_replace("/\\&+/", "&", $qwe);
203 $qwe=preg_replace("/\\~+/", "~", $qwe);
204 $qwe=preg_replace("/\\|\\&\\|/", "&", $qwe);
205 $qwe=preg_replace("/[|&~]+$/", "", $qwe);
206 $qwe=preg_replace("/^[|&]+/", "", $qwe);
207
208 // transform "w1 ~w2" -> "w1 default_op ~ w2"
209 // ") ~w" -> ") default_op ~w"
210 // "w ~ (" -> "w default_op ~("
211 // ") w" -> ") default_op w"
212 // "w (" -> "w default_op ("
213 // ")(" -> ") default_op ("
214
215 $qwe=preg_replace("/([^&~|()]+)~([^&~|()]+)/", "\\1".$default_op."~\\2", $qwe);
216 $qwe=preg_replace("/\\)~{1,}/", ")".$default_op."~", $qwe);
217 $qwe=preg_replace("/~{1,}\\(/", ($default_op=="|"? "~|(": "&~("), $qwe);
218 $qwe=preg_replace("/\\)([^&~|()]+)/", ")".$default_op."\\1", $qwe);
219 $qwe=preg_replace("/([^&~|()]+)\\(/", "\\1".$default_op."(", $qwe);
220 $qwe=preg_replace("/\\) *\\(/", ")".$default_op."(", $qwe);
221
222 // remove unnesessary boolean operators
223 $qwe=preg_replace("/\\|+/", "|", $qwe);
224 $qwe=preg_replace("/\\&+/", "&", $qwe);
225
226 // remove errornous format of query - ie: '(&', '&)', '(|', '|)', '~&', '~|', '~)'
227 $qwe=preg_replace("/\\(\\&{1,}/", "(", $qwe);
228 $qwe=preg_replace("/\\&{1,}\\)/", ")", $qwe);
229 $qwe=preg_replace("/\\~{1,}\\)/", ")", $qwe);
230 $qwe=preg_replace("/\\(\\|{1,}/", "(", $qwe);
231 $qwe=preg_replace("/\\|{1,}\\)/", ")", $qwe);
232 $qwe=preg_replace("/\\~{1,}\\&{1,}/", "&", $qwe);
233 $qwe=preg_replace("/\\~{1,}\\|{1,}/", "|", $qwe);
234
235 $qwe=preg_replace("/\\(\\)/", "", $qwe);
236 $qwe=preg_replace("/^[|&]{1,}/", "", $qwe);
237 $qwe=preg_replace("/[|&~]{1,}\$/", "", $qwe);
238 $qwe=preg_replace("/\\|\\&/", "&", $qwe);
239 $qwe=preg_replace("/\\&\\|/", "|", $qwe);
240
241 // remove unnesessary boolean operators
242 $qwe=preg_replace("/\\|+/", "|", $qwe);
243 $qwe=preg_replace("/\\&+/", "&", $qwe);
244
245 return($qwe);
246 }
247
248 static function CheckModule()
249 {
250 if(self::$searchModule === null)
251 {
252 self::$searchModule = CModule::IncludeModule("search");
253 }
254
255 return self::$searchModule;
256 }
257
258 static function isIndexExists()
259 {
260 return (COption::GetOptionString('support', 'SEARCH_VERSION', '0') === '12.0.3');
261 }
262
266 static function GetSQLfilter($s, $idName, $titleName, $messageName)
267 {
268 global $DB;
269 $res = "";
270 $and = "";
271 $arrQ = explode("^", $s);
272 foreach($arrQ as $k => $v)
273 {
274 if(substr_count($v, "%") > 0)
275 {
276 $res .= self::StrInEXISTS($and, $idName, "LIKE", $v);
277 }
278 else
279 {
280 $resArr = stemming($v, LANGUAGE_ID);
281 if(count($resArr) > 0)
282 {
283 foreach($resArr as $k2 => $v2)
284 {
285 $res .= self::StrInEXISTS($and, $idName, "=", $k2);
286 $and = " AND";
287 }
288 }
289 else
290 {
291 $res .= "\n" . $and . " ($titleName = '" . $DB->ForSql($v) . "' OR $messageName = '" . $DB->ForSql($v) . "')";
292 }
293 }
294 $and = " AND";
295 }
296 if($res != "") $res = "\n(" . $res . "\n)\n";
297 return $res;
298 }
299
303 static function StrInEXISTS($and, $idName, $sign, $key)
304 {
305 global $DB;
306 $ticketSearch = self::TABLE_NAME;
307 return "\n" . $and . " EXISTS(SELECT 1 FROM $ticketSearch WHERE MESSAGE_ID = $idName AND SEARCH_WORD $sign '" . $DB->ForSql($key) . "')";
308 }
309
316 public static function indexTicket($ticket, $messages = null)
317 {
318 if(!self::CheckModule())
319 {
320 return false;
321 }
322
323 global $DB;
324
325 // select ticket row
326 if (!is_array($ticket))
327 {
328 $result = $DB->Query("SELECT ID, SITE_ID, TITLE FROM b_ticket WHERE ID = ".intval($ticket));
329 $ticket = $result->Fetch();
330
331 if (!$ticket)
332 {
333 return false;
334 }
335 }
336
337 // select message rows
338 if ($messages === null)
339 {
340 $messages = array();
341 $result = $DB->Query("SELECT MESSAGE FROM b_ticket_message WHERE IS_LOG='N' AND TICKET_ID = ".intval($ticket['ID'])." AND LENGTH(MESSAGE) < 10240");
342 while ($row = $result->Fetch())
343 {
344 $messages[] = $row;
345 }
346 }
347
348 // select language for stemming
349 if ($ticket['SITE_ID'] === SITE_ID) // чему он равен в админке?
350 {
351 $langId = LANGUAGE_ID;
352 }
353 else
354 {
355 $result = CSite::GetByID($ticket['SITE_ID']);
356 $site = $result->Fetch();
357
358 if (!$site)
359 {
360 return false;
361 }
362
363 $langId = $site['LANGUAGE_ID'];
364 }
365
366 // set index text
367 $indexText = $ticket['TITLE'];
368
369 foreach ($messages as $message)
370 {
371 $indexText .= ' '.$message['MESSAGE'].' ';
372 }
373
374 $index = stemming(HTMLToTxt($indexText), $langId);
375
376 // insert index into db
377 // better to make DB->multiInsert
378 foreach (array_keys($index) as $phrase)
379 {
380 $insertQuery = "INSERT INTO " . self::TABLE_NAME . "(TICKET_ID, SEARCH_WORD) VALUES ".
381 "(".intval($ticket['ID']).", '" . $DB->ForSql($phrase) . "')";
382
383 $DB->Query($insertQuery);
384 }
385 }
386
387 public static function reindexTicket($ticket, $messages = null)
388 {
389 if(!self::CheckModule())
390 {
391 return false;
392 }
393
394 global $DB;
395
396 if (is_array($ticket))
397 {
398 $ticketId = intval($ticket['ID']);
399 }
400 else
401 {
402 $ticketId = intval($ticket);
403 }
404
405 $DB->Query("DELETE FROM ".self::TABLE_NAME." WHERE TICKET_ID = ".$ticketId);
406
407 return static::indexTicket($ticket, $messages);
408 }
409
410 public static function indexAllTickets($startFromId = 0, $timeLimit = 10)
411 {
412 return static::performAllTicketsIndexing($startFromId, $timeLimit, false);
413 }
414
415 public static function reindexAllTickets($startFromId = 0, $timeLimit = 10)
416 {
417 return static::performAllTicketsIndexing($startFromId, $timeLimit, true);
418 }
419
420 protected static function performAllTicketsIndexing($startFromId = 0, $timeLimit = 10, $removeOldIndex = false)
421 {
422 if (!static::CheckModule())
423 {
424 return false;
425 }
426
427 $endTime = time() + $timeLimit;
428
429 global $DB;
430
431 $lastId = intval($startFromId);
432
433 while (time() < $endTime)
434 {
435 $tickets = array();
436 $messages = array();
437
438 $result = $DB->Query($DB->TopSql("
439 SELECT
440 T.ID, T.SITE_ID, T.TITLE, TM.MESSAGE
441 FROM
442 b_ticket T,
443 b_ticket_message TM
444 WHERE
445 TM.TICKET_ID = T.ID AND T.ID > " . $lastId . " AND TM.IS_LOG='N'
446 ORDER BY
447 T.ID ASC"
448 , 100));
449
450 while ($row = $result->Fetch())
451 {
452 $tickets[$row['ID']] = $row;
453 $messages[$row['ID']][] = array('MESSAGE' => $row['MESSAGE']);
454 $endTicketId = $row['ID'];
455 }
456
457 // empty result
458 if (empty($tickets))
459 {
460 // set option allows to use new index
461 COption::SetOptionString('support', 'SEARCH_VERSION', '12.0.3');
462
463 // delete updater notification
464 CAdminNotify::DeleteByTag('SUPORT_SEARCH_CONVERT_12_0_3');
465
466 return -1;
467 }
468
469 // reselect last ticket's messages to complete them because of previous limit in query
470 unset($messages[$endTicketId]);
471 $result = $DB->Query("SELECT MESSAGE FROM b_ticket_message WHERE TICKET_ID = ".$endTicketId." AND IS_LOG='N' AND LENGTH(MESSAGE) < 10240");
472 while ($row = $result->Fetch())
473 {
474 $messages[$endTicketId][] = $row;
475 }
476
477 // remove old index
478 if ($removeOldIndex)
479 {
480 $ticketIds = array_keys($tickets);
481 $removeFromId = min($ticketIds);
482 $removeToId = max($ticketIds);
483
484 $DB->Query("DELETE FROM ".static::TABLE_NAME." WHERE TICKET_ID >= ".$removeFromId." AND TICKET_ID <= ".$removeToId);
485 }
486
487 // add new index
488 foreach ($tickets as $ticket)
489 {
490 static::indexTicket($ticket, $messages[$ticket['ID']]);
491 $lastId = $ticket['ID'];
492 }
493 }
494
495 return $lastId;
496 }
497
501 static function WriteWordsInTable($M_ID, $SITE_ID, $s)
502 {
503 global $DB;
504 if(!self::CheckModule()) return;
505 $M_ID = intval($M_ID);
506 $ticketSearch = self::TABLE_NAME;
507 $rsSite = CSite::GetByID($SITE_ID);
508 $arrSite = $rsSite->Fetch();
509 $langID = $arrSite["LANGUAGE_ID"];
510
511 $DB->Query("DELETE FROM $ticketSearch WHERE MESSAGE_ID = $M_ID");
512 $res = stemming(HTMLToTxt($s), $langID);
513 foreach($res as $key => $val)
514 {
515 $strSql = "INSERT INTO " . $ticketSearch . "(MESSAGE_ID, SEARCH_WORD) VALUES ($M_ID, '" . $DB->ForSql($key) . "')";
516 $res = $DB->Query($strSql);
517 //$DB->Insert($ticketSearch, array("MESSAGE_ID" => $M_ID, "SEARCH_WORD" => "'" . $DB->ForSql($key) . "'"));
518 }
519 }
520
524 static function ReindexMessages($firstID, $periodS = 8)
525 {
526 global $DB;
527 $firstID = intval($firstID);
528
529 $endTime = time() + $periodS;
530 $strSql = "
531 SELECT
532 T.SITE_ID,
533 TM.ID,
534 TM.MESSAGE
535 FROM
536 b_ticket T
537 INNER JOIN b_ticket_message TM
538 ON T.ID = TM.TICKET_ID
539 AND TM.ID > $firstID
540 AND TM.IS_LOG = 'N'
541 AND LENGTH(TM.MESSAGE) < 10240
542 ORDER BY TM.ID";
543 $res = $DB->Query($strSql);
544 $lastID = 0;
545 while($cs = $res->Fetch())
546 {
547 if(time() > $endTime) return $lastID;
548 self::WriteWordsInTable($cs["ID"], $cs["SITE_ID"], $cs["MESSAGE"]);
549 $lastID = intval($cs["ID"]);
550 }
551 return -1;
552 }
553}
static DeleteByTag($tagId)
Определения admin_notify.php:146
Определения search.php:6
static indexTicket($ticket, $messages=null)
Определения search.php:316
static StrInEXISTS($and, $idName, $sign, $key)
Определения search.php:303
GetFilterQuery($q, $idName, $titleName, $messageName, &$error)
Определения search.php:48
static reindexAllTickets($startFromId=0, $timeLimit=10)
Определения search.php:415
static WriteWordsInTable($M_ID, $SITE_ID, $s)
Определения search.php:501
static getSql($query)
Определения search.php:11
static reindexTicket($ticket, $messages=null)
Определения search.php:387
static indexAllTickets($startFromId=0, $timeLimit=10)
Определения search.php:410
ParseQ($q)
Определения search.php:167
static performAllTicketsIndexing($startFromId=0, $timeLimit=10, $removeOldIndex=false)
Определения search.php:420
static GetSQLfilter($s, $idName, $titleName, $messageName)
Определения search.php:266
static CheckModule()
Определения search.php:248
PrepareQuery($q, $idName, $titleName, $messageName, &$error)
Определения search.php:59
const TABLE_NAME
Определения search.php:9
static isIndexExists()
Определения search.php:258
static ReindexMessages($firstID, $periodS=8)
Определения search.php:524
ParseStr($qwe, $default_op="&")
Определения search.php:190
static $searchModule
Определения search.php:7
if(!\Bitrix\Main\Loader::includeModule('clouds')) $lastId
Определения sync.php:68
</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
$query
Определения get_search.php:11
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $DB
Определения cron_frame.php:29
HTMLToTxt($str, $strSiteUrl="", $aDelete=[], $maxlen=70)
Определения tools.php:2587
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$sign
Определения payment.php:69
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
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
$messages
Определения template.php:8
$val
Определения options.php:1793
const SITE_ID
Определения sonet_set_content_view.php:12
stemming($sText, $sLang='ru', $bIgnoreStopWords=false, $bReturnPositions=false)
Определения stemming.php:148
$error
Определения subscription_card_product.php:20
$k
Определения template_pdf.php:567
$n
Определения update_log.php:107
$SITE_ID
Определения yandex_run.php:607
$site
Определения yandex_run.php:614