1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
word.php
См. документацию.
1<?php
8namespace Bitrix\Sale\Location\Search;
9
10use Bitrix\Main;
11use Bitrix\Main\DB;
12use Bitrix\Main\Entity;
13use Bitrix\Main\Localization\Loc;
14
15use Bitrix\Sale\Location;
16use Bitrix\Sale\Location\DB\BlockInserter;
17use Bitrix\Sale\Location\DB\Helper;
18
19Loc::loadMessages(__FILE__);
20
37final class WordTable extends Entity\DataManager implements \Serializable
38{
39 protected $procData = array();
43
44 protected $dictionaryIndex = array();
45
46 const STEP_SIZE = 10000;
47 const MTU = 9999;
48
49 public function serialize(): ?string
50 {
51 return serialize($this->procData);
52 }
53
54 public function unserialize($data): void
55 {
56 $this->procData = unserialize($data, ['allowed_classes' => false]);
57 $this->initInsertHandles();
58 }
59
60 public function __serialize()
61 {
62 return $this->procData;
63 }
64
65 public function __unserialize($data): void
66 {
67 if (is_array($data))
68 {
69 $this->procData = $data;
70 $this->initInsertHandles();
71 }
72 }
73
74 public static function getFilePath()
75 {
76 return __FILE__;
77 }
78
79 // this for keeping word dictionary
80 public static function getTableName()
81 {
82 return 'b_sale_loc_search_word';
83 }
84
85 // this for keeping links between location-and-word-id
86 public static function getTableNameWord2Location()
87 {
88 return 'b_sale_loc_search_w2l';
89 }
90
91 // this for merge, temporal table
92 public static function getTableNamePositionTemporal()
93 {
94 return 'b_sale_loc_search_wt';
95 }
96
97 public function __construct($parameters)
98 {
99 $this->resetProcess();
100 $this->initInsertHandles();
101
102 if(is_array($parameters['TYPES']) && !empty($parameters['TYPES']))
103 {
104 $this->procData['ALLOWED_TYPES'] = array_unique($parameters['TYPES']);
105 }
106 if(is_array($parameters['LANGS']) && !empty($parameters['LANGS']))
107 {
108 $this->procData['ALLOWED_LANGS'] = array_unique($parameters['LANGS']);
109 }
110
111 $this->procData['CURRENT_LOCATION'] = false;
112 $this->procData['CURRENT_LOCATION_WORDS'] = array();
113 }
114
115 public function initInsertHandles()
116 {
117 $this->word2LocationInserter = new BlockInserter(array(
118 'tableName' => static::getTableNameWord2Location(),
119 'exactFields' => array(
120 'LOCATION_ID' => array('data_type' => 'integer'),
121 'WORD_ID' => array('data_type' => 'integer')
122 ),
123 'parameters' => array(
124 'mtu' => static::MTU
125 )
126 ));
127
128 $this->dictionaryInserter = new BlockInserter(array(
129 'entityName' => '\Bitrix\Sale\Location\Search\WordTable',
130 'exactFields' => array(
131 'WORD'
132 ),
133 'parameters' => array(
134 'mtu' => static::MTU,
135 'autoIncrementFld' => 'ID',
136 'CALLBACKS' => array(
137 'ON_BEFORE_FLUSH' => array($this, 'onBeforeDictionaryFlush')
138 )
139 )
140 ));
141
142 $this->dictionaryResorter = new BlockInserter(array(
143 'tableName' => static::getTableNamePositionTemporal(),
144 'exactFields' => array(
145 'WORD_ID' => array('data_type' => 'integer'),
146 'POSITION' => array('data_type' => 'integer')
147 ),
148 'parameters' => array(
149 'mtu' => static::MTU
150 )
151 ));
152 }
153
154 public function resetProcess()
155 {
156 $this->procData = array(
157 'OFFSET' => 0,
158 'POSITION' => 0,
159 'CURRENT_LOCATION' => false,
160 'CURRENT_LOCATION_WORDS' => array()
161 );
162 }
163
164 public static function cleanUpData()
165 {
166 $dbConnection = Main\HttpApplication::getConnection();
167
168 Helper::dropTable(static::getTableName());
169
170 $binary = mb_strtolower($dbConnection->getType()) == 'mysql' ? 'binary' : ''; // http://bugs.mysql.com/bug.php?id=34096
171
172 // ORACE: OK, MSSQL: OK
173 Main\HttpApplication::getConnection()->query("create table ".static::getTableName()." (
174
175 ID ".Helper::getSqlForDataType('int')." not null ".Helper::getSqlForAutoIncrement().",
176 WORD ".Helper::getSqlForDataType('varchar', 50)." ".$binary." not null,
177 POSITION ".Helper::getSqlForDataType('int')." default '0',
178 primary key (ID)
179 )");
180
181 Helper::createIndex(static::getTableName(), 'TMP', array('WORD'), true);
182 Helper::dropTable(static::getTableNameWord2Location());
183
184 // ORACLE: OK, MSSQL: OK
185 Main\HttpApplication::getConnection()->query("create table ".static::getTableNameWord2Location()." (
186
187 LOCATION_ID ".Helper::getSqlForDataType('int')." not null,
188 WORD_ID ".Helper::getSqlForDataType('int')." not null,
189
190 primary key (LOCATION_ID, WORD_ID)
191 )");
192
193 Helper::dropTable(static::getTableNamePositionTemporal());
194
195 $dbConnection->query("create table ".static::getTableNamePositionTemporal()." (
196 WORD_ID ".Helper::getSqlForDataType('int')." not null,
197 POSITION ".Helper::getSqlForDataType('int')." default '0'
198 )");
199 }
200
201 public static function createIndex()
202 {
203 Helper::dropIndexByName('IX_SALE_LOC_SEARCH_WORD_TMP', static::getTableName());
204 Helper::createIndex(static::getTableName(), 'WP', array('WORD', 'POSITION'));
205 }
206
207 public static function parseWords($words)
208 {
209 $result = array();
210 foreach($words as $k => &$word)
211 {
212 $word = mb_strtoupper(trim($word));
213 $word = str_replace('%', '', $word);
214
215 if($word == '')
216 continue;
217
218 $result[] = $word;
219 }
220
221 //natsort($result);
222
223 return array_unique($result);
224 }
225
226 public static function parseString($query)
227 {
228 $query = mb_strtoupper(Trim($query));
229
230 //$query = str_replace(array_keys(static::$blackList), static::$blackList, ' '.$query.' ');
231 $query = str_replace(array(')', '(', '%', '_'), array('', '', '', ''), $query);
232
233 $words = explode(' ', $query);
234
235 return self::parseWords($words);
236 }
237
238 public function setOffset($offset)
239 {
240 $this->procData['OFFSET'] = $offset;
241 }
242
243 public function getOffset()
244 {
245 return $this->procData['OFFSET'];
246 }
247
248 public function setPosition($position)
249 {
250 $this->procData['POSITION'] = $position;
251 }
252
253 public function getPosition()
254 {
255 return $this->procData['POSITION'];
256 }
257
258 public function onBeforeDictionaryFlush()
259 {
260 $this->dictionaryIndex = array();
261 }
262
263 public static function getFilterForInitData($parameters = array())
264 {
265 $filter = array();
266
267 if(!is_array($parameters))
268 $parameters = array();
269
270 if(is_array($parameters['TYPES']) && !empty($parameters['TYPES']))
271 $filter['=LOCATION.TYPE_ID'] = array_unique($parameters['TYPES']);
272
273 if(is_array($parameters['LANGS']) && !empty($parameters['LANGS']))
274 $filter['=LANGUAGE_ID'] = array_unique($parameters['LANGS']);
275
276 return $filter;
277 }
278
279 public function initializeData()
280 {
281 $res = Location\Name\LocationTable::getList(array(
282 'select' => array(
283 'NAME',
284 'LOCATION_ID'
285 ),
286 'filter' => static::getFilterForInitData(array(
287 'TYPES' => $this->procData['ALLOWED_TYPES'],
288 'LANGS' => $this->procData['ALLOWED_LANGS']
289 )),
290 'order' => array('LOCATION_ID' => 'asc'), // need to make same location ids stay together
291 'limit' => static::STEP_SIZE,
292 'offset' => $this->procData['OFFSET']
293 ));
294
295 $cnt = 0;
296 while($item = $res->fetch())
297 {
298 if($item['NAME'] <> '')
299 {
300 if($this->procData['CURRENT_LOCATION'] != $item['LOCATION_ID'])
301 {
302 $this->procData['CURRENT_LOCATION_WORDS'] = array();
303 }
304
305 $this->procData['CURRENT_LOCATION'] = $item['LOCATION_ID'];
306
307 $words = static::parseString($item['NAME']);
308
309 foreach($words as $k => &$word)
310 {
311 $wordHash = md5($word);
312
313 $wordId = false;
314 if(isset($this->dictionaryIndex[$wordHash])) // word is already added and in a hot index
315 {
316 $wordId = $this->dictionaryIndex[$wordHash];
317 }
318 else
319 {
320 $wordId = static::getIdByWord($word); // check if the word was added previously
321 }
322
323 if($wordId === false)
324 {
325 $wordId = $this->dictionaryInserter->insert(array(
326 'WORD' => $word
327 ));
328 $this->dictionaryIndex[$wordHash] = $wordId;
329 }
330
331 if($wordId !== false && !isset($this->procData['CURRENT_LOCATION_WORDS'][$wordId]))
332 {
333 $this->procData['CURRENT_LOCATION_WORDS'][$wordId] = true;
334
335 $this->word2LocationInserter->insert(array(
336 'LOCATION_ID' => intval($item['LOCATION_ID']),
337 'WORD_ID' => intval($wordId)
338 ));
339 }
340 }
341 }
342
343 $cnt++;
344 }
345
346 $this->procData['OFFSET'] += static::STEP_SIZE;
347
348 $this->dictionaryInserter->flush();
349 $this->word2LocationInserter->flush();
350
351 return !$cnt;
352 }
353
354 public function resort()
355 {
357 Main\HttpApplication::getConnection()->getSqlHelper()->getTopSql("select ID, WORD from ".static::getTableName()." order by WORD asc, ID asc", self::STEP_SIZE, intval($this->procData['OFFSET']))
358 );
359
360 $cnt = 0;
361 while($item = $res->fetch())
362 {
363 $this->procData['POSITION']++;
364
365 $this->dictionaryResorter->insert(array(
366 'WORD_ID' => $item['ID'],
367 'POSITION' => $this->procData['POSITION']
368 ));
369
370 $cnt++;
371 }
372
373 $this->procData['OFFSET'] += static::STEP_SIZE;
374
375 $this->dictionaryResorter->flush();
376
377 return !$cnt;
378 }
379
380 public static function mergeResort()
381 {
382 Helper::mergeTables(
383 static::getTableName(),
384 static::getTableNamePositionTemporal(),
385 array('POSITION' => 'POSITION'),
386 array('ID' => 'WORD_ID')
387 );
388
389 Main\HttpApplication::getConnection()->query("drop table ".static::getTableNamePositionTemporal());
390 }
391
392 public static function getIdByWord($word)
393 {
394 $word = trim((string)$word);
395 if ($word === '')
396 {
397 return false;
398 }
399
400 $dbConnection = Main\HttpApplication::getConnection();
401
402 $item = $dbConnection->query(
403 "select ID from " . static::getTableName()
404 . " where WORD = '" . $dbConnection->getSqlHelper()->forSql($word) . "'"
405 )->fetch();
406
407 $id = (int)($item['ID'] ?? 0);
408
409 return $id ?: false;
410 }
411
412 public static function getBoundsByWord($word)
413 {
414 $word = trim($word);
415
416 $dbConnection = Main\HttpApplication::getConnection();
417 $sql = "select MIN(POSITION) as INF, MAX(POSITION) as SUP from ".static::getTableName()." where WORD like '".mb_strtoupper($dbConnection->getSqlHelper()->forSql($word))."%'";
418
419 return $dbConnection->query($sql)->fetch();
420 }
421
422 public static function getWordsByBounds($inf, $sup)
423 {
424 return static::getList(array('filter' => array(
425 '>=POSITION' => intval($inf),
426 '<=POSITION' => intval($sup)
427 ), 'order' => array(
428 'POSITION' => 'asc'
429 )));
430 }
431
432 public static function getBoundsForPhrase($phrase)
433 {
434 if(is_string($phrase))
435 $words = self::parseString($phrase);
436 elseif(is_array($phrase))
437 $words = self::parseWords($phrase);
438 else
439 return array();
440
441 // check for empty request
442
443 $bounds = array();
444 $sizes = array();
445 $i = 0;
446 foreach($words as $word)
447 {
448 $bound = self::getBoundsByWord($word);
449 if(!intval($bound['INF']) && !intval($bound['SUP'])) // no such word
450 return array();
451
452 $bounds[$i] = $bound;
453
454 $sizes[] = $bound['SUP'] - $bound['INF'];
455
456 $i++;
457 }
458
459 // resort bounds to have sorted smallest to largest
460 //asort($sizes, SORT_NUMERIC);
461
462 $boundsSorted = array();
463 foreach($sizes as $j => $size)
464 $boundsSorted[] = $bounds[$j];
465
466 // todo: here drop nested intervals, if any
467
468 return $boundsSorted;
469 }
470
471 public static function getMap()
472 {
473 return array(
474
475 'ID' => array(
476 'data_type' => 'integer',
477 'primary' => true,
478 'autocomplete' => true,
479 ),
480 'WORD' => array(
481 'data_type' => 'string',
482 ),
483 'POSITION' => array(
484 'data_type' => 'integer'
485 ),
486
487 'CNT' => array(
488 'data_type' => 'integer',
489 'expression' => array(
490 'count(*)'
491 )
492 ),
493 );
494 }
495}
496
static getConnection($name="")
Определения application.php:638
setPosition($position)
Определения word.php:248
static parseString($query)
Определения word.php:226
static getMap()
Определения word.php:471
BlockInserter $dictionaryInserter
Определения word.php:41
static getBoundsForPhrase($phrase)
Определения word.php:432
static getIdByWord($word)
Определения word.php:392
static getWordsByBounds($inf, $sup)
Определения word.php:422
static getTableNamePositionTemporal()
Определения word.php:92
static getBoundsByWord($word)
Определения word.php:412
static getFilterForInitData($parameters=array())
Определения word.php:263
BlockInserter $word2LocationInserter
Определения word.php:40
static createIndex()
Определения word.php:201
static cleanUpData()
Определения word.php:164
static getFilePath()
Определения word.php:74
setOffset($offset)
Определения word.php:238
unserialize($data)
Определения word.php:54
__construct($parameters)
Определения word.php:97
static parseWords($words)
Определения word.php:207
static mergeResort()
Определения word.php:380
__unserialize($data)
Определения word.php:65
static getTableNameWord2Location()
Определения word.php:86
BlockInserter $dictionaryResorter
Определения word.php:42
static getTableName()
Определения word.php:80
$data['IS_AVAILABLE']
Определения .description.php:13
</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
$filter
Определения iblock_catalog_list.php:54
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$i
Определения factura.php:643
$k
Определения template_pdf.php:567