Major search overhaul
This commit is contained in:
parent
9f4bc39e55
commit
78ac0574b9
@ -1,27 +1,9 @@
|
||||
<?php
|
||||
class Entity {
|
||||
abstract class Entity {
|
||||
protected function __construct() {
|
||||
|
||||
}
|
||||
|
||||
protected function specify_search($searchterms, $searchfields) {
|
||||
if(array_key_exists('fritext', $searchterms)) {
|
||||
$freeterm = $searchterms['fritext'];
|
||||
unset($searchterms['fritext']);
|
||||
foreach($searchfields as $field) {
|
||||
if(array_key_exists($field, $searchterms)) {
|
||||
$term = $searchterms[$field];
|
||||
if(is_array($term)) {
|
||||
$term[] = $freeterm;
|
||||
} else {
|
||||
$searchterms[$field] = array($term, $freeterm);
|
||||
}
|
||||
} else {
|
||||
$searchterms[$field] = $freeterm;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $searchterms;
|
||||
}
|
||||
|
||||
abstract public function matches($term, $ldap);
|
||||
}
|
||||
?>
|
||||
|
@ -110,58 +110,117 @@ class Product extends Entity {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matches($terms, $matchAll=false) {
|
||||
print('DEBUG $terms in matches: ');
|
||||
var_dump($terms);
|
||||
print('<br><br>');
|
||||
$terms = $this->specify_search($terms, array('brand',
|
||||
'name',
|
||||
'serial',
|
||||
'invoice',
|
||||
'status',
|
||||
'tag'));
|
||||
print('DEBUG $terms POST TRANSLATION: ');
|
||||
var_dump($terms);
|
||||
print('<br><br>');
|
||||
/*
|
||||
Return a list of field-value mappings containing all matching search terms.
|
||||
*/
|
||||
public function matches($terms, $ldap) {
|
||||
$matches = array();
|
||||
foreach($terms as $field => $values) {
|
||||
if(property_exists($this, $field)) {
|
||||
if(match($values, $this->$field)) {
|
||||
$matches[$field] = $this->$field;
|
||||
} else {
|
||||
if($matchAll) {
|
||||
return array();
|
||||
|
||||
// Create a list mapping all basic fields to getters
|
||||
$fields = array('brand' => 'get_brand',
|
||||
'name' => 'get_name',
|
||||
'invoice' => 'get_invoice',
|
||||
'serial' => 'get_serial');
|
||||
|
||||
foreach($terms as $term) {
|
||||
$key = $term->get_key();
|
||||
$matched = false;
|
||||
switch($key) {
|
||||
case 'brand':
|
||||
case 'name':
|
||||
case 'invoice':
|
||||
case 'serial':
|
||||
// If $key is a standard field, check against its value
|
||||
$getter = $fields[$key];
|
||||
$value = $this->$getter();
|
||||
if(match($term, $value)) {
|
||||
//Record a successful match
|
||||
$matches[$key] = $value;
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
} else if(array_key_exists($field, $this->get_info())) {
|
||||
if(match($values, $this->get_info()[$field])) {
|
||||
$matches[$field] = $this->get_info()[$field];
|
||||
} else {
|
||||
if($matchAll) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
} else if($field == 'tag') {
|
||||
foreach($this->get_tags() as $tag) {
|
||||
if(match($values, $tag)) {
|
||||
if(!array_key_exists('tags', $matches)) {
|
||||
$matches['tags'] = array();
|
||||
}
|
||||
$matches['tags'][] = $tag;
|
||||
} else {
|
||||
if($matchAll) {
|
||||
return array();
|
||||
break;
|
||||
case 'tag':
|
||||
// If $key is tag, iterate over the tags
|
||||
$matched_tags = $this->match_tags($term);
|
||||
if($matched_tags) {
|
||||
// Record a successful match
|
||||
$matched = true;
|
||||
if(!isset($matches['tags'])) {
|
||||
// This is the first list of matching tags
|
||||
$matches['tags'] = $matched_tags;
|
||||
} else {
|
||||
// Merge these results with existing results
|
||||
$matches['tags'] = array_unique(
|
||||
array_merge($matches['tags'],
|
||||
$matched_tags));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if($field == 'status') {
|
||||
if(match($values, $this->get_status())) {
|
||||
$matches['status'] = $this->get_status();
|
||||
} else {
|
||||
if($matchAll) {
|
||||
return array();
|
||||
break;
|
||||
case 'fritext':
|
||||
// if $key is fritext:
|
||||
// First check basic fields
|
||||
foreach($fields as $field => $getter) {
|
||||
$value = $this->$getter();
|
||||
if(match($term, $value)) {
|
||||
$matches[$field] = $value;
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then tags
|
||||
$matched_tags = $this->match_tags($term);
|
||||
if($matched_tags) {
|
||||
$matched = true;
|
||||
if(!isset($matches['tags'])) {
|
||||
$matches['tags'] = $matched_tags;
|
||||
} else {
|
||||
$matches['tags'] = array_unique(
|
||||
array_merge($matches['tags'],
|
||||
$matched_tags));
|
||||
}
|
||||
}
|
||||
// Then custom fields
|
||||
foreach($this->get_info() as $field => $value) {
|
||||
if(match($term, $value)) {
|
||||
//Record a successful match
|
||||
$matches[$field] = $value;
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Handle custom fields
|
||||
$info = $this->get_info();
|
||||
if(isset($info[$key])) {
|
||||
// If $key is a valid custom field on this product
|
||||
$value = $info[$key];
|
||||
if(match($term, $value)) {
|
||||
//Record a successful match
|
||||
$matches[$key] = $value;
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// If a mandatory match failed, the entire search has failed
|
||||
// and we return an empty result.
|
||||
if($term->is_mandatory() && !$matched) {
|
||||
return array();
|
||||
}
|
||||
// If a negative match succeeded, the entire search has failed
|
||||
// and we return an empty result.
|
||||
if($term->is_negative() && $matched) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
private function match_tags($term) {
|
||||
$tags = $this->get_tags();
|
||||
$matches = array();
|
||||
foreach($tags as $tag) {
|
||||
if(match($term, $tag)) {
|
||||
$matches[] = $tag;
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
|
@ -28,11 +28,7 @@ class SearchPage extends Page {
|
||||
if(!$this->terms) {
|
||||
return $out;
|
||||
}
|
||||
print('<br>');
|
||||
foreach(array('user', 'product') as $type) {
|
||||
// print('=== DEBUG $type: ');
|
||||
// print_r($type);
|
||||
// print(' ===<br><br>');
|
||||
$result = $this->search($type, $this->terms);
|
||||
if($result) {
|
||||
$out[$type] = $result;
|
||||
@ -42,148 +38,32 @@ class SearchPage extends Page {
|
||||
}
|
||||
|
||||
private function search($type, $terms) {
|
||||
|
||||
/*
|
||||
==================================================
|
||||
|
||||
ORIGINAL CODE || BACKUP || FOR REFERENCE
|
||||
|
||||
$items = get_items($type);
|
||||
$out = array();
|
||||
foreach($items as $item) {
|
||||
$result = $item->matches($terms);
|
||||
if($result) {
|
||||
$out[] = array($item, $result);
|
||||
$matches = array();
|
||||
foreach(get_items($type) as $item) {
|
||||
if($result = $item->matches($terms, $this->ldap)) {
|
||||
$matches[] = array($item, $result);
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
|
||||
==================================================
|
||||
*/
|
||||
|
||||
$mustMatchArray = array();
|
||||
$cannotMatchArray = array();
|
||||
$mayMatchArray = array();
|
||||
foreach($terms as $key => $value) {
|
||||
if(!is_array($value)) {
|
||||
$value = array($value);
|
||||
}
|
||||
foreach($value as $term) {
|
||||
switch ($term[0]) {
|
||||
case "+":
|
||||
if (!array_key_exists($key, $mustMatchArray)) {
|
||||
$mustMatchArray[$key] = array();
|
||||
}
|
||||
$mustMatchArray[$key][] = substr($term, 1);
|
||||
break;
|
||||
case "!":
|
||||
case "-":
|
||||
if (!array_key_exists($key, $cannotMatchArray)) {
|
||||
$cannotMatchArray[$key] = array();
|
||||
}
|
||||
$cannotMatchArray[$key][] = substr($term, 1);
|
||||
break;
|
||||
case "~":
|
||||
if (!array_key_exists($key, $mayMatchArray)) {
|
||||
$mayMatchArray[$key] = array();
|
||||
}
|
||||
$mayMatchArray[$key][] = substr($term, 1);
|
||||
break;
|
||||
default:
|
||||
if (!array_key_exists($key, $mayMatchArray)) {
|
||||
$mayMatchArray[$key] = array();
|
||||
}
|
||||
$mayMatchArray[$key][] = $term;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = get_items($type);
|
||||
$sanitizedItems = array();
|
||||
foreach($items as $item) {
|
||||
$result = $item->matches($mustMatchArray, True);
|
||||
if($result) {
|
||||
$sanitizedItems[] = array($item, $result);
|
||||
}
|
||||
|
||||
// $mustMatchCheck = array();
|
||||
// foreach($mustMatchArray as $mustMatchTerm) {
|
||||
|
||||
// $matchResult = $item->matches($mustMatchTerm);
|
||||
// if($matchResult) {
|
||||
// $mustMatchCheck[] = True;
|
||||
// } else {
|
||||
// $mustMatchCheck[] = False;
|
||||
// }
|
||||
// }
|
||||
// if(in_array(False, $mustMatchCheck, True) === False) {
|
||||
// $sanitizedItems[] = array($item, $matchResult);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// $mustExcludeCheck = array();
|
||||
// foreach($mustExcludeArray as $mustExcludeTerm) {
|
||||
// if($item->matches($mustExcludeTerm)) {
|
||||
// $mustExcludeCheck[] = False;
|
||||
// } else {
|
||||
// $mustExcludeCheck[] = True;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (in_array(False, $mustIncludeCheck, True) === False) {
|
||||
// if(in_array(False, $mustExcludeCheck, True) === True) {
|
||||
|
||||
// // === IF TRUE DO NOTHING ===
|
||||
|
||||
// } else {
|
||||
// foreach ($canIncludeArray as $canIncludeTerm) {
|
||||
// $result = $item->matches($canIncludeTerm);
|
||||
// if($result) {
|
||||
// $out[] = array($item, $result);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
print('DEBUG $sanitizedItems: ');
|
||||
var_dump($sanitizedItems);
|
||||
print('<br><br>');
|
||||
// $out = array();
|
||||
// foreach($sanitizedItems as $sanitizedItem) {
|
||||
// print('DEBUG $sanitizedItem: ');
|
||||
// var_dump($sanitizedItem);
|
||||
// print('<br><br>');
|
||||
|
||||
// if($sanitizedItem->matches($cannotMatchArray)) {
|
||||
|
||||
// // === IF TRUE DO NOTHING ===
|
||||
|
||||
// }
|
||||
// else {
|
||||
// $result = $sanitizedItem->matches($mayMatchArray);
|
||||
// if($result) {
|
||||
// $out[] = array($sanitizedItem, $result);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return array();
|
||||
// return $out;
|
||||
return $sanitizedItems;
|
||||
return $matches;
|
||||
}
|
||||
|
||||
private function translate_terms($terms) {
|
||||
$matches = array();
|
||||
|
||||
// If there is a q-query
|
||||
// and it contains a : character
|
||||
if(isset($terms['q']) && preg_match('/([^:]+):(.*)/',
|
||||
$terms['q'],
|
||||
$matches)) {
|
||||
// remove the q key
|
||||
unset($terms['q']);
|
||||
// insert the term, using whatever came before
|
||||
// the : as the key and whatever came after as the value
|
||||
$terms[$matches[1]] = $matches[2];
|
||||
}
|
||||
$translated = array();
|
||||
foreach($terms as $key => $value) {
|
||||
// Translate all keys into a standard format
|
||||
foreach($terms as $key => $values) {
|
||||
$newkey = $key;
|
||||
switch($key) {
|
||||
case 'q':
|
||||
@ -204,17 +84,38 @@ class SearchPage extends Page {
|
||||
$newkey = 'serial';
|
||||
break;
|
||||
case 'tagg':
|
||||
case 'tags':
|
||||
$newkey = 'tag';
|
||||
break;
|
||||
case 'anteckning':
|
||||
$newkey = 'note';
|
||||
break;
|
||||
case 'e-post':
|
||||
case 'epost':
|
||||
case 'mail':
|
||||
$newkey = 'email';
|
||||
break;
|
||||
case 'status':
|
||||
$value = $this->translate_values($value);
|
||||
// Translate all status values into a standard format
|
||||
$values = $this->translate_values($values);
|
||||
break;
|
||||
}
|
||||
if(!array_key_exists($newkey, $translated)) {
|
||||
$translated[$newkey] = $value;
|
||||
} else {
|
||||
$temp = $translated[$newkey];
|
||||
$translated[$newkey] = array_merge((array)$temp, (array)$value);
|
||||
// Wrap the value in an array if it isn't one
|
||||
if(!is_array($values)) {
|
||||
$values = array($values);
|
||||
}
|
||||
// Make a SearchTerm object from each term
|
||||
foreach($values as $value) {
|
||||
// Check for flags
|
||||
$flag = SearchTerm::OPTIONAL;
|
||||
if(in_array($value[0], array(SearchTerm::MANDATORY,
|
||||
SearchTerm::OPTIONAL,
|
||||
SearchTerm::NEGATIVE))) {
|
||||
$flag = $value[0];
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
// Collect the new SearchTerm
|
||||
$translated[] = new SearchTerm($key, $value, $flag);
|
||||
}
|
||||
}
|
||||
return $translated;
|
||||
@ -270,16 +171,15 @@ class SearchPage extends Page {
|
||||
$terms = '';
|
||||
if($this->terms) {
|
||||
$hidden = '';
|
||||
foreach($this->terms as $key => $value) {
|
||||
if(!is_array($value)) {
|
||||
$value = array($value);
|
||||
}
|
||||
foreach($value as $item) {
|
||||
$terms .= replace(array('term' => ucfirst($key).": $item",
|
||||
'key' => $key,
|
||||
'value' => $item),
|
||||
$this->fragments['search_term']);
|
||||
}
|
||||
foreach($this->terms as $term) {
|
||||
$key = $term->get_key();
|
||||
$flag = $term->get_flag();
|
||||
$query = $term->get_query();
|
||||
$fullterm = ucfirst($key).": ".$flag.$query;
|
||||
$terms .= replace(array('term' => $fullterm,
|
||||
'key' => $key,
|
||||
'value' => $flag.$query),
|
||||
$this->fragments['search_term']);
|
||||
}
|
||||
}
|
||||
$products = 'Inga artiklar hittade.';
|
||||
@ -327,4 +227,53 @@ class SearchPage extends Page {
|
||||
. $data . '<br/>';
|
||||
}
|
||||
}
|
||||
|
||||
class SearchTerm {
|
||||
public const MANDATORY = '+';
|
||||
public const OPTIONAL = '~';
|
||||
public const NEGATIVE = '-';
|
||||
|
||||
private $key;
|
||||
private $query;
|
||||
private $flag;
|
||||
|
||||
public function __construct($key, $query, $flag=SearchTerm::OPTIONAL) {
|
||||
$this->key = $key;
|
||||
$this->query = $query;
|
||||
$this->flag = $flag;
|
||||
}
|
||||
|
||||
public function get_key() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function get_query() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function get_flag() {
|
||||
return $this->flag;
|
||||
}
|
||||
|
||||
public function is_optional() {
|
||||
if($this->flag == SearchTerm::OPTIONAL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function is_mandatory() {
|
||||
if($this->flag == SearchTerm::MANDATORY) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function is_negative() {
|
||||
if($this->flag == SearchTerm::NEGATIVE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -45,37 +45,60 @@ class User extends Entity {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matches($terms, $ldap, $matchAll=false) {
|
||||
$terms = $this->specify_search($terms, array('name',
|
||||
'email',
|
||||
'notes'));
|
||||
public function matches($terms, $ldap) {
|
||||
$matches = array();
|
||||
foreach($terms as $field => $values) {
|
||||
switch($field) {
|
||||
foreach($terms as $term) {
|
||||
// Iterate over the terms
|
||||
$matched = false;
|
||||
$key = $term->get_key();
|
||||
switch($key) {
|
||||
case 'name':
|
||||
if(match($values, $this->name)) {
|
||||
$matches['name'] = $this->name;
|
||||
// If the key is name, check username and displayname
|
||||
$name = $this->get_name();
|
||||
if(match($term, $name)) {
|
||||
$matches['name'] = $name;
|
||||
$matched = true;
|
||||
}
|
||||
$dname = $this->get_displayname($ldap);
|
||||
if(match($values, $dname)) {
|
||||
if(match($term, $dname)) {
|
||||
$matches['displayname'] = $dname;
|
||||
$matched = true;
|
||||
}
|
||||
break;
|
||||
case 'note':
|
||||
// If the key is note, check it.
|
||||
$note = $this->get_note();
|
||||
if($note && match($term, $note)) {
|
||||
$matches['note'] = $note;
|
||||
$matched = true;
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
$email = $this->get_email($ldap, false);
|
||||
if($email && match($values, $email)) {
|
||||
if($email && match($term, $email)) {
|
||||
$matches['email'] = $email;
|
||||
$matched = true;
|
||||
}
|
||||
break;
|
||||
case 'notes':
|
||||
if(match($values, $this->notes)) {
|
||||
$matches['notes'] = $this->notes;
|
||||
case 'fritext':
|
||||
//Check everything if the key is fritext
|
||||
$name = $this->get_name();
|
||||
if(match($term, $name)) {
|
||||
$matches['name'] = $name;
|
||||
$matched = true;
|
||||
}
|
||||
$dname = $this->get_displayname($ldap);
|
||||
if(match($term, $dname)) {
|
||||
$matches['displayname'] = $dname;
|
||||
$matched = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($matchAll && array_diff_assoc($terms, $matches)) {
|
||||
return array();
|
||||
if($term->is_mandatory() && !$matched) {
|
||||
return array();
|
||||
}
|
||||
if($term->is_negative() && $matched) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
@ -253,18 +253,23 @@ function suggest_content($fieldname) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
function match($searchterms, $subject) {
|
||||
if(!is_array($searchterms)) {
|
||||
$searchterms = array($searchterms);
|
||||
}
|
||||
foreach($searchterms as $term) {
|
||||
if(fnmatch('*'.$term.'*', $subject, FNM_CASEFOLD)) {
|
||||
return true;
|
||||
}
|
||||
function match($term, $subject) {
|
||||
if(fnmatch('*'.$term->get_query().'*', $subject, FNM_CASEFOLD)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function match_tags($searchterm, $tags) {
|
||||
$found = array();
|
||||
foreach($tags as $tag) {
|
||||
if(fnmatch('*'.$tag.'*', $searchterm, FNM_CASEFOLD)) {
|
||||
$found[] = $tag;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
function format_date($date) {
|
||||
if($date) {
|
||||
return gmdate('Y-m-d', $date);
|
||||
|
Loading…
x
Reference in New Issue
Block a user