mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-14 13:33:14 +00:00
[SearchEngine] Rename directories
This commit is contained in:
@@ -11,86 +11,37 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Phrasea;
|
namespace Alchemy\Phrasea\SearchEngine\Phrasea;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Silex\Application;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class PhraseaEngine implements SearchEngineInterface
|
class PhraseaEngine implements SearchEngineInterface
|
||||||
{
|
{
|
||||||
private $initialized;
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @var SearchEngineOptions
|
* @var SearchEngineOptions
|
||||||
*/
|
*/
|
||||||
private $options;
|
protected $options;
|
||||||
private $app;
|
protected $queries = array();
|
||||||
private $queries = array();
|
protected $arrayq = array();
|
||||||
private $arrayq = array();
|
protected $colls = array();
|
||||||
private $colls = array();
|
protected $qp = array();
|
||||||
private $qp = array();
|
protected $needthesaurus = array();
|
||||||
private $needthesaurus = array();
|
protected $configurationPanel;
|
||||||
private $configurationPanel;
|
protected $resetCacheNextQuery = false;
|
||||||
private $resetCacheNextQuery = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function __construct(Application $app)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
|
||||||
$this->options = new SearchEngineOptions();
|
$this->options = new SearchEngineOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize()
|
|
||||||
{
|
|
||||||
if ($this->initialized) {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
$choosenConnexion = $this->app['phraseanet.configuration']->getPhraseanet()->get('database');
|
|
||||||
|
|
||||||
$connexion = $this->app['phraseanet.configuration']->getConnexion($choosenConnexion);
|
|
||||||
|
|
||||||
$hostname = $connexion->get('host');
|
|
||||||
$port = (int) $connexion->get('port');
|
|
||||||
$user = $connexion->get('user');
|
|
||||||
$password = $connexion->get('password');
|
|
||||||
$dbname = $connexion->get('dbname');
|
|
||||||
|
|
||||||
if (!extension_loaded('phrasea2')) {
|
|
||||||
throw new RuntimeException('Phrasea extension is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('phrasea_conn')) {
|
|
||||||
throw new RuntimeException('Phrasea extension requires upgrade');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phrasea_conn($hostname, $port, $user, $password, $dbname) !== true) {
|
|
||||||
throw new RuntimeException('Unable to initialize Phrasea connection');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->initialized = true;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkSession()
|
|
||||||
{
|
|
||||||
if (!$this->app['phraseanet.user']) {
|
|
||||||
throw new \RuntimeException('Phrasea currently support only authenticated queries');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!phrasea_open_session($this->app['session']->get('phrasea_session_id'), $this->app['phraseanet.user']->get_id())) {
|
|
||||||
if (!$ses_id = phrasea_create_session((string) $this->app['phraseanet.user']->get_id())) {
|
|
||||||
throw new \Exception_InternalServerError('Unable to create phrasea session');
|
|
||||||
}
|
|
||||||
$this->app['session']->set('phrasea_session_id', $ses_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@@ -104,15 +55,26 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configurationPanel()
|
public function getConfigurationPanel(Application $app, Request $request)
|
||||||
{
|
{
|
||||||
if (!$this->configurationPanel) {
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postConfigurationPanel(Application $app, Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configurationPanel()
|
||||||
|
{
|
||||||
|
if ( ! $this->configurationPanel) {
|
||||||
$this->configurationPanel = new ConfigurationPanel($this);
|
$this->configurationPanel = new ConfigurationPanel($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->configurationPanel;
|
return $this->configurationPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@@ -161,7 +123,7 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
*/
|
*/
|
||||||
public function updateRecord(\record_adapter $record)
|
public function updateRecord(\record_adapter $record)
|
||||||
{
|
{
|
||||||
$record->set_binary_status(\databox_status::dec2bin($this->app, bindec($record->get_status()) & ~7 | 4));
|
$record->set_binary_status(\databox_status::dec2bin(bindec($record->get_status()) & ~7 | 4));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -235,9 +197,6 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
*/
|
*/
|
||||||
public function query($query, $offset, $perPage)
|
public function query($query, $offset, $perPage)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
|
||||||
$this->checkSession();
|
|
||||||
|
|
||||||
assert(is_int($offset));
|
assert(is_int($offset));
|
||||||
assert($offset >= 0);
|
assert($offset >= 0);
|
||||||
assert(is_int($perPage));
|
assert(is_int($perPage));
|
||||||
@@ -250,9 +209,12 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
$query .= ' AND recordtype=' . $this->options->getRecordType();
|
$query .= ' AND recordtype=' . $this->options->getRecordType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
||||||
|
$session = $appbox->get_session();
|
||||||
|
|
||||||
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
||||||
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
|
$stmt = $appbox->get_connection()->prepare($sql);
|
||||||
$stmt->execute(array(':ses_id' => $this->app['session']->get('phrasea_session_id')));
|
$stmt->execute(array(':ses_id' => $session->get_ses_id()));
|
||||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
|
|
||||||
@@ -267,24 +229,24 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->resetCacheNextQuery === true) {
|
if ($this->resetCacheNextQuery === true) {
|
||||||
phrasea_clear_cache($this->app['session']->get('phrasea_session_id'));
|
phrasea_clear_cache($session->get_ses_id());
|
||||||
$this->addQuery($query);
|
$this->addQuery($query);
|
||||||
$this->executeQuery($query);
|
$this->executeQuery($query);
|
||||||
|
|
||||||
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
||||||
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
|
$stmt = $appbox->get_connection()->prepare($sql);
|
||||||
$stmt->execute(array(':ses_id' => $this->app['session']->get('phrasea_session_id')));
|
$stmt->execute(array(':ses_id' => $session->get_ses_id()));
|
||||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* @todo clean this in DB
|
* @todo clean this in DB
|
||||||
*/
|
*/
|
||||||
$this->total_available = $this->total_results = $this->app['session']->get('phrasea_engine_n_results');
|
$this->total_available = $this->total_results = $session->get_session_prefs('phrasea_engine_n_results');
|
||||||
}
|
}
|
||||||
|
|
||||||
$res = phrasea_fetch_results(
|
$res = phrasea_fetch_results(
|
||||||
$this->app['session']->get('phrasea_session_id'), $offset + 1, $perPage, false
|
$session->get_ses_id(), $offset + 1, $perPage, false
|
||||||
);
|
);
|
||||||
|
|
||||||
$rs = array();
|
$rs = array();
|
||||||
@@ -301,31 +263,29 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
foreach ($rs as $data) {
|
foreach ($rs as $data) {
|
||||||
try {
|
try {
|
||||||
$records->add(new \record_adapter(
|
$records->add(new \record_adapter(
|
||||||
$this->app,
|
\phrasea::sbasFromBas($data['base_id']),
|
||||||
\phrasea::sbasFromBas($this->app, $data['base_id']),
|
|
||||||
$data['record_id'],
|
$data['record_id'],
|
||||||
$resultNumber
|
$resultNumber
|
||||||
));
|
));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
$resultNumber++;
|
$resultNumber ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return new SearchEngineResult($records, $query, $row['duration'], $offset, $row['total'], $row['total'], $error, '', new ArrayCollection(), new ArrayCollection(), '');
|
return new SearchEngineResult($records, $query, $row['duration'], $offset, $row['total'], $row['total'], $error, '', new ArrayCollection(), new ArrayCollection(), '');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function create(Application $app)
|
|
||||||
{
|
|
||||||
return new static($app);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
private function executeQuery($query)
|
private function executeQuery($query)
|
||||||
{
|
{
|
||||||
|
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
||||||
|
$session = $appbox->get_session();
|
||||||
|
$registry = $appbox->get_registry();
|
||||||
|
|
||||||
$dateLog = date("Y-m-d H:i:s");
|
$dateLog = date("Y-m-d H:i:s");
|
||||||
$nbanswers = $total_time = 0;
|
$nbanswers = $total_time = 0;
|
||||||
$sort = '';
|
$sort = '';
|
||||||
@@ -351,12 +311,12 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$results = phrasea_query2(
|
$results = phrasea_query2(
|
||||||
$this->app['session']->get('phrasea_session_id')
|
$session->get_ses_id()
|
||||||
, $sbas_id
|
, $sbas_id
|
||||||
, $this->colls[$sbas_id]
|
, $this->colls[$sbas_id]
|
||||||
, $this->arrayq[$sbas_id]
|
, $this->arrayq[$sbas_id]
|
||||||
, $this->app['phraseanet.registry']->get('GV_sit')
|
, $registry->get('GV_sit')
|
||||||
, $this->app['session']->get('usr_id')
|
, (string) $session->get_usr_id()
|
||||||
, false
|
, false
|
||||||
, $this->options->searchType() == SearchEngineOptions::RECORD_GROUPING ? PHRASEA_MULTIDOC_REGONLY : PHRASEA_MULTIDOC_DOCONLY
|
, $this->options->searchType() == SearchEngineOptions::RECORD_GROUPING ? PHRASEA_MULTIDOC_REGONLY : PHRASEA_MULTIDOC_DOCONLY
|
||||||
, $sort
|
, $sort
|
||||||
@@ -368,26 +328,26 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
$nbanswers += $results["nbanswers"];
|
$nbanswers += $results["nbanswers"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// $logger = $session->get_logger($this->appbox->get_databox($sbas_id));
|
$logger = $session->get_logger($appbox->get_databox($sbas_id));
|
||||||
//
|
|
||||||
// $conn2 = \connection::getPDOConnection($sbas_id);
|
$conn2 = \connection::getPDOConnection($sbas_id);
|
||||||
//
|
|
||||||
// $sql3 = "INSERT INTO log_search
|
$sql3 = "INSERT INTO log_search
|
||||||
// (id, log_id, date, search, results, coll_id )
|
(id, log_id, date, search, results, coll_id )
|
||||||
// VALUES
|
VALUES
|
||||||
// (null, :log_id, :date, :query, :nbresults, :colls)";
|
(null, :log_id, :date, :query, :nbresults, :colls)";
|
||||||
//
|
|
||||||
// $params = array(
|
$params = array(
|
||||||
// ':log_id' => $logger->get_id()
|
':log_id' => $logger->get_id()
|
||||||
// , ':date' => $dateLog
|
, ':date' => $dateLog
|
||||||
// , ':query' => $query
|
, ':query' => $query
|
||||||
// , ':nbresults' => $results["nbanswers"]
|
, ':nbresults' => $results["nbanswers"]
|
||||||
// , ':colls' => implode(',', $this->colls[$sbas_id])
|
, ':colls' => implode(',', $this->colls[$sbas_id])
|
||||||
// );
|
);
|
||||||
//
|
|
||||||
// $stmt = $conn2->prepare($sql3);
|
$stmt = $conn2->prepare($sql3);
|
||||||
// $stmt->execute($params);
|
$stmt->execute($params);
|
||||||
// $stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = 'UPDATE cache
|
$sql = 'UPDATE cache
|
||||||
@@ -396,18 +356,16 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
$params = array(
|
$params = array(
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
':ses_id' => $this->app['session']->get('phrasea_session_id'),
|
':ses_id' => $session->get_ses_id(),
|
||||||
':duration' => $total_time,
|
':duration' => $total_time,
|
||||||
':total' => $nbanswers,
|
':total' => $nbanswers,
|
||||||
);
|
);
|
||||||
|
|
||||||
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
|
$stmt = $appbox->get_connection()->prepare($sql);
|
||||||
$stmt->execute($params);
|
$stmt->execute($params);
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
|
|
||||||
if ($this->app['phraseanet.user']) {
|
\User_Adapter::saveQuery($query);
|
||||||
\User_Adapter::saveQuery($this->app, $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -427,17 +385,19 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
{
|
{
|
||||||
$ret = array();
|
$ret = array();
|
||||||
|
|
||||||
|
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
||||||
|
$session = $appbox->get_session();
|
||||||
$res = phrasea_fetch_results(
|
$res = phrasea_fetch_results(
|
||||||
$this->app['session']->get('phrasea_session_id'), ($record->get_number() + 1), 1, true, "[[em]]", "[[/em]]"
|
$session->get_ses_id(), ($record->get_number() + 1), 1, true, "[[em]]", "[[/em]]"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isset($res['results']) || !is_array($res['results'])) {
|
if ( ! isset($res['results']) || ! is_array($res['results'])) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$rs = $res['results'];
|
$rs = $res['results'];
|
||||||
$res = array_shift($rs);
|
$res = array_shift($rs);
|
||||||
if (!isset($res['xml'])) {
|
if ( ! isset($res['xml'])) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,8 +444,8 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
if ($status) {
|
if ($status) {
|
||||||
$requestStat = 'xxxx';
|
$requestStat = 'xxxx';
|
||||||
|
|
||||||
for ($i = 4; ($i <= 32); $i++) {
|
for ($i = 4; ($i <= 64); $i ++ ) {
|
||||||
if (!isset($status[$i])) {
|
if ( ! isset($status[$i])) {
|
||||||
$requestStat = 'x' . $requestStat;
|
$requestStat = 'x' . $requestStat;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -507,9 +467,7 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->options->fields()) {
|
if ($this->options->fields()) {
|
||||||
$this->queries[$sbas] .= ' IN (' . implode(' OR ', array_map(function(\databox_field $field) {
|
$this->queries[$sbas] .= ' IN (' . implode(' OR ', $this->options->fields()) . ')';
|
||||||
return $field->get_name();
|
|
||||||
}, $this->options->fields())) . ')';
|
|
||||||
}
|
}
|
||||||
if (($this->options->getMinDate() || $this->options->getMaxDate()) && $this->options->getDateFields()) {
|
if (($this->options->getMinDate() || $this->options->getMaxDate()) && $this->options->getDateFields()) {
|
||||||
if ($this->options->getMinDate()) {
|
if ($this->options->getMinDate()) {
|
||||||
@@ -568,7 +526,7 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
private function singleParse($sbas, $query)
|
private function singleParse($sbas, $query)
|
||||||
{
|
{
|
||||||
$this->qp[$sbas] = new PhraseaEngineQueryParser($this->app, $this->options->getLocale());
|
$this->qp[$sbas] = new PhraseaEngineQueryParser($this->options->getLocale());
|
||||||
$this->qp[$sbas]->debug = false;
|
$this->qp[$sbas]->debug = false;
|
||||||
|
|
||||||
if ($sbas == 'main') {
|
if ($sbas == 'main') {
|
||||||
@@ -586,39 +544,5 @@ class PhraseaEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function clearCache()
|
|
||||||
{
|
|
||||||
if ($this->app['session']->has('phrasea_session_id')) {
|
|
||||||
$this->initialize();
|
|
||||||
phrasea_close_session($this->app['session']->get('phrasea_session_id'));
|
|
||||||
$this->app['session']->remove('phrasea_session_id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function clearAllCache(\DateTime $date = null)
|
|
||||||
{
|
|
||||||
if (!$date) {
|
|
||||||
$date = new \DateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = "SELECT session_id FROM cache WHERE lastaccess <= :date";
|
|
||||||
|
|
||||||
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute(array(':date' => $date->format(DATE_ISO8601)));
|
|
||||||
$rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
foreach ($rs as $row) {
|
|
||||||
phrasea_close_session($row['session_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Phrasea;
|
namespace Alchemy\Phrasea\SearchEngine\Phrasea;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @package searchEngine
|
* @package searchEngine
|
||||||
@@ -86,17 +84,15 @@ class PhraseaEngineQueryParser
|
|||||||
*/
|
*/
|
||||||
public $proposals = Array("QRY" => "", "BASES" => array(), "QUERIES" => array());
|
public $proposals = Array("QRY" => "", "BASES" => array(), "QUERIES" => array());
|
||||||
|
|
||||||
public $app;
|
|
||||||
/**
|
/**
|
||||||
* Current language for thesaurus
|
* Current language for thesaurus
|
||||||
* @var <type>
|
* @var <type>
|
||||||
*/
|
*/
|
||||||
public $lng;
|
public $lng = null;
|
||||||
protected $unicode;
|
protected $unicode;
|
||||||
|
|
||||||
public function __construct(Application $app, $lng = "???")
|
public function __construct($lng = "???")
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
|
||||||
$this->lng = $lng;
|
$this->lng = $lng;
|
||||||
$this->unicode = new \unicode();
|
$this->unicode = new \unicode();
|
||||||
|
|
||||||
@@ -116,7 +112,7 @@ class PhraseaEngineQueryParser
|
|||||||
public function parsequery($phq)
|
public function parsequery($phq)
|
||||||
{
|
{
|
||||||
if ($this->debug) {
|
if ($this->debug) {
|
||||||
for ($i = 0; $i < mb_strlen($phq, 'UTF-8'); $i++) {
|
for ($i = 0; $i < mb_strlen($phq, 'UTF-8'); $i ++ ) {
|
||||||
$c = mb_substr($phq, $i, 1, 'UTF-8');
|
$c = mb_substr($phq, $i, 1, 'UTF-8');
|
||||||
printf("// %s : '%s' (%d octets)\n", $i, $c, strlen($c));
|
printf("// %s : '%s' (%d octets)\n", $i, $c, strlen($c));
|
||||||
}
|
}
|
||||||
@@ -353,7 +349,7 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
public function priority_opk(&$tree, $depth = 0)
|
public function priority_opk(&$tree, $depth = 0)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +369,7 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
public function distrib_opk(&$tree, $depth = 0)
|
public function distrib_opk(&$tree, $depth = 0)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +396,7 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
public function thesaurus2_apply(&$tree, $bid)
|
public function thesaurus2_apply(&$tree, $bid)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +455,7 @@ class PhraseaEngineQueryParser
|
|||||||
{
|
{
|
||||||
if ($depth == 0)
|
if ($depth == 0)
|
||||||
$ret = $tree;
|
$ret = $tree;
|
||||||
if (!$useThesaurus) {
|
if ( ! $useThesaurus) {
|
||||||
return; // full-text only : inchangé
|
return; // full-text only : inchangé
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +471,7 @@ class PhraseaEngineQueryParser
|
|||||||
if (isset($tree["RB"]["CONTEXT"]))
|
if (isset($tree["RB"]["CONTEXT"]))
|
||||||
$copy["CONTEXT"] = $tree["CONTEXT"] = $tree["RB"]["CONTEXT"];
|
$copy["CONTEXT"] = $tree["CONTEXT"] = $tree["RB"]["CONTEXT"];
|
||||||
else
|
else
|
||||||
if (!$keepfuzzy)
|
if ( ! $keepfuzzy)
|
||||||
$copy["CONTEXT"] = $tree["CONTEXT"] = "*";
|
$copy["CONTEXT"] = $tree["CONTEXT"] = "*";
|
||||||
|
|
||||||
$copy["RB"]["SREF"] = &$tree["RB"];
|
$copy["RB"]["SREF"] = &$tree["RB"];
|
||||||
@@ -536,7 +532,7 @@ class PhraseaEngineQueryParser
|
|||||||
if ($context !== null)
|
if ($context !== null)
|
||||||
$tmp["RB"]["CONTEXT"] = $context;
|
$tmp["RB"]["CONTEXT"] = $context;
|
||||||
else
|
else
|
||||||
if (!$keepfuzzy)
|
if ( ! $keepfuzzy)
|
||||||
$tmp["RB"]["CONTEXT"] = "*";
|
$tmp["RB"]["CONTEXT"] = "*";
|
||||||
// corrige les profondeurs des 2 copies du 'simple' d'origine
|
// corrige les profondeurs des 2 copies du 'simple' d'origine
|
||||||
$tmp["LB"]["DEPTH"] += 1;
|
$tmp["LB"]["DEPTH"] += 1;
|
||||||
@@ -563,7 +559,7 @@ class PhraseaEngineQueryParser
|
|||||||
if ($context !== null)
|
if ($context !== null)
|
||||||
$tmp["CONTEXT"] = $context;
|
$tmp["CONTEXT"] = $context;
|
||||||
else
|
else
|
||||||
if (!$keepfuzzy)
|
if ( ! $keepfuzzy)
|
||||||
$tmp["CONTEXT"] = "*";
|
$tmp["CONTEXT"] = "*";
|
||||||
// corrige la profondeur de la copie du 'simple' d'origine
|
// corrige la profondeur de la copie du 'simple' d'origine
|
||||||
$tmp["RB"]["DEPTH"] += 1;
|
$tmp["RB"]["DEPTH"] += 1;
|
||||||
@@ -583,7 +579,7 @@ class PhraseaEngineQueryParser
|
|||||||
if ($depth == 0)
|
if ($depth == 0)
|
||||||
$this->proposals["BASES"]["b$bid"] = array("BID" => $bid, "NAME" => $name, "TERMS" => array());
|
$this->proposals["BASES"]["b$bid"] = array("BID" => $bid, "NAME" => $name, "TERMS" => array());
|
||||||
|
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +604,7 @@ class PhraseaEngineQueryParser
|
|||||||
for ($n = $node->firstChild; $n; $n = $n->nextSibling) {
|
for ($n = $node->firstChild; $n; $n = $n->nextSibling) {
|
||||||
if ($n->nodeName == "sy") {
|
if ($n->nodeName == "sy") {
|
||||||
$lng = $n->getAttribute("lng");
|
$lng = $n->getAttribute("lng");
|
||||||
if (!array_key_exists($lng, $tsy))
|
if ( ! array_key_exists($lng, $tsy))
|
||||||
$tsy[$lng] = array();
|
$tsy[$lng] = array();
|
||||||
$zsy = array("v" => $n->getAttribute("v"), "w" => $n->getAttribute("w"), "k" => $n->getAttribute("k"));
|
$zsy = array("v" => $n->getAttribute("v"), "w" => $n->getAttribute("w"), "k" => $n->getAttribute("k"));
|
||||||
|
|
||||||
@@ -625,15 +621,15 @@ class PhraseaEngineQueryParser
|
|||||||
foreach ($tsy as $lng => $tsy2) {
|
foreach ($tsy as $lng => $tsy2) {
|
||||||
foreach ($tsy2 as $sy) {
|
foreach ($tsy2 as $sy) {
|
||||||
$alt .= $alt ? "\n" : "";
|
$alt .= $alt ? "\n" : "";
|
||||||
$alt .= "" . $lng . ": " . \p4string::MakeString($sy["v"], "js");
|
$alt .= "" . $lng . ": " . p4string::MakeString($sy["v"], "js");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->proposals['QUERIES'][$syfound["w"]] = $syfound["w"];
|
$this->proposals['QUERIES'][$syfound["w"]] = $syfound["w"];
|
||||||
|
|
||||||
$thtml = $syfound["v"];
|
$thtml = $syfound["v"];
|
||||||
$kjs = $syfound["k"] ? ("'" . \p4string::MakeString($syfound["k"], "js") . "'") : "null";
|
$kjs = $syfound["k"] ? ("'" . p4string::MakeString($syfound["k"], "js") . "'") : "null";
|
||||||
$wjs = "'" . \p4string::MakeString($syfound["w"], "js") . "'";
|
$wjs = "'" . p4string::MakeString($syfound["w"], "js") . "'";
|
||||||
|
|
||||||
if ($node->getAttribute("term")) {
|
if ($node->getAttribute("term")) {
|
||||||
$thtml = "<b>" . $thtml . "</b>";
|
$thtml = "<b>" . $thtml . "</b>";
|
||||||
@@ -660,7 +656,7 @@ class PhraseaEngineQueryParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$n->removeAttribute("marked");
|
$n->removeAttribute("marked");
|
||||||
for ($i = 0; array_key_exists($syfound . $i, $tsort) && $i < 9999; $i++)
|
for ($i = 0; array_key_exists($syfound . $i, $tsort) && $i < 9999; $i ++ )
|
||||||
;
|
;
|
||||||
$tsort[$syfound . $i] = $n;
|
$tsort[$syfound . $i] = $n;
|
||||||
}
|
}
|
||||||
@@ -733,7 +729,7 @@ class PhraseaEngineQueryParser
|
|||||||
$t = $w = implode(" ", $w);
|
$t = $w = implode(" ", $w);
|
||||||
|
|
||||||
if (isset($tree["CONTEXT"])) {
|
if (isset($tree["CONTEXT"])) {
|
||||||
if (!$tree["CONTEXT"]) {
|
if ( ! $tree["CONTEXT"]) {
|
||||||
$x0 = "@w=\"" . $w . "\" and not(@k)";
|
$x0 = "@w=\"" . $w . "\" and not(@k)";
|
||||||
} else {
|
} else {
|
||||||
if ($tree["CONTEXT"] == "*") {
|
if ($tree["CONTEXT"] == "*") {
|
||||||
@@ -752,10 +748,10 @@ class PhraseaEngineQueryParser
|
|||||||
if ($this->debug)
|
if ($this->debug)
|
||||||
printf("searching thesaurus with xpath='%s'<br/>\n", $x);
|
printf("searching thesaurus with xpath='%s'<br/>\n", $x);
|
||||||
|
|
||||||
$dxp = new \DOMXPath($domthe);
|
$dxp = new DOMXPath($domthe);
|
||||||
$nodes = $dxp->query($x);
|
$nodes = $dxp->query($x);
|
||||||
|
|
||||||
if (!isset($tree["RB"]["SREF"]["TIDS"]))
|
if ( ! isset($tree["RB"]["SREF"]["TIDS"]))
|
||||||
$tree["RB"]["SREF"]["TIDS"] = array();
|
$tree["RB"]["SREF"]["TIDS"] = array();
|
||||||
if ($nodes->length >= 1) {
|
if ($nodes->length >= 1) {
|
||||||
if ($nodes->length == 1) {
|
if ($nodes->length == 1) {
|
||||||
@@ -766,8 +762,8 @@ class PhraseaEngineQueryParser
|
|||||||
// on cherche plusieurs id's, on utilisera la syntaxe 'regexp' (l'extension repérera elle meme la syntaxe car la value finira par '$')
|
// on cherche plusieurs id's, on utilisera la syntaxe 'regexp' (l'extension repérera elle meme la syntaxe car la value finira par '$')
|
||||||
$val = "";
|
$val = "";
|
||||||
foreach ($nodes as $node) {
|
foreach ($nodes as $node) {
|
||||||
if (!isset($tree["CONTEXT"]))
|
if ( ! isset($tree["CONTEXT"]))
|
||||||
$ambigus++;
|
$ambigus ++;
|
||||||
$this->addtoTIDS($tree["RB"], $bid, $node);
|
$this->addtoTIDS($tree["RB"], $bid, $node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,15 +773,74 @@ class PhraseaEngineQueryParser
|
|||||||
$this->proposals["BASES"]["b$bid"]["TERMS"][$path]["HTML"] = $prophtml;
|
$this->proposals["BASES"]["b$bid"]["TERMS"][$path]["HTML"] = $prophtml;
|
||||||
} else {
|
} else {
|
||||||
// le mot n'est pas dans le thesaurus
|
// le mot n'est pas dans le thesaurus
|
||||||
$tree = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return($ambigus);
|
return($ambigus);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
function dead_setTids(&$tree, &$simple, $bid, &$domthe, $searchsynonyms)
|
||||||
|
{
|
||||||
|
// if($this->debug)
|
||||||
|
print("setTids:\n\$tree=" . var_export($tree, true) . "\n");
|
||||||
|
|
||||||
|
$ambigus = 0;
|
||||||
|
if(is_array($w = $simple["VALUE"]))
|
||||||
|
$t = $w = implode(" ", $w);
|
||||||
|
|
||||||
|
if (isset($tree["CONTEXT"])) {
|
||||||
|
if (!$tree["CONTEXT"]) {
|
||||||
|
$x0 = "@w=\"" . $w ."\" and not(@k)";
|
||||||
|
} else {
|
||||||
|
if ($tree["CONTEXT"]=="*") {
|
||||||
|
$x0 = "@w=\"" . $w ."\"";
|
||||||
|
} else {
|
||||||
|
$x0 = "@w=\"" . $w ."\" and @k=\"" . $tree["CONTEXT"] . "\"";
|
||||||
|
$t .= " (" . $tree["CONTEXT"] . ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$x0 = "@w=\"" . $w ."\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = "/thesaurus//sy[" . $x0 ."]";
|
||||||
|
|
||||||
|
if($this->debug)
|
||||||
|
printf("searching thesaurus with xpath='%s'<br/>\n", $x);
|
||||||
|
|
||||||
|
$dxp = new DOMXPath($domthe);
|
||||||
|
$nodes = $dxp->query($x);
|
||||||
|
|
||||||
|
if(!isset($tree["RB"]["SREF"]["TIDS"]))
|
||||||
|
$tree["RB"]["SREF"]["TIDS"] = array();
|
||||||
|
if ($nodes->length >= 1) {
|
||||||
|
if ($nodes->length == 1) {
|
||||||
|
// on cherche un id simple, on utilisera la syntaxe sql 'like' (l'extension repérera elle méme la syntaxe car la value finira par '%')
|
||||||
|
$this->addtoTIDS($tree["RB"], $bid, $nodes->item(0));
|
||||||
|
// $this->thesaurusDOMNodes[] = $nodes->item(0);
|
||||||
|
} else {
|
||||||
|
// on cherche plusieurs id's, on utilisera la syntaxe 'regexp' (l'extension repérera elle meme la syntaxe car la value finira par '$')
|
||||||
|
$val = "";
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if(!isset($tree["CONTEXT"]))
|
||||||
|
$ambigus++;
|
||||||
|
$this->addtoTIDS($tree["RB"], $bid, $node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$path = $tree["RB"]["SREF"]["PATH"];
|
||||||
|
$prophtml = "";
|
||||||
|
$this->propAsHTML($domthe->documentElement, $prophtml, $path);
|
||||||
|
$this->proposals["TERMS"][$path]["HTML"] = $prophtml;
|
||||||
|
} else {
|
||||||
|
// le mot n'est pas dans le thesaurus
|
||||||
|
}
|
||||||
|
|
||||||
|
return($ambigus);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public function containsColonOperator(&$tree)
|
public function containsColonOperator(&$tree)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
if ($tree["CLASS"] == "OPK" && $tree["NODETYPE"] == PHRASEA_OP_COLON && ($tree["RB"]["CLASS"] == "SIMPLE" || $tree["RB"]["CLASS"] == "QSIMPLE")) {
|
if ($tree["CLASS"] == "OPK" && $tree["NODETYPE"] == PHRASEA_OP_COLON && ($tree["RB"]["CLASS"] == "SIMPLE" || $tree["RB"]["CLASS"] == "QSIMPLE")) {
|
||||||
@@ -813,7 +868,7 @@ class PhraseaEngineQueryParser
|
|||||||
if ($this->debug)
|
if ($this->debug)
|
||||||
printf("found node id='%s', v='%s' w='%s', k='%s', p='%s' for node-path=%s \n", $id, $DOMnode->getAttribute("v"), $w, $k, $p, $path);
|
printf("found node id='%s', v='%s' w='%s', k='%s', p='%s' for node-path=%s \n", $id, $DOMnode->getAttribute("v"), $w, $k, $p, $path);
|
||||||
|
|
||||||
if (!$k)
|
if ( ! $k)
|
||||||
$k = null;
|
$k = null;
|
||||||
|
|
||||||
$found = false;
|
$found = false;
|
||||||
@@ -832,11 +887,11 @@ class PhraseaEngineQueryParser
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$found)
|
if ( ! $found)
|
||||||
$extendednode["SREF"]["TIDS"][] = array("bid" => $bid, "pid" => $pid, "id" => $id, "w" => $w, "k" => $k, "lng" => $lng, "p" => $p);
|
$extendednode["SREF"]["TIDS"][] = array("bid" => $bid, "pid" => $pid, "id" => $id, "w" => $w, "k" => $k, "lng" => $lng, "p" => $p);
|
||||||
|
|
||||||
// on liste les propositions de thésaurus pour ce node (dans l'arbre simple)
|
// on liste les propositions de thésaurus pour ce node (dans l'arbre simple)
|
||||||
if (!isset($this->proposals["BASES"]["b$bid"]["TERMS"][$path])) {
|
if ( ! isset($this->proposals["BASES"]["b$bid"]["TERMS"][$path])) {
|
||||||
// $this->proposals["TERMS"][$path] = array("TERM"=>implode(" ", $extendednode["VALUE"]), "PROPOSALS"=>array());
|
// $this->proposals["TERMS"][$path] = array("TERM"=>implode(" ", $extendednode["VALUE"]), "PROPOSALS"=>array());
|
||||||
$term = implode(" ", $extendednode["VALUE"]);
|
$term = implode(" ", $extendednode["VALUE"]);
|
||||||
if (isset($extendednode["CONTEXT"]) && $extendednode["CONTEXT"]) {
|
if (isset($extendednode["CONTEXT"]) && $extendednode["CONTEXT"]) {
|
||||||
@@ -857,7 +912,7 @@ class PhraseaEngineQueryParser
|
|||||||
// puis par remonter au père
|
// puis par remonter au père
|
||||||
for ($node = $DOMnode->parentNode; $node && $node->nodeType == XML_ELEMENT_NODE && $node->parentNode; $node = $node->parentNode) {
|
for ($node = $DOMnode->parentNode; $node && $node->nodeType == XML_ELEMENT_NODE && $node->parentNode; $node = $node->parentNode) {
|
||||||
$id = $node->getAttribute("id");
|
$id = $node->getAttribute("id");
|
||||||
if (!$id)
|
if ( ! $id)
|
||||||
break; // on a dépassé la racine du thésaurus
|
break; // on a dépassé la racine du thésaurus
|
||||||
$node->setAttribute("marked", "1");
|
$node->setAttribute("marked", "1");
|
||||||
}
|
}
|
||||||
@@ -880,7 +935,7 @@ class PhraseaEngineQueryParser
|
|||||||
$txt .= $tab . "\t\"" . $w . "";
|
$txt .= $tab . "\t\"" . $w . "";
|
||||||
$txt .= $tab . "\t<span id='thamb_w_" . $ambiguites["n"] . "'></span>\"";
|
$txt .= $tab . "\t<span id='thamb_w_" . $ambiguites["n"] . "'></span>\"";
|
||||||
$txt .= $tab . "</span></b>\n";
|
$txt .= $tab . "</span></b>\n";
|
||||||
$ambiguites["n"]++;
|
$ambiguites["n"] ++;
|
||||||
} else {
|
} else {
|
||||||
if (isset($tree["CONTEXT"]))
|
if (isset($tree["CONTEXT"]))
|
||||||
$w .= "[" . $tree["CONTEXT"] . "]";
|
$w .= "[" . $tree["CONTEXT"] . "]";
|
||||||
@@ -915,7 +970,7 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
public function get_ambigu(&$tree, $mouseCallback = "void", $depth = 0)
|
public function get_ambigu(&$tree, $mouseCallback = "void", $depth = 0)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return("");
|
return("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -937,22 +992,22 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
public function set_default(&$tree, &$emptyw, $depth = 0)
|
public function set_default(&$tree, &$emptyw, $depth = 0)
|
||||||
{
|
{
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") {
|
if ($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") {
|
||||||
if ($tree["CLASS"] == "OPS") {
|
if ($tree["CLASS"] == "OPS") {
|
||||||
if (!$this->set_default($tree["LB"], $emptyw, $depth + 1)) {
|
if ( ! $this->set_default($tree["LB"], $emptyw, $depth + 1)) {
|
||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
if (!$this->set_default($tree["RB"], $emptyw, $depth + 1)) {
|
if ( ! $this->set_default($tree["RB"], $emptyw, $depth + 1)) {
|
||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
} else { // OPK !
|
} else { // OPK !
|
||||||
// jy 20041223 : ne pas appliquer d'op. par def. derriere un op arith.
|
// jy 20041223 : ne pas appliquer d'op. par def. derriere un op arith.
|
||||||
// ex : "d < 1/2/2003" : grouper la liste "1","2","2004" en "mot" unique
|
// ex : "d < 1/2/2003" : grouper la liste "1","2","2004" en "mot" unique
|
||||||
if (!$tree["LB"] || ($tree["LB"]["CLASS"] != "SIMPLE" && $tree["LB"]["CLASS"] != "QSIMPLE") || (is_array($tree["LB"]["VALUE"]) && count($tree["LB"]["VALUE"]) != 1)) {
|
if ( ! $tree["LB"] || ($tree["LB"]["CLASS"] != "SIMPLE" && $tree["LB"]["CLASS"] != "QSIMPLE") || (is_array($tree["LB"]["VALUE"]) && count($tree["LB"]["VALUE"]) != 1)) {
|
||||||
// un op. arith. doit étre précédé d'un seul nom de champ
|
// un op. arith. doit étre précédé d'un seul nom de champ
|
||||||
if ($this->errmsg != "")
|
if ($this->errmsg != "")
|
||||||
$this->errmsg .= sprintf("\\n");
|
$this->errmsg .= sprintf("\\n");
|
||||||
@@ -960,7 +1015,7 @@ class PhraseaEngineQueryParser
|
|||||||
|
|
||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
if (!$tree["RB"] || ($tree["RB"]["CLASS"] != "SIMPLE" && $tree["RB"]["CLASS"] != "QSIMPLE")) {
|
if ( ! $tree["RB"] || ($tree["RB"]["CLASS"] != "SIMPLE" && $tree["RB"]["CLASS"] != "QSIMPLE")) {
|
||||||
// un op. arith. doit étre suivi d'une valeur
|
// un op. arith. doit étre suivi d'une valeur
|
||||||
if ($this->errmsg != "")
|
if ($this->errmsg != "")
|
||||||
$this->errmsg .= sprintf("\\n");
|
$this->errmsg .= sprintf("\\n");
|
||||||
@@ -979,21 +1034,21 @@ class PhraseaEngineQueryParser
|
|||||||
/** gestion des branches null
|
/** gestion des branches null
|
||||||
* a revoir car ca ppete pas d'erreur mais corrige automatiquement
|
* a revoir car ca ppete pas d'erreur mais corrige automatiquement
|
||||||
* ** */
|
* ** */
|
||||||
if (!isset($tree["RB"]))
|
if ( ! isset($tree["RB"]))
|
||||||
$tree = $tree["LB"];
|
$tree = $tree["LB"];
|
||||||
else
|
else
|
||||||
if (!isset($tree["LB"]))
|
if ( ! isset($tree["LB"]))
|
||||||
$tree = $tree["RB"];
|
$tree = $tree["RB"];
|
||||||
} else {
|
} else {
|
||||||
if (($tree["CLASS"] == "SIMPLE" || $tree["CLASS"] == "QSIMPLE")) {
|
if (($tree["CLASS"] == "SIMPLE" || $tree["CLASS"] == "QSIMPLE")) {
|
||||||
if (is_array($tree["VALUE"])) {
|
if (is_array($tree["VALUE"])) {
|
||||||
$treetmp = null;
|
$treetmp = null;
|
||||||
$pnum = 0;
|
$pnum = 0;
|
||||||
for ($i = 0; $i < count($tree["VALUE"]); $i++) {
|
for ($i = 0; $i < count($tree["VALUE"]); $i ++ ) {
|
||||||
// gestion mot vide
|
// gestion mot vide
|
||||||
if (isset($emptyw[$tree["VALUE"][$i]]) || $tree["VALUE"][$i] == "?" || $tree["VALUE"][$i] == "*") {
|
if (isset($emptyw[$tree["VALUE"][$i]]) || $tree["VALUE"][$i] == "?" || $tree["VALUE"][$i] == "*") {
|
||||||
// on a forcé les '?' ou '*' isolés comme des mots vides
|
// on a forcé les '?' ou '*' isolés comme des mots vides
|
||||||
$pnum++;
|
$pnum ++;
|
||||||
} else {
|
} else {
|
||||||
if ($treetmp == null) {
|
if ($treetmp == null) {
|
||||||
$treetmp = array("CLASS" => $tree["CLASS"],
|
$treetmp = array("CLASS" => $tree["CLASS"],
|
||||||
@@ -1052,7 +1107,7 @@ class PhraseaEngineQueryParser
|
|||||||
unset($tree["LB"]);
|
unset($tree["LB"]);
|
||||||
unset($tree["RB"]);
|
unset($tree["RB"]);
|
||||||
unset($tree["PNUM"]);
|
unset($tree["PNUM"]);
|
||||||
$nmodif++;
|
$nmodif ++;
|
||||||
} else {
|
} else {
|
||||||
$nmodif += $this->factor_or2($tree["LB"], $depth + 1);
|
$nmodif += $this->factor_or2($tree["LB"], $depth + 1);
|
||||||
$nmodif += $this->factor_or2($tree["RB"], $depth + 1);
|
$nmodif += $this->factor_or2($tree["RB"], $depth + 1);
|
||||||
@@ -1166,7 +1221,7 @@ class PhraseaEngineQueryParser
|
|||||||
}
|
}
|
||||||
$tmp = $onedate;
|
$tmp = $onedate;
|
||||||
|
|
||||||
if (!is_array($tmp))
|
if ( ! is_array($tmp))
|
||||||
$tmp = explode(" ", $tmp);
|
$tmp = explode(" ", $tmp);
|
||||||
|
|
||||||
switch (sizeof($tmp)) {
|
switch (sizeof($tmp)) {
|
||||||
@@ -1420,7 +1475,7 @@ class PhraseaEngineQueryParser
|
|||||||
if ($inquote) {
|
if ($inquote) {
|
||||||
// quand on est entre guillements les tokens perdent leur signification
|
// quand on est entre guillements les tokens perdent leur signification
|
||||||
$tree = $this->addtotree($tree, $t, $depth, $inquote);
|
$tree = $this->addtotree($tree, $t, $depth, $inquote);
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1439,16 +1494,16 @@ class PhraseaEngineQueryParser
|
|||||||
if ($inquote) {
|
if ($inquote) {
|
||||||
// quand on est entre guillements les tokens perdent leur signification
|
// quand on est entre guillements les tokens perdent leur signification
|
||||||
$tree = $this->addtotree($tree, $t, $depth, $inquote);
|
$tree = $this->addtotree($tree, $t, $depth, $inquote);
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
} else { // '(' : appel récursif
|
} else { // '(' : appel récursif
|
||||||
if (!$tree)
|
if ( ! $tree)
|
||||||
$tree = $this->maketree($depth + 1);
|
$tree = $this->maketree($depth + 1);
|
||||||
else {
|
else {
|
||||||
if (($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") && $tree["RB"] == null) {
|
if (($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") && $tree["RB"] == null) {
|
||||||
$tree["RB"] = $this->maketree($depth + 1);
|
$tree["RB"] = $this->maketree($depth + 1);
|
||||||
if (!$tree["RB"])
|
if ( ! $tree["RB"])
|
||||||
$tree = null;
|
$tree = null;
|
||||||
} else {
|
} else {
|
||||||
// ici on applique l'opérateur par défaut
|
// ici on applique l'opérateur par défaut
|
||||||
@@ -1461,7 +1516,7 @@ class PhraseaEngineQueryParser
|
|||||||
"RB" => $this->maketree($depth + 1));
|
"RB" => $this->maketree($depth + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1490,12 +1545,12 @@ class PhraseaEngineQueryParser
|
|||||||
print("OPENING QUOTE!<br>");
|
print("OPENING QUOTE!<br>");
|
||||||
}
|
}
|
||||||
// ouverture des guillemets -> récursivité
|
// ouverture des guillemets -> récursivité
|
||||||
if (!$tree)
|
if ( ! $tree)
|
||||||
$tree = $this->maketree($depth + 1, true);
|
$tree = $this->maketree($depth + 1, true);
|
||||||
else {
|
else {
|
||||||
if (($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") && $tree["RB"] == null) {
|
if (($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") && $tree["RB"] == null) {
|
||||||
$tree["RB"] = $this->maketree($depth + 1, true);
|
$tree["RB"] = $this->maketree($depth + 1, true);
|
||||||
if (!$tree["RB"])
|
if ( ! $tree["RB"])
|
||||||
$tree = null;
|
$tree = null;
|
||||||
} else {
|
} else {
|
||||||
// ici on applique l'opérateur par défaut
|
// ici on applique l'opérateur par défaut
|
||||||
@@ -1508,7 +1563,7 @@ class PhraseaEngineQueryParser
|
|||||||
"RB" => $this->maketree($depth + 1, true));
|
"RB" => $this->maketree($depth + 1, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1520,7 +1575,7 @@ class PhraseaEngineQueryParser
|
|||||||
var_dump($tree);
|
var_dump($tree);
|
||||||
print("-------------------------\n");
|
print("-------------------------\n");
|
||||||
}
|
}
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1545,7 +1600,7 @@ class PhraseaEngineQueryParser
|
|||||||
print("-------------------------\n");
|
print("-------------------------\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$t) {
|
if ( ! $t) {
|
||||||
return($tree);
|
return($tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1559,7 +1614,7 @@ class PhraseaEngineQueryParser
|
|||||||
// un [xxx] suit un terme : il introduit un contexte
|
// un [xxx] suit un terme : il introduit un contexte
|
||||||
$tree["CONTEXT"] = $t["VALUE"];
|
$tree["CONTEXT"] = $t["VALUE"];
|
||||||
} elseif ($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") {
|
} elseif ($tree["CLASS"] == "OPS" || $tree["CLASS"] == "OPK") {
|
||||||
if (!isset($tree["RB"]) || !$tree["RB"]) {
|
if ( ! isset($tree["RB"]) || ! $tree["RB"]) {
|
||||||
// un [xxx] peut suivre un opérateur, c'est un paramétre normalement numérique
|
// un [xxx] peut suivre un opérateur, c'est un paramétre normalement numérique
|
||||||
$tree["PNUM"] = $t["VALUE"];
|
$tree["PNUM"] = $t["VALUE"];
|
||||||
} else {
|
} else {
|
||||||
@@ -1586,7 +1641,7 @@ class PhraseaEngineQueryParser
|
|||||||
break;
|
break;
|
||||||
case "TOK_CMP":
|
case "TOK_CMP":
|
||||||
// < > <= >= <> = : sont des opérateurs de comparaison
|
// < > <= >= <> = : sont des opérateurs de comparaison
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
// printf("\nUne question ne peut commencer par '" . $t["VALUE"] . "'<br>");
|
// printf("\nUne question ne peut commencer par '" . $t["VALUE"] . "'<br>");
|
||||||
if ($this->errmsg != "")
|
if ($this->errmsg != "")
|
||||||
$this->errmsg .= "\\n";
|
$this->errmsg .= "\\n";
|
||||||
@@ -1606,9 +1661,9 @@ class PhraseaEngineQueryParser
|
|||||||
return(array("CLASS" => "OPK", "VALUE" => $t["VALUE"], "NODETYPE" => $this->opk[$t["VALUE"]]["NODETYPE"], "PNUM" => null, "DEPTH" => $depth, "LB" => $tree, "RB" => null));
|
return(array("CLASS" => "OPK", "VALUE" => $t["VALUE"], "NODETYPE" => $this->opk[$t["VALUE"]]["NODETYPE"], "PNUM" => null, "DEPTH" => $depth, "LB" => $tree, "RB" => null));
|
||||||
break;
|
break;
|
||||||
case "TOK_WORD":
|
case "TOK_WORD":
|
||||||
if ($t["CLASS"] == "TOK_WORD" && isset($this->ops[$t["VALUE"]]) && !$inquote) {
|
if ($t["CLASS"] == "TOK_WORD" && isset($this->ops[$t["VALUE"]]) && ! $inquote) {
|
||||||
// ce mot est un opérateur phrasea
|
// ce mot est un opérateur phrasea
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
// printf("\n581 : Une question ne peut commencer par un opérateur<br>");
|
// printf("\n581 : Une question ne peut commencer par un opérateur<br>");
|
||||||
if ($this->errmsg != "")
|
if ($this->errmsg != "")
|
||||||
$this->errmsg .= "\\n";
|
$this->errmsg .= "\\n";
|
||||||
@@ -1641,7 +1696,7 @@ class PhraseaEngineQueryParser
|
|||||||
// ce mot n'est pas un opérateur
|
// ce mot n'est pas un opérateur
|
||||||
$pnum = null;
|
$pnum = null;
|
||||||
$nodetype = PHRASEA_KEYLIST;
|
$nodetype = PHRASEA_KEYLIST;
|
||||||
if ($t["CLASS"] == "TOK_WORD" && isset($this->spw[$t["VALUE"]]) && !$inquote) {
|
if ($t["CLASS"] == "TOK_WORD" && isset($this->spw[$t["VALUE"]]) && ! $inquote) {
|
||||||
// mais c'est un mot 'spécial' de phrasea ('last', 'all')
|
// mais c'est un mot 'spécial' de phrasea ('last', 'all')
|
||||||
$type = $this->spw[$t["VALUE"]]["CLASS"];
|
$type = $this->spw[$t["VALUE"]]["CLASS"];
|
||||||
$nodetype = $this->spw[$t["VALUE"]]["NODETYPE"];
|
$nodetype = $this->spw[$t["VALUE"]]["NODETYPE"];
|
||||||
@@ -1668,24 +1723,25 @@ class PhraseaEngineQueryParser
|
|||||||
public function addsimple($t, $type, $nodetype, $pnum, $tree, $depth)
|
public function addsimple($t, $type, $nodetype, $pnum, $tree, $depth)
|
||||||
{
|
{
|
||||||
$nok = 0;
|
$nok = 0;
|
||||||
|
$registry = \registry::get_instance();
|
||||||
$w = $t["VALUE"];
|
$w = $t["VALUE"];
|
||||||
if ($w != "?" && $w != "*") { // on laisse passer les 'isolés' pour les traiter plus tard comme des mots vides
|
if ($w != "?" && $w != "*") { // on laisse passer les 'isolés' pour les traiter plus tard comme des mots vides
|
||||||
for ($i = 0; $i < strlen($w); $i++) {
|
for ($i = 0; $i < strlen($w); $i ++ ) {
|
||||||
$c = substr($w, $i, 1);
|
$c = substr($w, $i, 1);
|
||||||
if ($c == "?" || $c == "*") {
|
if ($c == "?" || $c == "*") {
|
||||||
if ($nok < $this->app['phraseanet.registry']->get('GV_min_letters_truncation')) {
|
if ($nok < $registry->get('GV_min_letters_truncation')) {
|
||||||
if ($this->errmsg != "")
|
if ($this->errmsg != "")
|
||||||
$this->errmsg .= sprintf("\\n");
|
$this->errmsg .= sprintf("\\n");
|
||||||
$this->errmsg .= _('qparser:: Formulation incorrecte, necessite plus de caractere : ') . "<br>" . $this->app['phraseanet.registry']->get('GV_min_letters_truncation');
|
$this->errmsg .= _('qparser:: Formulation incorrecte, necessite plus de caractere : ') . "<br>" . $registry->get('GV_min_letters_truncation');
|
||||||
|
|
||||||
return(null);
|
return(null);
|
||||||
}
|
}
|
||||||
// $nok = 0;
|
// $nok = 0;
|
||||||
} else
|
} else
|
||||||
$nok++;
|
$nok ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$tree) {
|
if ( ! $tree) {
|
||||||
return(array("CLASS" => $type, "NODETYPE" => $nodetype, "VALUE" => array($t["VALUE"]), "PNUM" => $pnum, "DEPTH" => $depth));
|
return(array("CLASS" => $type, "NODETYPE" => $nodetype, "VALUE" => array($t["VALUE"]), "PNUM" => $pnum, "DEPTH" => $depth));
|
||||||
}
|
}
|
||||||
switch ($tree["CLASS"]) {
|
switch ($tree["CLASS"]) {
|
||||||
@@ -1870,8 +1926,8 @@ class PhraseaEngineQueryParser
|
|||||||
$l = mb_strlen($this->phq, 'UTF-8');
|
$l = mb_strlen($this->phq, 'UTF-8');
|
||||||
$t = "";
|
$t = "";
|
||||||
$c_utf8 = "";
|
$c_utf8 = "";
|
||||||
for ($i = 0; $i < $l; $i++) {
|
for ($i = 0; $i < $l; $i ++ ) {
|
||||||
if (!$this->unicode->has_indexer_bad_char(($c_utf8 = mb_substr($this->phq, $i, 1, 'UTF-8')))) {
|
if ( ! $this->unicode->has_indexer_bad_char(($c_utf8 = mb_substr($this->phq, $i, 1, 'UTF-8')))) {
|
||||||
// $c = mb_strtolower($c);
|
// $c = mb_strtolower($c);
|
||||||
// $t .= isset($this->noaccent[$c]) ? $this->noaccent[$c] : $c;
|
// $t .= isset($this->noaccent[$c]) ? $this->noaccent[$c] : $c;
|
||||||
$t .= $this->unicode->remove_diacritics(mb_strtolower($c_utf8));
|
$t .= $this->unicode->remove_diacritics(mb_strtolower($c_utf8));
|
||||||
|
@@ -1,517 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2012 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
|
|
||||||
class PhraseaEngine implements SearchEngineInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var SearchEngineOptions
|
|
||||||
*/
|
|
||||||
protected $options;
|
|
||||||
protected $queries = array();
|
|
||||||
protected $arrayq = array();
|
|
||||||
protected $colls = array();
|
|
||||||
protected $qp = array();
|
|
||||||
protected $needthesaurus = array();
|
|
||||||
protected $resetCacheNextQuery = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->options = new SearchEngineOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function status()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function availableTypes()
|
|
||||||
{
|
|
||||||
return array(self::GEM_TYPE_RECORD, self::GEM_TYPE_STORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function addRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->updateRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function removeRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
$connbas = $record->get_databox()->get_connection();
|
|
||||||
|
|
||||||
$sql = "DELETE FROM prop WHERE record_id = :record_id";
|
|
||||||
$stmt = $connbas->prepare($sql);
|
|
||||||
$stmt->execute(array(':record_id' => $record->get_record_id()));
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
$sql = "DELETE FROM idx WHERE record_id = :record_id";
|
|
||||||
$stmt = $connbas->prepare($sql);
|
|
||||||
$stmt->execute(array(':record_id' => $record->get_record_id()));
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
$sql = "DELETE FROM thit WHERE record_id = :record_id";
|
|
||||||
$stmt = $connbas->prepare($sql);
|
|
||||||
$stmt->execute(array(':record_id' => $record->get_record_id()));
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
unset($stmt, $connbas);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function updateRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
$record->set_binary_status(\databox_status::dec2bin(bindec($record->get_status()) & ~7 | 4));
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function addStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->updateRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function removeStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->removeRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function updateStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->updateRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function addFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Phrasea Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function removeFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Phrasea Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function updateFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Phrasea Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function setOptions(SearchEngineOptions $options)
|
|
||||||
{
|
|
||||||
$this->options = $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function resetOptions()
|
|
||||||
{
|
|
||||||
$this->options = new SearchEngineOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function query($query, $offset, $perPage)
|
|
||||||
{
|
|
||||||
assert(is_int($offset));
|
|
||||||
assert($offset >= 0);
|
|
||||||
assert(is_int($perPage));
|
|
||||||
|
|
||||||
if (trim($query) === '') {
|
|
||||||
$query = "all";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->options->getRecordType()) {
|
|
||||||
$query .= ' AND recordtype=' . $this->options->getRecordType();
|
|
||||||
}
|
|
||||||
|
|
||||||
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
|
||||||
$session = $appbox->get_session();
|
|
||||||
|
|
||||||
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
|
||||||
$stmt = $appbox->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute(array(':ses_id' => $session->get_ses_id()));
|
|
||||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
$date_obj = new \DateTime('-10 min');
|
|
||||||
$date_quest = new \DateTime($row['query_time']);
|
|
||||||
|
|
||||||
if ($query != $row['query']) {
|
|
||||||
$this->resetCacheNextQuery = true;
|
|
||||||
}
|
|
||||||
if ($date_obj > $date_quest) {
|
|
||||||
$this->resetCacheNextQuery = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->resetCacheNextQuery === true) {
|
|
||||||
phrasea_clear_cache($session->get_ses_id());
|
|
||||||
$this->addQuery($query);
|
|
||||||
$this->executeQuery($query);
|
|
||||||
|
|
||||||
$sql = 'SELECT query, query_time, duration, total FROM cache WHERE session_id = :ses_id';
|
|
||||||
$stmt = $appbox->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute(array(':ses_id' => $session->get_ses_id()));
|
|
||||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
||||||
$stmt->closeCursor();
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* @todo clean this in DB
|
|
||||||
*/
|
|
||||||
$this->total_available = $this->total_results = $session->get_session_prefs('phrasea_engine_n_results');
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = phrasea_fetch_results(
|
|
||||||
$session->get_ses_id(), $offset + 1, $perPage, false
|
|
||||||
);
|
|
||||||
|
|
||||||
$rs = array();
|
|
||||||
$error = _('Unable to execute query');
|
|
||||||
|
|
||||||
if (isset($res['results']) && is_array($res['results'])) {
|
|
||||||
$rs = $res['results'];
|
|
||||||
$error = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$resultNumber = $offset;
|
|
||||||
$records = new ArrayCollection();
|
|
||||||
|
|
||||||
foreach ($rs as $data) {
|
|
||||||
try {
|
|
||||||
$records->add(new \record_adapter(
|
|
||||||
\phrasea::sbasFromBas($data['base_id']),
|
|
||||||
$data['record_id'],
|
|
||||||
$resultNumber
|
|
||||||
));
|
|
||||||
} catch (Exception $e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
$resultNumber ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new SearchEngineResult($records, $query, $row['duration'], $offset, $row['total'], $row['total'], $error, '', new ArrayCollection(), new ArrayCollection(), '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
private function executeQuery($query)
|
|
||||||
{
|
|
||||||
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
|
||||||
$session = $appbox->get_session();
|
|
||||||
$registry = $appbox->get_registry();
|
|
||||||
|
|
||||||
$dateLog = date("Y-m-d H:i:s");
|
|
||||||
$nbanswers = $total_time = 0;
|
|
||||||
$sort = '';
|
|
||||||
|
|
||||||
if ($this->options->sortBy()) {
|
|
||||||
switch ($this->options->sortOrder()) {
|
|
||||||
case SearchEngineOptions::SORT_MODE_ASC:
|
|
||||||
$sort = '+';
|
|
||||||
break;
|
|
||||||
case SearchEngineOptions::SORT_MODE_DESC:
|
|
||||||
default:
|
|
||||||
$sort = '-';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$sort .= '0' . $this->options->sortBy();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->queries as $sbas_id => $qry) {
|
|
||||||
$BF = array();
|
|
||||||
|
|
||||||
foreach ($this->options->businessFieldsOn() as $collection) {
|
|
||||||
$BF[] = $collection->get_base_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = phrasea_query2(
|
|
||||||
$session->get_ses_id()
|
|
||||||
, $sbas_id
|
|
||||||
, $this->colls[$sbas_id]
|
|
||||||
, $this->arrayq[$sbas_id]
|
|
||||||
, $registry->get('GV_sit')
|
|
||||||
, (string) $session->get_usr_id()
|
|
||||||
, false
|
|
||||||
, $this->options->searchType() == SearchEngineOptions::RECORD_GROUPING ? PHRASEA_MULTIDOC_REGONLY : PHRASEA_MULTIDOC_DOCONLY
|
|
||||||
, $sort
|
|
||||||
, $BF
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($results) {
|
|
||||||
$total_time += $results['time_all'];
|
|
||||||
$nbanswers += $results["nbanswers"];
|
|
||||||
}
|
|
||||||
|
|
||||||
$logger = $session->get_logger($appbox->get_databox($sbas_id));
|
|
||||||
|
|
||||||
$conn2 = \connection::getPDOConnection($sbas_id);
|
|
||||||
|
|
||||||
$sql3 = "INSERT INTO log_search
|
|
||||||
(id, log_id, date, search, results, coll_id )
|
|
||||||
VALUES
|
|
||||||
(null, :log_id, :date, :query, :nbresults, :colls)";
|
|
||||||
|
|
||||||
$params = array(
|
|
||||||
':log_id' => $logger->get_id()
|
|
||||||
, ':date' => $dateLog
|
|
||||||
, ':query' => $query
|
|
||||||
, ':nbresults' => $results["nbanswers"]
|
|
||||||
, ':colls' => implode(',', $this->colls[$sbas_id])
|
|
||||||
);
|
|
||||||
|
|
||||||
$stmt = $conn2->prepare($sql3);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$stmt->closeCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = 'UPDATE cache
|
|
||||||
SET query = :query, query_time = NOW(), duration = :duration, total = :total
|
|
||||||
WHERE session_id = :ses_id';
|
|
||||||
|
|
||||||
$params = array(
|
|
||||||
'query' => $query,
|
|
||||||
':ses_id' => $session->get_ses_id(),
|
|
||||||
':duration' => $total_time,
|
|
||||||
':total' => $nbanswers,
|
|
||||||
);
|
|
||||||
|
|
||||||
$stmt = $appbox->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
\User_Adapter::saveQuery($query);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function autocomplete($query)
|
|
||||||
{
|
|
||||||
return new ArrayCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function excerpt($query, $fields, \record_adapter $record)
|
|
||||||
{
|
|
||||||
$ret = array();
|
|
||||||
|
|
||||||
$appbox = \appbox::get_instance(\bootstrap::getCore());
|
|
||||||
$session = $appbox->get_session();
|
|
||||||
$res = phrasea_fetch_results(
|
|
||||||
$session->get_ses_id(), ($record->get_number() + 1), 1, true, "[[em]]", "[[/em]]"
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( ! isset($res['results']) || ! is_array($res['results'])) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$rs = $res['results'];
|
|
||||||
$res = array_shift($rs);
|
|
||||||
if ( ! isset($res['xml'])) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$sxe = simplexml_load_string($res['xml']);
|
|
||||||
|
|
||||||
foreach ($fields as $name => $field) {
|
|
||||||
if ($sxe && $sxe->description && $sxe->description->$name) {
|
|
||||||
$val = array();
|
|
||||||
foreach ($sxe->description->$name as $value) {
|
|
||||||
$val[] = str_replace(array('[[em]]', '[[/em]]'), array('<em>', '</em>'), (string) $value);
|
|
||||||
}
|
|
||||||
$separator = $field['separator'] ? $field['separator'][0] : '';
|
|
||||||
$val = implode(' ' . $separator . ' ', $val);
|
|
||||||
} else {
|
|
||||||
$val = $field['value'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$ret[] = $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function resetCache()
|
|
||||||
{
|
|
||||||
$this->resetCacheNextQuery = true;
|
|
||||||
$this->queries = $this->arrayq = $this->colls = $this->qp = $this->needthesaurus = array();
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addQuery($query)
|
|
||||||
{
|
|
||||||
foreach ($this->options->databoxes() as $databox) {
|
|
||||||
$this->queries[$databox->get_sbas_id()] = $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = $this->options->getStatus();
|
|
||||||
|
|
||||||
foreach ($this->queries as $sbas => $qs) {
|
|
||||||
if ($status) {
|
|
||||||
$requestStat = 'xxxx';
|
|
||||||
|
|
||||||
for ($i = 4; ($i <= 64); $i ++ ) {
|
|
||||||
if ( ! isset($status[$i])) {
|
|
||||||
$requestStat = 'x' . $requestStat;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$val = 'x';
|
|
||||||
if (isset($status[$i][$sbas])) {
|
|
||||||
if ($status[$i][$sbas] == '0') {
|
|
||||||
$val = '0';
|
|
||||||
} elseif ($status[$i][$sbas] == '1') {
|
|
||||||
$val = '1';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$requestStat = $val . $requestStat;
|
|
||||||
}
|
|
||||||
|
|
||||||
$requestStat = ltrim($requestStat, 'x');
|
|
||||||
|
|
||||||
if ($requestStat !== '') {
|
|
||||||
$this->queries[$sbas] .= ' AND (recordstatus=' . $requestStat . ')';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->options->fields()) {
|
|
||||||
$this->queries[$sbas] .= ' IN (' . implode(' OR ', $this->options->fields()) . ')';
|
|
||||||
}
|
|
||||||
if (($this->options->getMinDate() || $this->options->getMaxDate()) && $this->options->getDateFields()) {
|
|
||||||
if ($this->options->getMinDate()) {
|
|
||||||
$this->queries[$sbas] .= ' AND ( ' . implode(' >= ' . $this->options->getMinDate()->format('Y-m-d') . ' OR ', $this->options->getDateFields()) . ' >= ' . $this->options->getMinDate()->format('Y-m-d') . ' ) ';
|
|
||||||
}
|
|
||||||
if ($this->options->getMaxDate()) {
|
|
||||||
$this->queries[$sbas] .= ' AND ( ' . implode(' <= ' . $this->options->getMaxDate()->format('Y-m-d') . ' OR ', $this->options->getDateFields()) . ' <= ' . $this->options->getMaxDate()->format('Y-m-d') . ' ) ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->singleParse('main', $query);
|
|
||||||
|
|
||||||
foreach ($this->queries as $sbas => $db_query) {
|
|
||||||
$this->singleParse($sbas, $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
$base_ids = array_map(function(\collection $collection) {
|
|
||||||
return $collection->get_base_id();
|
|
||||||
}, $this->options->collections());
|
|
||||||
|
|
||||||
foreach ($this->options->databoxes() as $databox) {
|
|
||||||
$sbas_id = $databox->get_sbas_id();
|
|
||||||
|
|
||||||
$this->colls[$sbas_id] = array();
|
|
||||||
|
|
||||||
foreach ($databox->get_collections() as $collection) {
|
|
||||||
if (in_array($collection->get_base_id(), $base_ids)) {
|
|
||||||
$this->colls[$sbas_id][] = $collection->get_base_id();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sizeof($this->colls[$sbas_id]) <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->needthesaurus[$sbas_id]) {
|
|
||||||
if ($databox->get_dom_thesaurus()) {
|
|
||||||
$this->qp[$sbas_id]->thesaurus2($this->indep_treeq[$sbas_id], $sbas_id, $databox->get_dbname(), $databox->get_dom_thesaurus(), true);
|
|
||||||
$this->qp['main']->thesaurus2($this->indep_treeq['main'], $sbas_id, $databox->get_dbname(), $databox->get_dom_thesaurus(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$emptyw = false;
|
|
||||||
|
|
||||||
$this->qp[$sbas_id]->set_default($this->indep_treeq[$sbas_id], $emptyw);
|
|
||||||
$this->qp[$sbas_id]->distrib_in($this->indep_treeq[$sbas_id]);
|
|
||||||
$this->qp[$sbas_id]->factor_or($this->indep_treeq[$sbas_id]);
|
|
||||||
$this->qp[$sbas_id]->setNumValue($this->indep_treeq[$sbas_id], $databox->get_sxml_structure());
|
|
||||||
$this->qp[$sbas_id]->thesaurus2_apply($this->indep_treeq[$sbas_id], $sbas_id);
|
|
||||||
$this->arrayq[$sbas_id] = $this->qp[$sbas_id]->makequery($this->indep_treeq[$sbas_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function singleParse($sbas, $query)
|
|
||||||
{
|
|
||||||
$this->qp[$sbas] = new PhraseaEngineQueryParser($this->options->getLocale());
|
|
||||||
$this->qp[$sbas]->debug = false;
|
|
||||||
|
|
||||||
if ($sbas == 'main') {
|
|
||||||
$simple_treeq = $this->qp[$sbas]->parsequery($query);
|
|
||||||
} else {
|
|
||||||
$simple_treeq = $this->qp[$sbas]->parsequery($this->queries[$sbas]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->qp[$sbas]->priority_opk($simple_treeq);
|
|
||||||
$this->qp[$sbas]->distrib_opk($simple_treeq);
|
|
||||||
$this->needthesaurus[$sbas] = false;
|
|
||||||
|
|
||||||
$this->indep_treeq[$sbas] = $this->qp[$sbas]->extendThesaurusOnTerms($simple_treeq, true, true, false);
|
|
||||||
$this->needthesaurus[$sbas] = $this->qp[$sbas]->containsColonOperator($this->indep_treeq[$sbas]);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@ use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
|||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Silex\Application;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
interface SearchEngineInterface
|
interface SearchEngineInterface
|
||||||
{
|
{
|
||||||
|
@@ -1,689 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2012 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../vendor/sphinx/sphinxapi.php';
|
|
||||||
|
|
||||||
class SphinxSearch implements SearchEngineInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var \SphinxClient
|
|
||||||
*/
|
|
||||||
protected $sphinx;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var \PDO
|
|
||||||
*/
|
|
||||||
protected $rt_conn;
|
|
||||||
protected $options;
|
|
||||||
|
|
||||||
public function __construct($host, $port, $rt_host, $rt_port)
|
|
||||||
{
|
|
||||||
$this->options = new SearchEngineOptions();
|
|
||||||
|
|
||||||
$this->sphinx = new \SphinxClient();
|
|
||||||
|
|
||||||
$this->sphinx->SetServer($host, $port);
|
|
||||||
$this->sphinx->SetArrayResult(true);
|
|
||||||
$this->sphinx->SetConnectTimeout(1);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->rt_conn = @new \PDO(sprintf('mysql:host=%s;port=%s;', $rt_host, $rt_port));
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
$this->rt_conn = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function status()
|
|
||||||
{
|
|
||||||
$status = $this->sphinx->Status();
|
|
||||||
|
|
||||||
if (false === $status) {
|
|
||||||
throw new Exception(_('Sphinx server is offline'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $this->rt_conn) {
|
|
||||||
throw new RuntimeException('Unable to connect to sphinx rt');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function availableTypes()
|
|
||||||
{
|
|
||||||
return array(self::GEM_TYPE_RECORD, self::GEM_TYPE_STORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
$all_datas = array();
|
|
||||||
|
|
||||||
foreach ($record->get_caption()->get_fields(null, true) as $field) {
|
|
||||||
if ( ! $field->is_indexable()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$all_datas[] = $field->get_serialized_values();
|
|
||||||
|
|
||||||
foreach ($field->get_values() as $value) {
|
|
||||||
|
|
||||||
$this->rt_conn->exec("REPLACE INTO "
|
|
||||||
. "metas_realtime" . $this->CRCdatabox($record->get_databox()) . " VALUES (
|
|
||||||
'" . $value->getId() . "'
|
|
||||||
,'" . str_replace("'", "\'", $value->getValue()) . "'
|
|
||||||
,'" . $value->getDatabox_field()->get_id() . "'
|
|
||||||
," . $record->get_record_id() . "
|
|
||||||
," . $record->get_sbas_id() . "
|
|
||||||
," . $record->get_collection()->get_coll_id() . "
|
|
||||||
," . (int) $record->is_grouping() . "
|
|
||||||
," . crc32($record->get_sbas_id() . '_' . $value->getDatabox_field()->get_id()) . "
|
|
||||||
," . crc32($record->get_sbas_id() . '_' . $record->get_collection()->get_coll_id()) . "
|
|
||||||
," . crc32($record->get_sbas_id() . '_' . $record->get_record_id()) . "
|
|
||||||
," . crc32($record->get_type()) . "
|
|
||||||
,0
|
|
||||||
," . (int) $value->getDatabox_field()->isBusiness() . "
|
|
||||||
," . crc32($record->get_collection()->get_coll_id() . '_' . (int) $value->getDatabox_field()->isBusiness()) . "
|
|
||||||
," . $record->get_creation_date()->format('U') . " )");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->rt_conn->exec("REPLACE INTO "
|
|
||||||
. "docs_realtime" . $this->CRCdatabox($record->get_databox()) . " VALUES (
|
|
||||||
'" . $record->get_record_id() . "'
|
|
||||||
,'" . str_replace("'", "\'", implode(' ', $all_datas)) . "'
|
|
||||||
," . $record->get_record_id() . "
|
|
||||||
," . $record->get_sbas_id() . "
|
|
||||||
," . $record->get_collection()->get_coll_id() . "
|
|
||||||
," . (int) $record->is_grouping() . "
|
|
||||||
," . crc32($record->get_sbas_id() . '_' . $record->get_collection()->get_coll_id()) . "
|
|
||||||
," . crc32($record->get_sbas_id() . '_' . $record->get_record_id()) . "
|
|
||||||
," . crc32($record->get_type()) . "
|
|
||||||
,0
|
|
||||||
," . $record->get_creation_date()->format('U') . " )");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
$CRCdatabox = $this->CRCdatabox($record->get_databox());
|
|
||||||
$indexes = array(
|
|
||||||
"metadatas" . $CRCdatabox,
|
|
||||||
"metadatas" . $CRCdatabox . "_stemmed_en",
|
|
||||||
"metadatas" . $CRCdatabox . "_stemmed_fr",
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($record->get_caption()->get_fields(null, true) as $field) {
|
|
||||||
|
|
||||||
foreach ($field->get_values() as $value) {
|
|
||||||
|
|
||||||
foreach ($indexes as $index) {
|
|
||||||
$this->sphinx->UpdateAttributes($index, array("deleted"), array($value->getId() => array(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->rt_conn->exec("DELETE FROM metas_realtime" . $CRCdatabox . " WHERE id = " . $value->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$indexes = array(
|
|
||||||
"documents" . $CRCdatabox,
|
|
||||||
"documents" . $CRCdatabox . "_stemmed_fr",
|
|
||||||
"documents" . $CRCdatabox . "_stemmed_en"
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($indexes as $index) {
|
|
||||||
$this->sphinx->UpdateAttributes($index, array("deleted"), array($record->get_record_id() => array(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->rt_conn->exec("DELETE FROM docs_realtime" . $CRCdatabox . " WHERE id = " . $record->get_record_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateRecord(\record_adapter $record)
|
|
||||||
{
|
|
||||||
$this->removeRecord($record);
|
|
||||||
$this->addRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->addRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->removeRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateStory(\record_adapter $record)
|
|
||||||
{
|
|
||||||
return $this->updateRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Sphinx Search Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Sphinx Search Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateFeedEntry(\Feed_Entry_Adapter $entry)
|
|
||||||
{
|
|
||||||
throw new RuntimeException('Feed Entry indexing not supported by Sphinx Search Engine');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setOptions(SearchEngineOptions $options)
|
|
||||||
{
|
|
||||||
$this->options = $options;
|
|
||||||
$this->applyOptions($options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resetOptions()
|
|
||||||
{
|
|
||||||
$this->options = new SearchEngineOptions();
|
|
||||||
$this->resetSphinx();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function resetSphinx()
|
|
||||||
{
|
|
||||||
$this->sphinx->ResetGroupBy();
|
|
||||||
$this->sphinx->ResetFilters();
|
|
||||||
$this->sphinx->ResetOverrides();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function query($query, $offset, $perPage)
|
|
||||||
{
|
|
||||||
assert(is_int($offset));
|
|
||||||
assert($offset >= 0);
|
|
||||||
assert(is_int($perPage));
|
|
||||||
|
|
||||||
$query = $this->parseQuery($query);
|
|
||||||
|
|
||||||
$preg = preg_match('/\s?recordid\s?=\s?([0-9]+)/i', $query, $matches, 0, 0);
|
|
||||||
|
|
||||||
if ($preg > 0) {
|
|
||||||
$this->sphinx->SetFilter('record_id', array($matches[1]));
|
|
||||||
$query = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->SetLimits($offset, $perPage);
|
|
||||||
$this->sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
|
|
||||||
|
|
||||||
$index = $this->getQueryIndex($query);
|
|
||||||
$res = $this->sphinx->Query($query, $index);
|
|
||||||
|
|
||||||
$results = new ArrayCollection();
|
|
||||||
|
|
||||||
if ($res === false) {
|
|
||||||
if ($this->sphinx->IsConnectError() === true) {
|
|
||||||
$error = _('Sphinx server is offline');
|
|
||||||
} else {
|
|
||||||
$error = $this->sphinx->GetLastError();
|
|
||||||
}
|
|
||||||
$warning = $this->sphinx->GetLastWarning();
|
|
||||||
|
|
||||||
$total = $available = $duration = 0;
|
|
||||||
$suggestions = $propositions = array();
|
|
||||||
} else {
|
|
||||||
$error = $res['error'];
|
|
||||||
$warning = $res['warning'];
|
|
||||||
|
|
||||||
$duration = $res['time'];
|
|
||||||
$total = $res['total_found'];
|
|
||||||
$available = $res['total'];
|
|
||||||
|
|
||||||
$resultOffset = $offset;
|
|
||||||
|
|
||||||
if (isset($res['matches'])) {
|
|
||||||
foreach ($res['matches'] as $record_id => $match) {
|
|
||||||
try {
|
|
||||||
$record =
|
|
||||||
new \record_adapter(
|
|
||||||
$match['attrs']['sbas_id']
|
|
||||||
, $match['attrs']['record_id']
|
|
||||||
, $resultOffset
|
|
||||||
);
|
|
||||||
|
|
||||||
$results->add($record);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
$resultOffset ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$suggestions = $this->getSuggestions($query);
|
|
||||||
$propositions = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SearchEngineResult($results, $query, $duration, $offset, $available, $total, $error, $warning, $suggestions, $propositions, $index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function autocomplete($query)
|
|
||||||
{
|
|
||||||
$words = explode(" ", $this->cleanupQuery($query));
|
|
||||||
|
|
||||||
return $this->getSuggestions(array_pop($words));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function excerpt($query, $fields, \record_adapter $record)
|
|
||||||
{
|
|
||||||
$index = '';
|
|
||||||
// in this case search is done on metas
|
|
||||||
if ($this->options->fields() || $this->options->businessFieldsOn()) {
|
|
||||||
if ($this->options->stemmed() && $this->options->getLocale()) {
|
|
||||||
$index = 'metadatas' . $this->CRCdatabox($record->get_databox()) . '_stemmed_' . $this->options->getLocale();
|
|
||||||
} else {
|
|
||||||
$index = 'metadatas' . $this->CRCdatabox($record->get_databox());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($this->options->stemmed()) {
|
|
||||||
$index = 'documents' . $this->CRCdatabox($record->get_databox()) . '_stemmed_' . $this->options->getLocale();
|
|
||||||
} else {
|
|
||||||
$index = 'documents' . $this->CRCdatabox($record->get_databox());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$opts = array(
|
|
||||||
'before_match' => "<em>",
|
|
||||||
'after_match' => "</em>",
|
|
||||||
);
|
|
||||||
|
|
||||||
$fields_to_send = array();
|
|
||||||
|
|
||||||
foreach ($fields as $k => $f) {
|
|
||||||
$fields_to_send[$k] = $f['value'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->sphinx->BuildExcerpts($fields_to_send, $index, $query, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resetCache()
|
|
||||||
{
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset sphinx client and apply the options
|
|
||||||
*
|
|
||||||
* Only apply filters and group by
|
|
||||||
*
|
|
||||||
* @param SearchEngineOptions $options
|
|
||||||
* @return SphinxSearch
|
|
||||||
*/
|
|
||||||
protected function applyOptions(SearchEngineOptions $options)
|
|
||||||
{
|
|
||||||
$this->resetSphinx();
|
|
||||||
|
|
||||||
$filters = array();
|
|
||||||
|
|
||||||
foreach ($options->collections() as $collection) {
|
|
||||||
$filters[] = crc32($collection->get_databox()->get_sbas_id() . '_' . $collection->get_coll_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->SetFilter('crc_sbas_coll', $filters);
|
|
||||||
|
|
||||||
$this->sphinx->SetFilter('deleted', array(0));
|
|
||||||
$this->sphinx->SetFilter('parent_record_id', array($options->searchType()));
|
|
||||||
|
|
||||||
|
|
||||||
if ($options->fields()) {
|
|
||||||
|
|
||||||
$filters = array();
|
|
||||||
foreach ($options->fields() as $field) {
|
|
||||||
$filters[] = crc32($field->get_databox()->get_sbas_id() . '_' . $field->get_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->SetFilter('crc_struct_id', $filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($options->businessFieldsOn()) {
|
|
||||||
|
|
||||||
$crc_coll_business = array();
|
|
||||||
|
|
||||||
foreach ($options->businessFieldsOn() as $collection) {
|
|
||||||
$crc_coll_business[] = crc32($collection->get_coll_id() . '_1');
|
|
||||||
$crc_coll_business[] = crc32($collection->get_coll_id() . '_0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$non_business = array();
|
|
||||||
|
|
||||||
foreach ($options->collections() as $collection) {
|
|
||||||
foreach ($options->businessFieldsOn() as $BFcollection) {
|
|
||||||
if ($collection->get_base_id() == $BFcollection->get_base_id()) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$non_business[] = $collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($non_business as $collection) {
|
|
||||||
$crc_coll_business[] = crc32($collection->get_coll_id() . '_0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->SetFilter('crc_coll_business', $crc_coll_business);
|
|
||||||
} elseif ($options->fields()) {
|
|
||||||
$this->sphinx->SetFilter('business', array(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo : enhance : check status in a better way
|
|
||||||
*/
|
|
||||||
foreach ($options->databoxes() as $databox) {
|
|
||||||
$status_opts = $options->getStatus();
|
|
||||||
foreach ($databox->get_statusbits() as $n => $status) {
|
|
||||||
if ( ! array_key_exists($n, $status_opts))
|
|
||||||
continue;
|
|
||||||
if ( ! array_key_exists($databox->get_sbas_id(), $status_opts[$n]))
|
|
||||||
continue;
|
|
||||||
$crc = crc32($databox->get_sbas_id() . '_' . $n);
|
|
||||||
$this->sphinx->SetFilter('status', array($crc), ($status_opts[$n][$databox->get_sbas_id()] == '0'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ($options->getRecordType()) {
|
|
||||||
$this->sphinx->SetFilter('crc_type', array(crc32($options->getRecordType())));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$order = '';
|
|
||||||
switch ($options->sortOrder()) {
|
|
||||||
case SearchEngineOptions::SORT_MODE_ASC:
|
|
||||||
$order = 'ASC';
|
|
||||||
break;
|
|
||||||
case SearchEngineOptions::SORT_MODE_DESC:
|
|
||||||
default:
|
|
||||||
$order = 'DESC';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($options->sortBy()) {
|
|
||||||
case SearchEngineOptions::SORT_RANDOM:
|
|
||||||
$sort = '@random';
|
|
||||||
break;
|
|
||||||
case SearchEngineOptions::SORT_RELEVANCE:
|
|
||||||
default:
|
|
||||||
$sort = '@relevance ' . $order . ', created_on ' . $order;
|
|
||||||
break;
|
|
||||||
case SearchEngineOptions::SORT_CREATED_ON:
|
|
||||||
$sort = 'created_on ' . $order;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->SetGroupBy('crc_sbas_record', SPH_GROUPBY_ATTR, $sort);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return unique integer key for a databox
|
|
||||||
*
|
|
||||||
* @param \databox $databox
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function CRCdatabox(\databox $databox)
|
|
||||||
{
|
|
||||||
return crc32(
|
|
||||||
str_replace(
|
|
||||||
array('.', '%')
|
|
||||||
, '_'
|
|
||||||
, sprintf('%s_%s_%s_%s', $databox->get_host(), $databox->get_port(), $databox->get_user(), $databox->get_dbname())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all keywords, operators, quotes from a query string
|
|
||||||
*
|
|
||||||
* @param string $query
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function cleanupQuery($query)
|
|
||||||
{
|
|
||||||
return str_replace(array("all", "last", "et", "ou", "sauf", "and", "or", "except", "in", "dans", "'", '"', "(", ")", "_", "-", "+"), ' ', $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a collection of suggestion corresponding a query
|
|
||||||
*
|
|
||||||
* @param string $query
|
|
||||||
* @return ArrayCollection An array collection of SearchEngineSuggestion
|
|
||||||
*/
|
|
||||||
private function getSuggestions($query)
|
|
||||||
{
|
|
||||||
// First we split the query into simple words
|
|
||||||
$words = explode(" ", $this->cleanupQuery(mb_strtolower($query)));
|
|
||||||
|
|
||||||
$tmpWords = array();
|
|
||||||
|
|
||||||
foreach ($words as $word) {
|
|
||||||
if (trim($word) === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$tmpWords[] = $word;
|
|
||||||
}
|
|
||||||
|
|
||||||
$words = array_unique($tmpWords);
|
|
||||||
|
|
||||||
$altVersions = array();
|
|
||||||
|
|
||||||
// As we got words, we look for alternate word for each of them
|
|
||||||
if (function_exists('enchant_broker_init') && $this->options->getLocale()) {
|
|
||||||
$broker = enchant_broker_init();
|
|
||||||
if (enchant_broker_dict_exists($broker, $this->options->getLocale())) {
|
|
||||||
$dictionnary = enchant_broker_request_dict($broker, $this->options->getLocale());
|
|
||||||
|
|
||||||
foreach ($words as $word) {
|
|
||||||
|
|
||||||
if (enchant_dict_check($dictionnary, $word) == false) {
|
|
||||||
$suggs = array_merge(array($word), enchant_dict_suggest($dictionnary, $word));
|
|
||||||
}
|
|
||||||
|
|
||||||
$altVersions[$word] = array_unique($suggs);
|
|
||||||
}
|
|
||||||
enchant_broker_free_dict($dictionnary);
|
|
||||||
}
|
|
||||||
enchant_broker_free($broker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo enhance the trigramm query, as it could be sent in one batch
|
|
||||||
*/
|
|
||||||
foreach ($altVersions as $word => $versions) {
|
|
||||||
$altVersions[$word] = array_unique(array_merge($versions, $this->get_sugg_trigrams($word)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now build an array of all possibilities based on the original query
|
|
||||||
$queries = array($query);
|
|
||||||
|
|
||||||
foreach ($altVersions as $word => $versions) {
|
|
||||||
$tmp_queries = array();
|
|
||||||
foreach ($versions as $version) {
|
|
||||||
foreach ($queries as $alt_query) {
|
|
||||||
$tmp_queries[] = $alt_query;
|
|
||||||
$tmp_queries[] = str_replace($word, $version, $alt_query);
|
|
||||||
}
|
|
||||||
$tmp_queries[] = str_replace($word, $version, $query);
|
|
||||||
}
|
|
||||||
$queries = array_unique(array_merge($queries, $tmp_queries));
|
|
||||||
}
|
|
||||||
|
|
||||||
$suggestions = array();
|
|
||||||
$max_results = 0;
|
|
||||||
|
|
||||||
foreach ($queries as $alt_query) {
|
|
||||||
$results = $this->sphinx->Query($alt_query, $this->getQueryIndex($alt_query));
|
|
||||||
|
|
||||||
if ($results !== false && isset($results['total_found'])) {
|
|
||||||
if ($results['total_found'] > 0) {
|
|
||||||
|
|
||||||
$max_results = max($max_results, (int) $results['total_found']);
|
|
||||||
$suggestions[] = new SearchEngineSuggestion($query, $alt_query, (int) $results['total_found']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usort($suggestions, array('self', 'suggestionsHitSorter'));
|
|
||||||
|
|
||||||
$tmpSuggestions = new ArrayCollection();
|
|
||||||
foreach ($suggestions as $key => $suggestion) {
|
|
||||||
if ($suggestion->hits() < ($max_results / 100)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$tmpSuggestions->add($suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $tmpSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function suggestionsHitSorter(SearchEngineSuggestion $a, SearchEngineSuggestion $b)
|
|
||||||
{
|
|
||||||
if ($a->hits() == $b->hits()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($a->hits() > $b->hits()) ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function BuildTrigrams($keyword)
|
|
||||||
{
|
|
||||||
$t = "__" . $keyword . "__";
|
|
||||||
|
|
||||||
$trigrams = "";
|
|
||||||
for ($i = 0; $i < strlen($t) - 2; $i ++ ) {
|
|
||||||
$trigrams .= substr($t, $i, 3) . " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $trigrams;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function get_sugg_trigrams($word)
|
|
||||||
{
|
|
||||||
$trigrams = $this->BuildTrigrams($word);
|
|
||||||
$query = "\"$trigrams\"/1";
|
|
||||||
$len = strlen($word);
|
|
||||||
|
|
||||||
$this->resetSphinx();
|
|
||||||
|
|
||||||
$this->sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
|
|
||||||
$this->sphinx->SetRankingMode(SPH_RANK_WORDCOUNT);
|
|
||||||
$this->sphinx->SetFilterRange("len", $len - 2, $len + 4);
|
|
||||||
|
|
||||||
$this->sphinx->SetSortMode(SPH_SORT_EXTENDED, "@weight DESC");
|
|
||||||
$this->sphinx->SetLimits(0, 10);
|
|
||||||
|
|
||||||
$indexes = array();
|
|
||||||
|
|
||||||
foreach ($this->options->databoxes() as $databox) {
|
|
||||||
$indexes[] = 'suggest' . $this->CRCdatabox($databox);
|
|
||||||
}
|
|
||||||
|
|
||||||
$index = implode(',', $indexes);
|
|
||||||
|
|
||||||
$res = $this->sphinx->Query($query, $index);
|
|
||||||
|
|
||||||
if ($this->sphinx->Status() === false) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $res || ! isset($res["matches"])) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sphinx->ResetGroupBy();
|
|
||||||
$this->sphinx->ResetFilters();
|
|
||||||
|
|
||||||
$words = array();
|
|
||||||
foreach ($res["matches"] as $match) {
|
|
||||||
$words[] = $match['attrs']['keyword'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->applyOptions($this->options);
|
|
||||||
|
|
||||||
return $words;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getQueryIndex($query)
|
|
||||||
{
|
|
||||||
$index = '*';
|
|
||||||
|
|
||||||
$index_keys = array();
|
|
||||||
|
|
||||||
foreach ($this->options->databoxes() as $databox) {
|
|
||||||
$index_keys[] = $this->CRCdatabox($databox);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($index_keys) > 0) {
|
|
||||||
if ($this->options->fields() || $this->options->businessFieldsOn()) {
|
|
||||||
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
|
||||||
$index = ', metadatas' . implode('_stemmed_' . $this->options->getLocale() . ', metadatas', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
|
||||||
} else {
|
|
||||||
$index = 'metadatas' . implode(',metadatas', $index_keys);
|
|
||||||
}
|
|
||||||
$index .= ', metas_realtime' . implode(', metas_realtime', $index_keys);
|
|
||||||
} else {
|
|
||||||
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
|
||||||
$index .= ', documents' . implode('_stemmed_' . $this->options->getLocale() . ', documents', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
|
||||||
} else {
|
|
||||||
$index .= 'documents' . implode(', documents', $index_keys);
|
|
||||||
}
|
|
||||||
$index .= ', docs_realtime' . implode(', docs_realtime', $index_keys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function parseQuery($query)
|
|
||||||
{
|
|
||||||
$query = trim($query);
|
|
||||||
|
|
||||||
while (substr($query, 0, 1) === '(' && substr($query, -1) === ')') {
|
|
||||||
$query = substr($query, 1, (mb_strlen($query) - 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($query == 'all') {
|
|
||||||
$query = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
while (mb_strpos($query, ' ') !== false) {
|
|
||||||
$query = str_replace(' ', ' ', $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$offset = 0;
|
|
||||||
while (false !== $pos = mb_strpos($query, '-', $offset)) {
|
|
||||||
$offset = $pos + 1;
|
|
||||||
if ($pos === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mb_substr($query, ($pos - 1), 1) !== ' ') {
|
|
||||||
$query = mb_substr($query, 0, ($pos)) . ' ' . mb_substr($query, $pos + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = str_ireplace(array(' ou ', ' or '), '|', $query);
|
|
||||||
$query = str_ireplace(array(' sauf ', ' except '), ' -', $query);
|
|
||||||
$query = str_ireplace(array(' and ', ' et '), ' +', $query);
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -11,15 +11,16 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\SphinxSearch;
|
namespace Alchemy\Phrasea\SearchEngine\SphinxSearch;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineResult;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Symfony\Component\Process\ExecutableFinder;
|
use Silex\Application;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../../../vendor/sphinx/sphinxapi.php';
|
||||||
|
|
||||||
class SphinxSearchEngine implements SearchEngineInterface
|
class SphinxSearchEngine implements SearchEngineInterface
|
||||||
{
|
{
|
||||||
@@ -29,12 +30,6 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
*/
|
*/
|
||||||
protected $sphinx;
|
protected $sphinx;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var \SphinxClient
|
|
||||||
*/
|
|
||||||
protected $suggestionClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @var \PDO
|
* @var \PDO
|
||||||
@@ -42,26 +37,19 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
protected $rt_conn;
|
protected $rt_conn;
|
||||||
protected $configurationPanel;
|
protected $configurationPanel;
|
||||||
protected $options;
|
protected $options;
|
||||||
protected $app;
|
|
||||||
|
|
||||||
public function __construct(Application $app, $host, $port, $rt_host, $rt_port)
|
public function __construct($host, $port, $rt_host, $rt_port)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
|
||||||
$this->options = new SearchEngineOptions();
|
$this->options = new SearchEngineOptions();
|
||||||
|
|
||||||
$this->sphinx = new \SphinxClient();
|
$this->sphinx = new \SphinxClient();
|
||||||
|
|
||||||
$this->sphinx->SetServer($host, $port);
|
$this->sphinx->SetServer($host, $port);
|
||||||
$this->sphinx->SetArrayResult(true);
|
$this->sphinx->SetArrayResult(true);
|
||||||
$this->sphinx->SetConnectTimeout(1);
|
$this->sphinx->SetConnectTimeout(1);
|
||||||
|
|
||||||
$this->suggestionClient = new \SphinxClient();
|
|
||||||
$this->suggestionClient->SetServer($host, $port);
|
|
||||||
$this->suggestionClient->SetArrayResult(true);
|
|
||||||
$this->suggestionClient->SetConnectTimeout(1);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->rt_conn = @new \PDO(sprintf('mysql:host=%s;port=%s;', $rt_host, $rt_port));
|
$this->rt_conn = @new \PDO(sprintf('mysql:host=%s;port=%s;', $rt_host, $rt_port));
|
||||||
$this->rt_conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
$this->rt_conn = null;
|
$this->rt_conn = null;
|
||||||
}
|
}
|
||||||
@@ -71,11 +59,9 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
if (false === $this->sphinx->Status()) {
|
$status = $this->sphinx->Status();
|
||||||
throw new RuntimeException(_('Sphinx server is offline'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === $this->suggestionClient->Status()) {
|
if (false === $status) {
|
||||||
throw new RuntimeException(_('Sphinx server is offline'));
|
throw new RuntimeException(_('Sphinx server is offline'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +69,17 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
throw new RuntimeException('Unable to connect to sphinx rt');
|
throw new RuntimeException('Unable to connect to sphinx rt');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->sphinx->Status();
|
return $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfigurationPanel(Application $app, Request $request)
|
||||||
|
{
|
||||||
|
return $this->configurationPanel()->get($app, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postConfigurationPanel(Application $app, Request $request)
|
||||||
|
{
|
||||||
|
return $this->configurationPanel()->post($app, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +88,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
*/
|
*/
|
||||||
public function configurationPanel()
|
public function configurationPanel()
|
||||||
{
|
{
|
||||||
if (!$this->configurationPanel) {
|
if ( ! $this->configurationPanel) {
|
||||||
$this->configurationPanel = new ConfigurationPanel($this);
|
$this->configurationPanel = new ConfigurationPanel($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,33 +102,18 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
public function addRecord(\record_adapter $record)
|
public function addRecord(\record_adapter $record)
|
||||||
{
|
{
|
||||||
if (!$this->rt_conn) {
|
|
||||||
throw new RuntimeException('Unable to connect to sphinx real-time index');
|
|
||||||
}
|
|
||||||
|
|
||||||
$all_datas = array();
|
$all_datas = array();
|
||||||
$status = array();
|
|
||||||
|
|
||||||
$binStatus = strrev($record->get_status());
|
|
||||||
|
|
||||||
for ($i = 4; $i < 32; $i++) {
|
|
||||||
if ($binStatus[$i]) {
|
|
||||||
$status[] = crc32($record->get_databox()->get_sbas_id() . '_' . $i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($record->get_caption()->get_fields(null, true) as $field) {
|
foreach ($record->get_caption()->get_fields(null, true) as $field) {
|
||||||
if (!$field->is_indexable()) {
|
if ( ! $field->is_indexable()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$field->get_databox_field()->isBusiness()) {
|
$all_datas[] = $field->get_serialized_values();
|
||||||
$all_datas[] = $field->get_serialized_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($field->get_values() as $value) {
|
foreach ($field->get_values() as $value) {
|
||||||
|
|
||||||
$sql = "REPLACE INTO "
|
$this->rt_conn->exec("REPLACE INTO "
|
||||||
. "metas_realtime" . $this->CRCdatabox($record->get_databox()) . " VALUES (
|
. "metas_realtime" . $this->CRCdatabox($record->get_databox()) . " VALUES (
|
||||||
'" . $value->getId() . "'
|
'" . $value->getId() . "'
|
||||||
,'" . str_replace("'", "\'", $value->getValue()) . "'
|
,'" . str_replace("'", "\'", $value->getValue()) . "'
|
||||||
@@ -148,10 +129,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
,0
|
,0
|
||||||
," . (int) $value->getDatabox_field()->isBusiness() . "
|
," . (int) $value->getDatabox_field()->isBusiness() . "
|
||||||
," . crc32($record->get_collection()->get_coll_id() . '_' . (int) $value->getDatabox_field()->isBusiness()) . "
|
," . crc32($record->get_collection()->get_coll_id() . '_' . (int) $value->getDatabox_field()->isBusiness()) . "
|
||||||
," . $record->get_creation_date()->format('U') . "
|
," . $record->get_creation_date()->format('U') . " )");
|
||||||
,(" . implode(',', $status) . ") )";
|
|
||||||
|
|
||||||
$this->rt_conn->exec($sql);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,18 +145,13 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
," . crc32($record->get_sbas_id() . '_' . $record->get_record_id()) . "
|
," . crc32($record->get_sbas_id() . '_' . $record->get_record_id()) . "
|
||||||
," . crc32($record->get_type()) . "
|
," . crc32($record->get_type()) . "
|
||||||
,0
|
,0
|
||||||
," . $record->get_creation_date()->format('U') . "
|
," . $record->get_creation_date()->format('U') . " )");
|
||||||
,(" . implode(',', $status) . ") )");
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeRecord(\record_adapter $record)
|
public function removeRecord(\record_adapter $record)
|
||||||
{
|
{
|
||||||
if (!$this->rt_conn) {
|
|
||||||
throw new RuntimeException('Unable to connect to sphinx real-time index');
|
|
||||||
}
|
|
||||||
|
|
||||||
$CRCdatabox = $this->CRCdatabox($record->get_databox());
|
$CRCdatabox = $this->CRCdatabox($record->get_databox());
|
||||||
$indexes = array(
|
$indexes = array(
|
||||||
"metadatas" . $CRCdatabox,
|
"metadatas" . $CRCdatabox,
|
||||||
@@ -194,7 +167,9 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
$this->sphinx->UpdateAttributes($index, array("deleted"), array($value->getId() => array(1)));
|
$this->sphinx->UpdateAttributes($index, array("deleted"), array($value->getId() => array(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->rt_conn->exec("DELETE FROM metas_realtime" . $CRCdatabox . " WHERE id = " . $value->getId());
|
$stmt = $this->rt_conn->exec("DELETE FROM metas_realtime" . $CRCdatabox . " WHERE id = " . $value->getId());
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->closeCursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +240,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
{
|
{
|
||||||
$this->sphinx->ResetGroupBy();
|
$this->sphinx->ResetGroupBy();
|
||||||
$this->sphinx->ResetFilters();
|
$this->sphinx->ResetFilters();
|
||||||
|
$this->sphinx->ResetOverrides();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function query($query, $offset, $perPage)
|
public function query($query, $offset, $perPage)
|
||||||
@@ -275,10 +251,10 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
$query = $this->parseQuery($query);
|
$query = $this->parseQuery($query);
|
||||||
|
|
||||||
$preg = preg_match('/\s?(recordid|storyid)\s?=\s?([0-9]+)/i', $query, $matches, 0, 0);
|
$preg = preg_match('/\s?recordid\s?=\s?([0-9]+)/i', $query, $matches, 0, 0);
|
||||||
|
|
||||||
if ($preg > 0) {
|
if ($preg > 0) {
|
||||||
$this->sphinx->SetFilter('record_id', array($matches[2]));
|
$this->sphinx->SetFilter('record_id', array($matches[1]));
|
||||||
$query = '';
|
$query = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,17 +291,16 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
try {
|
try {
|
||||||
$record =
|
$record =
|
||||||
new \record_adapter(
|
new \record_adapter(
|
||||||
$this->app,
|
$match['attrs']['sbas_id']
|
||||||
$match['attrs']['sbas_id'],
|
, $match['attrs']['record_id']
|
||||||
$match['attrs']['record_id'],
|
, $resultOffset
|
||||||
$resultOffset
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$results->add($record);
|
$results->add($record);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
$resultOffset++;
|
$resultOffset ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +329,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
$index = 'metadatas' . $this->CRCdatabox($record->get_databox());
|
$index = 'metadatas' . $this->CRCdatabox($record->get_databox());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->options->stemmed() && $this->options->getLocale()) {
|
if ($this->options->stemmed()) {
|
||||||
$index = 'documents' . $this->CRCdatabox($record->get_databox()) . '_stemmed_' . $this->options->getLocale();
|
$index = 'documents' . $this->CRCdatabox($record->get_databox()) . '_stemmed_' . $this->options->getLocale();
|
||||||
} else {
|
} else {
|
||||||
$index = 'documents' . $this->CRCdatabox($record->get_databox());
|
$index = 'documents' . $this->CRCdatabox($record->get_databox());
|
||||||
@@ -463,12 +438,12 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
/**
|
/**
|
||||||
* @todo : enhance : check status in a better way
|
* @todo : enhance : check status in a better way
|
||||||
*/
|
*/
|
||||||
$status_opts = $options->getStatus();
|
|
||||||
foreach ($options->databoxes() as $databox) {
|
foreach ($options->databoxes() as $databox) {
|
||||||
|
$status_opts = $options->getStatus();
|
||||||
foreach ($databox->get_statusbits() as $n => $status) {
|
foreach ($databox->get_statusbits() as $n => $status) {
|
||||||
if (!array_key_exists($n, $status_opts))
|
if ( ! array_key_exists($n, $status_opts))
|
||||||
continue;
|
continue;
|
||||||
if (!array_key_exists($databox->get_sbas_id(), $status_opts[$n]))
|
if ( ! array_key_exists($databox->get_sbas_id(), $status_opts[$n]))
|
||||||
continue;
|
continue;
|
||||||
$crc = crc32($databox->get_sbas_id() . '_' . $n);
|
$crc = crc32($databox->get_sbas_id() . '_' . $n);
|
||||||
$this->sphinx->SetFilter('status', array($crc), ($status_opts[$n][$databox->get_sbas_id()] == '0'));
|
$this->sphinx->SetFilter('status', array($crc), ($status_opts[$n][$databox->get_sbas_id()] == '0'));
|
||||||
@@ -545,10 +520,6 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
$altVersions = array();
|
$altVersions = array();
|
||||||
|
|
||||||
foreach ($words as $word) {
|
|
||||||
$altVersions[$word] = array($word);
|
|
||||||
}
|
|
||||||
|
|
||||||
// As we got words, we look for alternate word for each of them
|
// As we got words, we look for alternate word for each of them
|
||||||
if (function_exists('enchant_broker_init') && $this->options->getLocale()) {
|
if (function_exists('enchant_broker_init') && $this->options->getLocale()) {
|
||||||
$broker = enchant_broker_init();
|
$broker = enchant_broker_init();
|
||||||
@@ -558,7 +529,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
foreach ($words as $word) {
|
foreach ($words as $word) {
|
||||||
|
|
||||||
if (enchant_dict_check($dictionnary, $word) == false) {
|
if (enchant_dict_check($dictionnary, $word) == false) {
|
||||||
$suggs = array_merge(enchant_dict_suggest($dictionnary, $word));
|
$suggs = array_merge(array($word), enchant_dict_suggest($dictionnary, $word));
|
||||||
}
|
}
|
||||||
|
|
||||||
$altVersions[$word] = array_unique($suggs);
|
$altVersions[$word] = array_unique($suggs);
|
||||||
@@ -595,6 +566,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
foreach ($queries as $alt_query) {
|
foreach ($queries as $alt_query) {
|
||||||
$results = $this->sphinx->Query($alt_query, $this->getQueryIndex($alt_query));
|
$results = $this->sphinx->Query($alt_query, $this->getQueryIndex($alt_query));
|
||||||
|
|
||||||
if ($results !== false && isset($results['total_found'])) {
|
if ($results !== false && isset($results['total_found'])) {
|
||||||
if ($results['total_found'] > 0) {
|
if ($results['total_found'] > 0) {
|
||||||
|
|
||||||
@@ -631,7 +603,7 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
$t = "__" . $keyword . "__";
|
$t = "__" . $keyword . "__";
|
||||||
|
|
||||||
$trigrams = "";
|
$trigrams = "";
|
||||||
for ($i = 0; $i < strlen($t) - 2; $i++) {
|
for ($i = 0; $i < strlen($t) - 2; $i ++ ) {
|
||||||
$trigrams .= substr($t, $i, 3) . " ";
|
$trigrams .= substr($t, $i, 3) . " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,12 +618,12 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
$this->resetSphinx();
|
$this->resetSphinx();
|
||||||
|
|
||||||
$this->suggestionClient->SetMatchMode(SPH_MATCH_EXTENDED2);
|
$this->sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
|
||||||
$this->suggestionClient->SetRankingMode(SPH_RANK_WORDCOUNT);
|
$this->sphinx->SetRankingMode(SPH_RANK_WORDCOUNT);
|
||||||
$this->suggestionClient->SetFilterRange("len", $len - 2, $len + 4);
|
$this->sphinx->SetFilterRange("len", $len - 2, $len + 4);
|
||||||
|
|
||||||
$this->suggestionClient->SetSortMode(SPH_SORT_EXTENDED, "@weight DESC");
|
$this->sphinx->SetSortMode(SPH_SORT_EXTENDED, "@weight DESC");
|
||||||
$this->suggestionClient->SetLimits(0, 10);
|
$this->sphinx->SetLimits(0, 10);
|
||||||
|
|
||||||
$indexes = array();
|
$indexes = array();
|
||||||
|
|
||||||
@@ -660,21 +632,27 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$index = implode(',', $indexes);
|
$index = implode(',', $indexes);
|
||||||
$res = $this->suggestionClient->Query($query, $index);
|
|
||||||
|
|
||||||
if ($this->suggestionClient->Status() === false) {
|
$res = $this->sphinx->Query($query, $index);
|
||||||
|
|
||||||
|
if ($this->sphinx->Status() === false) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$res || !isset($res["matches"])) {
|
if ( ! $res || ! isset($res["matches"])) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->sphinx->ResetGroupBy();
|
||||||
|
$this->sphinx->ResetFilters();
|
||||||
|
|
||||||
$words = array();
|
$words = array();
|
||||||
foreach ($res["matches"] as $match) {
|
foreach ($res["matches"] as $match) {
|
||||||
$words[] = $match['attrs']['keyword'];
|
$words[] = $match['attrs']['keyword'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->applyOptions($this->options);
|
||||||
|
|
||||||
return $words;
|
return $words;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,14 +669,14 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
if (count($index_keys) > 0) {
|
if (count($index_keys) > 0) {
|
||||||
if ($this->options->fields() || $this->options->businessFieldsOn()) {
|
if ($this->options->fields() || $this->options->businessFieldsOn()) {
|
||||||
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
||||||
$index = 'metadatas' . implode('_stemmed_' . $this->options->getLocale() . ', metadatas', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
$index = ', metadatas' . implode('_stemmed_' . $this->options->getLocale() . ', metadatas', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
||||||
} else {
|
} else {
|
||||||
$index = 'metadatas' . implode(',metadatas', $index_keys);
|
$index = 'metadatas' . implode(',metadatas', $index_keys);
|
||||||
}
|
}
|
||||||
$index .= ', metas_realtime' . implode(', metas_realtime', $index_keys);
|
$index .= ', metas_realtime' . implode(', metas_realtime', $index_keys);
|
||||||
} else {
|
} else {
|
||||||
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
if ($query !== '' && $this->options->stemmed() && $this->options->getLocale()) {
|
||||||
$index = 'documents' . implode('_stemmed_' . $this->options->getLocale() . ', documents', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
$index = ', documents' . implode('_stemmed_' . $this->options->getLocale() . ', documents', $index_keys) . '_stemmed_' . $this->options->getLocale();
|
||||||
} else {
|
} else {
|
||||||
$index = 'documents' . implode(', documents', $index_keys);
|
$index = 'documents' . implode(', documents', $index_keys);
|
||||||
}
|
}
|
||||||
@@ -743,92 +721,5 @@ class SphinxSearchEngine implements SearchEngineInterface
|
|||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildSuggestions(array $databoxes, $configuration, $threshold = 10)
|
|
||||||
{
|
|
||||||
$executableFinder = new ExecutableFinder();
|
|
||||||
$indexer = $executableFinder->find('indexer');
|
|
||||||
|
|
||||||
if (!is_executable($indexer)) {
|
|
||||||
throw new RuntimeException('Indexer does not seem to be executable');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($databoxes as $databox) {
|
|
||||||
$tmp_file = tempnam(sys_get_temp_dir(), 'sphinx_sugg');
|
|
||||||
|
|
||||||
$cmd = $indexer . ' --config ' . $configuration . ' metadatas' . $this->CRCdatabox($databox)
|
|
||||||
. ' --buildstops ' . $tmp_file . ' 1000000 --buildfreqs';
|
|
||||||
$process = new Process($cmd);
|
|
||||||
$process->run();
|
|
||||||
|
|
||||||
$sql = 'TRUNCATE suggest';
|
|
||||||
$stmt = $databox->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->closeCursor();
|
|
||||||
|
|
||||||
if (null !== $sql = $this->BuildDictionarySQL(file_get_contents($tmp_file), $threshold)) {
|
|
||||||
$stmt = $databox->get_connection()->prepare($sql);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->closeCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
unlink($tmp_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function BuildDictionarySQL($dictionnary, $threshold)
|
|
||||||
{
|
|
||||||
$out = array();
|
|
||||||
|
|
||||||
$n = 1;
|
|
||||||
$lines = explode("\n", $dictionnary);
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
if (trim($line) === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list ( $keyword, $freq ) = explode(" ", trim($line));
|
|
||||||
|
|
||||||
if ($freq < $threshold || strstr($keyword, "_") !== false || strstr($keyword, "'") !== false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctype_digit($keyword)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mb_strlen($keyword) < 3) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trigrams = $this->BuildTrigrams($keyword);
|
|
||||||
|
|
||||||
$out[] = "( $n, '$keyword', '$trigrams', $freq )";
|
|
||||||
$n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($out) {
|
|
||||||
return "INSERT INTO suggest VALUES " . implode(",\n", $out) . ";";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function clearCache()
|
|
||||||
{
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function clearAllCache(\DateTime $date = null)
|
|
||||||
{
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user