2011-03-28 14:21:28 +00:00
|
|
|
<?
|
|
|
|
//Require base class
|
2013-04-30 18:18:07 +00:00
|
|
|
if (!extension_loaded('sphinx')) {
|
2011-03-28 14:21:28 +00:00
|
|
|
require(SERVER_ROOT.'/classes/sphinxapi.php');
|
|
|
|
}
|
|
|
|
|
|
|
|
class SPHINX_SEARCH extends SphinxClient {
|
2013-05-28 08:01:02 +00:00
|
|
|
private $Index = '*';
|
2011-03-28 14:21:28 +00:00
|
|
|
public $TotalResults = 0;
|
|
|
|
public $Queries = array();
|
|
|
|
public $Time = 0.0;
|
|
|
|
public $Filters = array();
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
function SPHINX_SEARCH() {
|
|
|
|
parent::__construct();
|
|
|
|
$this->SetServer(SPHINX_HOST, SPHINX_PORT);
|
|
|
|
$this->SetMatchMode(SPH_MATCH_EXTENDED2);
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
/****************************************************************
|
|
|
|
/--- Search function --------------------------------------------
|
2013-02-22 08:00:24 +00:00
|
|
|
|
|
|
|
This function queries sphinx for whatever is in $Query, in
|
2011-03-28 14:21:28 +00:00
|
|
|
extended2 mode. It then fetches the records for each primary key
|
|
|
|
from memcached (by joining $CachePrefix and the primary key), and
|
2013-02-22 08:00:24 +00:00
|
|
|
fetches the fields needed ($ReturnData) from the memcached
|
2011-03-28 14:21:28 +00:00
|
|
|
result.
|
2013-02-22 08:00:24 +00:00
|
|
|
|
|
|
|
Any keys not found in memcached are then queried in MySQL, using
|
2011-03-28 14:21:28 +00:00
|
|
|
$SQL. They are then cached, and merged with the memcached matches
|
|
|
|
and returned.
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
$Query - sphinx query
|
|
|
|
$CachePrefix - Prefix for memcache key (no underscore)
|
|
|
|
$CacheLength - How long to store data in the cache, if it's found by MySQL
|
2013-02-22 08:00:24 +00:00
|
|
|
$ReturnData - Array of keys to the array in memcached to return.
|
2011-03-28 14:21:28 +00:00
|
|
|
If empty, return all.
|
|
|
|
$SQL - SQL query to fetch results not found in memcached
|
|
|
|
- Should take the format of:
|
|
|
|
SELECT fields FROM table WHERE primary key IN(%ids)
|
|
|
|
where %ids will be replaced by a list of IDs not found in memcached
|
|
|
|
$IDColumn - The primary key of the SQL table - must be the
|
|
|
|
same primary key returned by sphinx!
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
****************************************************************/
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-05-28 08:01:02 +00:00
|
|
|
function search($Query = '', $CachePrefix = '', $CacheLength = 0, $ReturnData = array(), $SQL = '', $IDColumn = 'ID') {
|
2011-03-28 14:21:28 +00:00
|
|
|
global $Cache, $DB;
|
2013-05-28 08:01:02 +00:00
|
|
|
$QueryStartTime = microtime(true);
|
2011-03-28 14:21:28 +00:00
|
|
|
$Result = $this->Query($Query, $this->Index);
|
2013-05-28 08:01:02 +00:00
|
|
|
$QueryEndTime = microtime(true);
|
2011-03-28 14:21:28 +00:00
|
|
|
|
|
|
|
$Filters = array();
|
2013-04-30 18:18:07 +00:00
|
|
|
foreach ($this->Filters as $Name => $Values) {
|
|
|
|
foreach ($Values as $Value) {
|
2013-07-10 00:08:53 +00:00
|
|
|
$Filters[] = "$Name - $Value";
|
2011-09-10 08:00:10 +00:00
|
|
|
}
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2011-09-10 08:00:10 +00:00
|
|
|
|
2013-07-10 00:08:53 +00:00
|
|
|
$this->Queries[] = array("Params: $Query Filters: ".implode(', ', $Filters).' Indicies: '.$this->Index, ($QueryEndTime - $QueryStartTime) * 1000);
|
2013-05-28 08:01:02 +00:00
|
|
|
$this->Time += ($QueryEndTime - $QueryStartTime) * 1000;
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-04-30 18:18:07 +00:00
|
|
|
if ($Result === false) {
|
|
|
|
if ($this->_connerror && !$Cache->get_value('sphinx_crash_reported')) {
|
2011-03-28 14:21:28 +00:00
|
|
|
send_irc('PRIVMSG '.ADMIN_CHAN.' :!dev Connection to searchd failed');
|
|
|
|
$Cache->cache_value('sphinx_crash_reported', 1, 3600);
|
|
|
|
}
|
2013-07-10 00:08:53 +00:00
|
|
|
send_irc('PRIVMSG '.LAB_CHAN." :Search for \"$Query\" (".str_replace("\n", '', print_r($this->Filters, true)).') failed: '.$this->GetLastError());
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2012-08-08 08:00:12 +00:00
|
|
|
$this->TotalResults = $Result['total_found'];
|
2011-03-28 14:21:28 +00:00
|
|
|
$this->SearchTime = $Result['time'];
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-04-30 18:18:07 +00:00
|
|
|
if (empty($Result['matches'])) {
|
2011-03-28 14:21:28 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$Matches = $Result['matches'];
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
$MatchIDs = array_keys($Matches);
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
$NotFound = array();
|
|
|
|
$Skip = array();
|
2013-04-30 18:18:07 +00:00
|
|
|
if (!empty($ReturnData)) {
|
2011-03-28 14:21:28 +00:00
|
|
|
$AllFields = false;
|
|
|
|
} else {
|
|
|
|
$AllFields = true;
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-04-30 18:18:07 +00:00
|
|
|
foreach ($MatchIDs as $Match) {
|
2011-03-28 14:21:28 +00:00
|
|
|
$Matches[$Match] = $Matches[$Match]['attrs'];
|
2013-04-30 18:18:07 +00:00
|
|
|
if (!empty($CachePrefix)) {
|
2013-07-10 00:08:53 +00:00
|
|
|
$Data = $Cache->get_value($CachePrefix."_$Match");
|
2013-04-30 18:18:07 +00:00
|
|
|
if ($Data == false) {
|
|
|
|
$NotFound[] = $Match;
|
2011-03-28 14:21:28 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-30 18:18:07 +00:00
|
|
|
$NotFound[] = $Match;
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2013-04-30 18:18:07 +00:00
|
|
|
if (!$AllFields) {
|
2011-03-28 14:21:28 +00:00
|
|
|
// Populate list of fields to unset (faster than picking out the ones we need). Should only be run once, on the first cache key
|
2013-04-30 18:18:07 +00:00
|
|
|
if (empty($Skip)) {
|
|
|
|
foreach (array_keys($Data) as $Key) {
|
|
|
|
if (!in_array($Key, $ReturnData)) {
|
|
|
|
$Skip[] = $Key;
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
|
|
|
}
|
2013-04-30 18:18:07 +00:00
|
|
|
if (empty($Skip)) {
|
2011-03-28 14:21:28 +00:00
|
|
|
$AllFields = true;
|
|
|
|
}
|
|
|
|
}
|
2013-04-30 18:18:07 +00:00
|
|
|
foreach ($Skip as $Key) {
|
2011-03-28 14:21:28 +00:00
|
|
|
unset($Data[$Key]);
|
|
|
|
}
|
|
|
|
reset($Skip);
|
|
|
|
}
|
2013-04-30 18:18:07 +00:00
|
|
|
if (!empty($Data)) {
|
2011-03-28 14:21:28 +00:00
|
|
|
$Matches[$Match] = array_merge($Matches[$Match], $Data);
|
|
|
|
}
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-04-30 18:18:07 +00:00
|
|
|
if ($SQL != '') {
|
|
|
|
if (!empty($NotFound)) {
|
2013-05-28 08:01:02 +00:00
|
|
|
$DB->query(str_replace('%ids', implode(',', $NotFound), $SQL));
|
2013-04-30 18:18:07 +00:00
|
|
|
while ($Data = $DB->next_record(MYSQLI_ASSOC)) {
|
2011-03-28 14:21:28 +00:00
|
|
|
$Matches[$Data[$IDColumn]] = array_merge($Matches[$Data[$IDColumn]], $Data);
|
|
|
|
$Cache->cache_value($CachePrefix.'_'.$Data[$IDColumn], $Data, $CacheLength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-07-10 00:08:53 +00:00
|
|
|
$Matches = array('matches' => $Matches, 'notfound' => $NotFound);
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
return $Matches;
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2012-08-08 08:00:12 +00:00
|
|
|
function limit($Start, $Length) {
|
|
|
|
$Start = (int)$Start;
|
|
|
|
$Length = (int)$Length;
|
2013-05-28 08:01:02 +00:00
|
|
|
$this->SetLimits($Start, $Length, $Start + $Length);
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
function set_index($Index) {
|
|
|
|
$this->Index = $Index;
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2013-05-28 08:01:02 +00:00
|
|
|
function set_filter($Name, $Vals, $Exclude = false) {
|
2013-04-30 18:18:07 +00:00
|
|
|
foreach ($Vals as $Val) {
|
|
|
|
if ($Exclude) {
|
2012-09-02 08:00:26 +00:00
|
|
|
$this->Filters[$Name][] = "!$Val";
|
|
|
|
} else {
|
|
|
|
$this->Filters[$Name][] = $Val;
|
|
|
|
}
|
2011-09-10 08:00:10 +00:00
|
|
|
}
|
|
|
|
$this->SetFilter($Name, $Vals, $Exclude);
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
function set_filter_range($Name, $Min, $Max, $Exclude) {
|
2013-07-10 00:08:53 +00:00
|
|
|
$this->Filters[$Name] = array("$Min-$Max");
|
2011-03-28 14:21:28 +00:00
|
|
|
$this->SetFilterRange($Name, $Min, $Max, $Exclude);
|
|
|
|
}
|
2013-02-22 08:00:24 +00:00
|
|
|
|
2011-03-28 14:21:28 +00:00
|
|
|
function escape_string($String) {
|
2012-10-14 08:00:20 +00:00
|
|
|
return strtr($String, array(
|
|
|
|
'('=>'\(',
|
|
|
|
')'=>'\)',
|
|
|
|
'|'=>'\|',
|
|
|
|
'-'=>'\-',
|
|
|
|
'@'=>'\@',
|
|
|
|
'~'=>'\~',
|
|
|
|
'&'=>'\&',
|
|
|
|
'!'=>'\!',
|
|
|
|
'"'=>'\"',
|
|
|
|
'/'=>'\/',
|
|
|
|
'\\'=>'\\\\',
|
|
|
|
'*'=>'\*',
|
|
|
|
'?'=>'\?',
|
|
|
|
'^'=>'\^',
|
|
|
|
'$'=>'\$',
|
|
|
|
'='=>'\='));
|
|
|
|
}
|
2011-03-28 14:21:28 +00:00
|
|
|
}
|
|
|
|
?>
|