%PDF- %PDF-
| Direktori : /proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/location/search/ |
| Current File : //proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/location/search/word.php |
<?php
/**
* Bitrix Framework
* @package Bitrix\Sale\Location
* @subpackage sale
* @copyright 2001-2014 Bitrix
*/
namespace Bitrix\Sale\Location\Search;
use Bitrix\Main;
use Bitrix\Main\DB;
use Bitrix\Main\Entity;
use Bitrix\Main\Localization\Loc;
use Bitrix\Sale\Location;
use Bitrix\Sale\Location\DB\BlockInserter;
use Bitrix\Sale\Location\DB\Helper;
Loc::loadMessages(__FILE__);
final class WordTable extends Entity\DataManager implements \Serializable
{
protected $procData = array();
protected $word2LocationInserter = null;
protected $dictionaryInserter = null;
protected $dictionaryIndex = array();
const STEP_SIZE = 10000;
const MTU = 9999;
public function serialize()
{
return serialize($this->procData);
}
public function unserialize($data)
{
$this->procData = unserialize($data);
$this->initInsertHandles();
}
public static function getFilePath()
{
return __FILE__;
}
// this for keeping word dictionary
public static function getTableName()
{
return 'b_sale_loc_search_word';
}
// this for keeping links between location-and-word-id
public static function getTableNameWord2Location()
{
return 'b_sale_loc_search_w2l';
}
// this for merge, temporal table
public static function getTableNamePositionTemporal()
{
return 'b_sale_loc_search_wt';
}
public function __construct($parameters)
{
$this->resetProcess();
$this->initInsertHandles();
if(is_array($parameters['TYPES']) && !empty($parameters['TYPES']))
{
$this->procData['ALLOWED_TYPES'] = array_unique($parameters['TYPES']);
}
if(is_array($parameters['LANGS']) && !empty($parameters['LANGS']))
{
$this->procData['ALLOWED_LANGS'] = array_unique($parameters['LANGS']);
}
$this->procData['CURRENT_LOCATION'] = false;
$this->procData['CURRENT_LOCATION_WORDS'] = array();
}
public function initInsertHandles()
{
$this->word2LocationInserter = new BlockInserter(array(
'tableName' => static::getTableNameWord2Location(),
'exactFields' => array(
'LOCATION_ID' => array('data_type' => 'integer'),
'WORD_ID' => array('data_type' => 'integer')
),
'parameters' => array(
'mtu' => static::MTU
)
));
$this->dictionaryInserter = new BlockInserter(array(
'entityName' => '\Bitrix\Sale\Location\Search\WordTable',
'exactFields' => array(
'WORD'
),
'parameters' => array(
'mtu' => static::MTU,
'autoIncrementFld' => 'ID',
'CALLBACKS' => array(
'ON_BEFORE_FLUSH' => array($this, 'onBeforeDictionaryFlush')
)
)
));
$this->dictionaryResorter = new BlockInserter(array(
'tableName' => static::getTableNamePositionTemporal(),
'exactFields' => array(
'WORD_ID' => array('data_type' => 'integer'),
'POSITION' => array('data_type' => 'integer')
),
'parameters' => array(
'mtu' => static::MTU
)
));
}
public function resetProcess()
{
$this->procData = array(
'OFFSET' => 0,
'POSITION' => 0,
'CURRENT_LOCATION' => false,
'CURRENT_LOCATION_WORDS' => array()
);
}
public static function cleanUpData()
{
$dbConnection = Main\HttpApplication::getConnection();
Helper::dropTable(static::getTableName());
$binary = ToLower($dbConnection->getType()) == 'mysql' ? 'binary' : ''; // http://bugs.mysql.com/bug.php?id=34096
// ORACE: OK, MSSQL: OK
Main\HttpApplication::getConnection()->query("create table ".static::getTableName()." (
ID ".Helper::getSqlForDataType('int')." not null ".Helper::getSqlForAutoIncrement()." primary key,
WORD ".Helper::getSqlForDataType('varchar', 50)." ".$binary." not null,
POSITION ".Helper::getSqlForDataType('int')." default '0'
)");
Helper::addAutoIncrement(static::getTableName()); // only for ORACLE
Helper::createIndex(static::getTableName(), 'TMP', array('WORD'), true);
Helper::dropTable(static::getTableNameWord2Location());
// ORACLE: OK, MSSQL: OK
Main\HttpApplication::getConnection()->query("create table ".static::getTableNameWord2Location()." (
LOCATION_ID ".Helper::getSqlForDataType('int')." not null,
WORD_ID ".Helper::getSqlForDataType('int')." not null,
primary key (LOCATION_ID, WORD_ID)
)");
Helper::dropTable(static::getTableNamePositionTemporal());
$dbConnection->query("create table ".static::getTableNamePositionTemporal()." (
WORD_ID ".Helper::getSqlForDataType('int')." not null,
POSITION ".Helper::getSqlForDataType('int')." default '0'
)");
}
public static function createIndex()
{
Helper::dropIndexByName('IX_SALE_LOC_SEARCH_WORD_TMP', static::getTableName());
Helper::createIndex(static::getTableName(), 'WP', array('WORD', 'POSITION'));
}
public static function parseWords($words)
{
$result = array();
foreach($words as $k => &$word)
{
$word = ToUpper(trim($word));
$word = str_replace('%', '', $word);
if(!strlen($word))
continue;
$result[] = $word;
}
//natsort($result);
return array_unique($result);
}
public static function parseString($query)
{
$query = ToUpper(Trim($query));
//$query = str_replace(array_keys(static::$blackList), static::$blackList, ' '.$query.' ');
$query = str_replace(array(')', '(', '%', '_'), array('', '', '', ''), $query);
$words = explode(' ', $query);
return self::parseWords($words);
}
public function setOffset($offset)
{
$this->procData['OFFSET'] = $offset;
}
public function getOffset()
{
return $this->procData['OFFSET'];
}
public function setPosition($position)
{
$this->procData['POSITION'] = $position;
}
public function getPosition()
{
return $this->procData['POSITION'];
}
public function onBeforeDictionaryFlush()
{
$this->dictionaryIndex = array();
}
public static function getFilterForInitData($parameters = array())
{
$filter = array();
if(!is_array($parameters))
$parameters = array();
if(is_array($parameters['TYPES']) && !empty($parameters['TYPES']))
$filter['=LOCATION.TYPE_ID'] = array_unique($parameters['TYPES']);
if(is_array($parameters['LANGS']) && !empty($parameters['LANGS']))
$filter['=LANGUAGE_ID'] = array_unique($parameters['LANGS']);
return $filter;
}
public function initializeData()
{
$res = Location\Name\LocationTable::getList(array(
'select' => array(
'NAME',
'LOCATION_ID'
),
'filter' => static::getFilterForInitData(array(
'TYPES' => $this->procData['ALLOWED_TYPES'],
'LANGS' => $this->procData['ALLOWED_LANGS']
)),
'order' => array('LOCATION_ID' => 'asc'), // need to make same location ids stay together
'limit' => static::STEP_SIZE,
'offset' => $this->procData['OFFSET']
));
$cnt = 0;
while($item = $res->fetch())
{
if(strlen($item['NAME']))
{
if($this->procData['CURRENT_LOCATION'] != $item['LOCATION_ID'])
$this->procData['CURRENT_LOCATION_WORDS'] = array();
$this->procData['CURRENT_LOCATION'] = $item['LOCATION_ID'];
$words = static::parseString($item['NAME']);
foreach($words as $k => &$word)
{
$wordHash = md5($word);
$wordId = false;
if(isset($this->dictionaryIndex[$wordHash])) // word is already added and in a hot index
{
$wordId = $this->dictionaryIndex[$wordHash];
}
else
{
$wordId = static::getIdByWord($word); // check if the word was added previously
}
if($wordId === false)
{
$wordId = $this->dictionaryInserter->insert(array(
'WORD' => $word
));
$this->dictionaryIndex[$wordHash] = $wordId;
}
if($wordId !== false && !isset($this->procData['CURRENT_LOCATION_WORDS'][$wordId]))
{
$this->procData['CURRENT_LOCATION_WORDS'][$wordId] = true;
$this->word2LocationInserter->insert(array(
'LOCATION_ID' => intval($item['LOCATION_ID']),
'WORD_ID' => intval($wordId)
));
}
}
}
$cnt++;
}
$this->procData['OFFSET'] += static::STEP_SIZE;
$this->dictionaryInserter->flush();
$this->word2LocationInserter->flush();
return !$cnt;
}
public function resort()
{
$res = Main\HttpApplication::getConnection()->query(
Main\HttpApplication::getConnection()->getSqlHelper()->getTopSql("select ID, WORD from ".static::getTableName()." order by WORD asc, ID asc", self::STEP_SIZE, intval($this->procData['OFFSET']))
);
$cnt = 0;
while($item = $res->fetch())
{
$this->procData['POSITION']++;
$this->dictionaryResorter->insert(array(
'WORD_ID' => $item['ID'],
'POSITION' => $this->procData['POSITION']
));
$cnt++;
}
$this->procData['OFFSET'] += static::STEP_SIZE;
$this->dictionaryResorter->flush();
return !$cnt;
}
public static function mergeResort()
{
Helper::mergeTables(
static::getTableName(),
static::getTableNamePositionTemporal(),
array('POSITION' => 'POSITION'),
array('ID' => 'WORD_ID')
);
Main\HttpApplication::getConnection()->query("drop table ".static::getTableNamePositionTemporal());
}
public static function getIdByWord($word)
{
if(!strlen($word))
return false;
$dbConnection = Main\HttpApplication::getConnection();
$item = $dbConnection->query("select ID from ".static::getTableName()." where WORD = '".$dbConnection->getSqlHelper()->forSql($word)."'")->fetch();
return intval($item['ID']) ? intval($item['ID']) : false;
}
public static function getBoundsByWord($word)
{
$word = trim($word);
$dbConnection = Main\HttpApplication::getConnection();
$sql = "select MIN(POSITION) as INF, MAX(POSITION) as SUP from ".static::getTableName()." where WORD like '".ToUpper($dbConnection->getSqlHelper()->forSql($word))."%'";
return $dbConnection->query($sql)->fetch();
}
public static function getWordsByBounds($inf, $sup)
{
return static::getList(array('filter' => array(
'>=POSITION' => intval($inf),
'<=POSITION' => intval($sup)
), 'order' => array(
'POSITION' => 'asc'
)));
}
public static function getBoundsForPhrase($phrase)
{
if(is_string($phrase))
$words = self::parseString($phrase);
elseif(is_array($phrase))
$words = self::parseWords($phrase);
else
return array();
// check for empty request
$bounds = array();
$sizes = array();
$i = 0;
foreach($words as $word)
{
$bound = self::getBoundsByWord($word);
if(!intval($bound['INF']) && !intval($bound['SUP'])) // no such word
return array();
$bounds[$i] = $bound;
$sizes[] = $bound['SUP'] - $bound['INF'];
$i++;
}
// resort bounds to have sorted smallest to largest
//asort($sizes, SORT_NUMERIC);
$boundsSorted = array();
foreach($sizes as $j => $size)
$boundsSorted[] = $bounds[$j];
// todo: here drop nested intervals, if any
return $boundsSorted;
}
public static function getMap()
{
return array(
'ID' => array(
'data_type' => 'integer',
'primary' => true
),
'WORD' => array(
'data_type' => 'string',
),
'POSITION' => array(
'data_type' => 'integer'
),
'CNT' => array(
'data_type' => 'integer',
'expression' => array(
'count(*)'
)
),
);
}
}