Skip to content
Snippets Groups Projects
Commit f106c74f authored by Peter Rotich's avatar Peter Rotich
Browse files

Merge pull request #2603 from greezybacon/issue/bubble-search-relevance


Fix several search related issues

Reviewed-By: default avatarPeter Rotich <peter@osticket.com>
parents 59323050 0fd3bcb1
No related branches found
No related tags found
No related merge requests found
...@@ -29,6 +29,9 @@ class OrgsAjaxAPI extends AjaxController { ...@@ -29,6 +29,9 @@ class OrgsAjaxAPI extends AjaxController {
$q = $_REQUEST['q']; $q = $_REQUEST['q'];
$limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
if (strlen($q) < 2)
return $this->encode(array());
$orgs = Organization::objects() $orgs = Organization::objects()
->values_flat('id', 'name') ->values_flat('id', 'name')
->limit($limit); ->limit($limit);
...@@ -38,7 +41,7 @@ class OrgsAjaxAPI extends AjaxController { ...@@ -38,7 +41,7 @@ class OrgsAjaxAPI extends AjaxController {
$orgs->order_by(new SqlCode('__relevance__'), QuerySet::DESC) $orgs->order_by(new SqlCode('__relevance__'), QuerySet::DESC)
->distinct('id'); ->distinct('id');
if (!count($orgs) && substr($q, strlen($q)-1) != '*') { if (!count($orgs) && preg_match('`\w$`u', $q)) {
// Do wildcard full-text search // Do wildcard full-text search
$_REQUEST['q'] = $q."*"; $_REQUEST['q'] = $q."*";
return $this->search($type); return $this->search($type);
......
...@@ -47,8 +47,9 @@ class TicketsAjaxAPI extends AjaxController { ...@@ -47,8 +47,9 @@ class TicketsAjaxAPI extends AjaxController {
->limit($limit); ->limit($limit);
$q = $_REQUEST['q']; $q = $_REQUEST['q'];
// Drop at sign in email addresses
$q = str_replace('@', ' ', $q); if (strlen($q) < 2)
return $this->encode(array());
global $ost; global $ost;
$hits = $ost->searcher->find($q, $hits) $hits = $ost->searcher->find($q, $hits)
...@@ -66,7 +67,7 @@ class TicketsAjaxAPI extends AjaxController { ...@@ -66,7 +67,7 @@ class TicketsAjaxAPI extends AjaxController {
->limit($limit) ->limit($limit)
->union($hits); ->union($hits);
} }
elseif (!count($hits) && $q[strlen($q)-1] != '*') { elseif (!count($hits) && preg_match('`\w$`u', $q)) {
// Do wild-card fulltext search // Do wild-card fulltext search
$_REQUEST['q'] = $q.'*'; $_REQUEST['q'] = $q.'*';
return $this->lookup(); return $this->lookup();
......
...@@ -34,6 +34,9 @@ class UsersAjaxAPI extends AjaxController { ...@@ -34,6 +34,9 @@ class UsersAjaxAPI extends AjaxController {
$users=array(); $users=array();
$emails=array(); $emails=array();
if (strlen($q) < 2)
return $this->encode(array());
if (!$type || !strcasecmp($type, 'remote')) { if (!$type || !strcasecmp($type, 'remote')) {
foreach (AuthenticationBackend::searchUsers($q) as $u) { foreach (AuthenticationBackend::searchUsers($q) as $u) {
$name = new UsersName(array('first' => $u['first'], 'last' => $u['last'])); $name = new UsersName(array('first' => $u['first'], 'last' => $u['last']));
...@@ -55,7 +58,7 @@ class UsersAjaxAPI extends AjaxController { ...@@ -55,7 +58,7 @@ class UsersAjaxAPI extends AjaxController {
$users->order_by(new SqlCode('__relevance__'), QuerySet::DESC) $users->order_by(new SqlCode('__relevance__'), QuerySet::DESC)
->distinct('id'); ->distinct('id');
if (!count($emails) && !count($users) && substr($q, strlen($q)-1) != '*') { if (!count($emails) && !count($users) && preg_match('`\w$`u', $q)) {
// Do wildcard full-text search // Do wildcard full-text search
$_REQUEST['q'] = $q."*"; $_REQUEST['q'] = $q."*";
return $this->search($type); return $this->search($type);
......
...@@ -306,7 +306,7 @@ class MysqlSearchBackend extends SearchBackend { ...@@ -306,7 +306,7 @@ class MysqlSearchBackend extends SearchBackend {
// Quote things like email addresses // Quote things like email addresses
function quote($query) { function quote($query) {
$parts = array(); $parts = array();
if (!preg_match_all('`([^\s"\']+)|"[^"]*"|\'[^\']*\'`', $query, $parts, if (!preg_match_all('`(?:([^\s"\']+)|"[^"]*"|\'[^\']*\')(\s*)`', $query, $parts,
PREG_SET_ORDER)) PREG_SET_ORDER))
return $query; return $query;
...@@ -319,21 +319,22 @@ class MysqlSearchBackend extends SearchBackend { ...@@ -319,21 +319,22 @@ class MysqlSearchBackend extends SearchBackend {
$char = strpos($m[1], '"') ? "'" : '"'; $char = strpos($m[1], '"') ? "'" : '"';
$m[0] = $char . $m[0] . $char; $m[0] = $char . $m[0] . $char;
} }
$results[] = $m[0]; $results[] = $m[0].$m[2];
} }
return implode(' ', $results); return implode('', $results);
} }
function find($query, QuerySet $criteria, $addRelevance=true) { function find($query, QuerySet $criteria, $addRelevance=true) {
global $thisstaff; global $thisstaff;
// MySQL usually doesn't handle words shorter than three letters
// (except with special configuration)
if (strlen($query) < 3)
return $criteria;
$criteria = clone $criteria; $criteria = clone $criteria;
$mode = ' IN NATURAL LANGUAGE MODE'; $mode = ' IN NATURAL LANGUAGE MODE';
// If using boolean operators, search in boolean mode. This regex
// will ensure proper placement of operators, whitespace, and quotes
// in an effort to avoid crashing the query at MySQL
$query = $this->quote($query);
// According to the MySQL full text boolean mode, this grammar is // According to the MySQL full text boolean mode, this grammar is
// assumed: // assumed:
...@@ -357,6 +358,10 @@ class MysqlSearchBackend extends SearchBackend { ...@@ -357,6 +358,10 @@ class MysqlSearchBackend extends SearchBackend {
if (preg_match('`(^|\s)["()<>~+-]`u', $query, $T = array()) if (preg_match('`(^|\s)["()<>~+-]`u', $query, $T = array())
&& preg_match("`^{$BOOLEAN}$`u", $query, $T = array()) && preg_match("`^{$BOOLEAN}$`u", $query, $T = array())
) { ) {
// If using boolean operators, search in boolean mode. This regex
// will ensure proper placement of operators, whitespace, and quotes
// in an effort to avoid crashing the query at MySQL
$query = $this->quote($query);
$mode = ' IN BOOLEAN MODE'; $mode = ' IN BOOLEAN MODE';
} }
#elseif (count(explode(' ', $query)) == 1) #elseif (count(explode(' ', $query)) == 1)
...@@ -376,7 +381,7 @@ class MysqlSearchBackend extends SearchBackend { ...@@ -376,7 +381,7 @@ class MysqlSearchBackend extends SearchBackend {
$criteria->extra(array( $criteria->extra(array(
'tables' => array( 'tables' => array(
str_replace(array(':', '{}'), array(TABLE_PREFIX, $search), str_replace(array(':', '{}'), array(TABLE_PREFIX, $search),
"(SELECT COALESCE(Z3.`object_id`, Z5.`ticket_id`, Z8.`ticket_id`) as `ticket_id`, {} AS `relevance` FROM `:_search` Z1 LEFT JOIN `:thread_entry` Z2 ON (Z1.`object_type` = 'H' AND Z1.`object_id` = Z2.`id`) LEFT JOIN `:thread` Z3 ON (Z2.`thread_id` = Z3.`id` AND Z3.`object_type` = 'T') LEFT JOIN `:ticket` Z5 ON (Z1.`object_type` = 'T' AND Z1.`object_id` = Z5.`ticket_id`) LEFT JOIN `:user` Z6 ON (Z6.`id` = Z1.`object_id` and Z1.`object_type` = 'U') LEFT JOIN `:organization` Z7 ON (Z7.`id` = Z1.`object_id` AND Z7.`id` = Z6.`org_id` AND Z1.`object_type` = 'O') LEFT JOIN :ticket Z8 ON (Z8.`user_id` = Z6.`id`) WHERE {}) Z1"), "(SELECT COALESCE(Z3.`object_id`, Z5.`ticket_id`, Z8.`ticket_id`) as `ticket_id`, SUM({}) AS `relevance` FROM `:_search` Z1 LEFT JOIN `:thread_entry` Z2 ON (Z1.`object_type` = 'H' AND Z1.`object_id` = Z2.`id`) LEFT JOIN `:thread` Z3 ON (Z2.`thread_id` = Z3.`id` AND Z3.`object_type` = 'T') LEFT JOIN `:ticket` Z5 ON (Z1.`object_type` = 'T' AND Z1.`object_id` = Z5.`ticket_id`) LEFT JOIN `:user` Z6 ON (Z6.`id` = Z1.`object_id` and Z1.`object_type` = 'U') LEFT JOIN `:organization` Z7 ON (Z7.`id` = Z1.`object_id` AND Z7.`id` = Z6.`org_id` AND Z1.`object_type` = 'O') LEFT JOIN :ticket Z8 ON (Z8.`user_id` = Z6.`id`) WHERE {} GROUP BY `ticket_id`) Z1"),
) )
)); ));
$criteria->filter(array('ticket_id'=>new SqlCode('Z1.`ticket_id`'))); $criteria->filter(array('ticket_id'=>new SqlCode('Z1.`ticket_id`')));
......
...@@ -90,10 +90,10 @@ if ($thisclient->canSeeOrgTickets()) { ...@@ -90,10 +90,10 @@ if ($thisclient->canSeeOrgTickets()) {
// Perform basic search // Perform basic search
if ($settings['keywords']) { if ($settings['keywords']) {
$q = $settings['keywords']; $q = trim($settings['keywords']);
if (is_numeric($q)) { if (is_numeric($q)) {
$tickets->filter(array('number__startswith'=>$q)); $tickets->filter(array('number__startswith'=>$q));
} else { //Deep search! } elseif (strlen($q) > 2) { //Deep search!
// Use the search engine to perform the search // Use the search engine to perform the search
$tickets = $ost->searcher->find($q, $tickets); $tickets = $ost->searcher->find($q, $tickets);
} }
......
...@@ -93,18 +93,22 @@ case 'search': ...@@ -93,18 +93,22 @@ case 'search':
)); ));
} }
} }
elseif ($_REQUEST['query']) { elseif (isset($_REQUEST['query'])
&& ($q = trim($_REQUEST['query']))
&& strlen($q) > 2
) {
// [Search] click, consider keywords // [Search] click, consider keywords
$__tickets = $ost->searcher->find($_REQUEST['query'], $tickets); $__tickets = $ost->searcher->find($q, $tickets);
if (!count($__tickets)) { if (!count($__tickets) && preg_match('`\w$`u', $q)) {
// Do wildcard search if no hits // Do wildcard search if no hits
$__tickets = $ost->searcher->find($_REQUEST['query'].'*', $tickets); $__tickets = $ost->searcher->find($q.'*', $tickets);
} }
$tickets = $__tickets->distinct('ticket_id'); $tickets = $__tickets;
$has_relevance = true; $has_relevance = true;
} }
if (count($tickets) == 1) { if (count($tickets) == 1) {
// Redirect to ticket page // Redirect to ticket page
Http::redirect('tickets.php?id='.$tickets[0]->getId());
} }
// Clear sticky search queue // Clear sticky search queue
unset($_SESSION[$queue_key]); unset($_SESSION[$queue_key]);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment