integrate custom-field search in token processing

to allow to use something like: "<regluar-match-token> +<custom-field-match-token>" to return correct entries matching both
also no longer search private custom-fields not accessible by current user (gives a slower query, if private cfs exist)
This commit is contained in:
Ralf Becker 2019-02-19 16:21:02 +01:00
parent 5086e6cca3
commit 1d8f2a606b
2 changed files with 35 additions and 21 deletions

View File

@ -407,31 +407,37 @@ class Storage extends Storage\Base
}
/**
* Return criteria array for a given search pattern
* Return SQL fragment to search custom-fields for given $pattern
*
* Reimplemented to handle search in custom-fields by ORing with a sub-query
* returning all auto-ids of custom-fields matching the search-criteria
* To be or-ed to query for $_pattern in regular columns of main-table.
*
* @param string $_pattern search pattern incl. * or ? as wildcard, if no wildcards used we append and prepend one!
* @param string &$wildcard ='' on return wildcard char to use, if pattern does not already contain wildcards!
* @param string &$op ='AND' on return boolean operation to use, if pattern does not start with ! we use OR else AND
* @param string $extra_col =null extra column to search
* @param array $search_cols =array() List of columns to search. If not provided, all columns in $this->db_cols will be considered
* @return array or column => value pairs
* @return string with SQL fragment running on main table: "id IN (SELECT id FROM extra-table WHERE extra_value like '$pattern')"
*/
public function search2criteria($_pattern,&$wildcard='',&$op='AND',$extra_col=null, $search_cols = array())
public function cf_match($_pattern)
{
$pattern = $wildcard.$_pattern.$wildcard;
static $private_cfs=null;
$criteria = parent::search2criteria($_pattern, $wildcard, $op, $extra_col, $search_cols);
if (!$this->customfields) return ''; // no custom-fields --> no search
$criteria[0] = '('.$criteria[0].' OR '.
$this->table_name.'.'.$this->autoinc_id.' IN (SELECT '.$this->autoinc_id.
$sql = ' OR '.$this->table_name.'.'.$this->autoinc_id.' IN (SELECT '.$this->autoinc_id.
' FROM '.$this->extra_table.' WHERE '.$this->extra_value.' '.
$this->db->capabilities[Db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.
$GLOBALS['egw']->db->quote($pattern).'))';
$GLOBALS['egw']->db->quote($_pattern);
return $criteria;
// check if there are private cfs not available to current user --> filter by available cfs
if (!isset($private_cfs))
{
$private_cfs = array_diff_key(
Storage\Customfields::get($this->app, true, null, $this->db), // true: get private cfs too
$this->customfields);
//error_log(__METHOD__."() private_cfs=".array2string($private_cfs));
}
if ($private_cfs)
{
$sql .= ' AND '.$this->db->expression($this->extra_table, array($this->extra_key => array_keys($this->customfields)));
}
return $sql.')';
}
/**

View File

@ -1056,16 +1056,16 @@ class Base
* Parse an array of search criteria into something that can be passed on
* to the DB
*
* @param array $criteria
* @param array $_criteria
* @param string $wildcard ='' appended befor and after each criteria
* @param boolean $empty =false False=empty criteria are ignored in query, True=empty have to be empty in row
* @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
* @return Array
* @throws Api\Db\Exception
*/
protected function parse_search(Array $criteria, $wildcard, $empty, $op)
protected function parse_search(Array $_criteria, $wildcard, $empty, $op)
{
$criteria = $this->data2db($criteria);
$criteria = $this->data2db($_criteria);
foreach($criteria as $col => $val)
{
if (is_int($col))
@ -1351,9 +1351,17 @@ class Base
$op = 'OR';
break;
}
$search_token = $wildcard.str_replace(array('%','_','*','?'),array('\\%','\\_','%','_'),$token).$wildcard;
$token_filter = ' '.call_user_func_array(array($GLOBALS['egw']->db,'concat'),$columns).' '.
$this->db->capabilities['case_insensitive_like'] . ' ' .
$GLOBALS['egw']->db->quote($wildcard.str_replace(array('%','_','*','?'),array('\\%','\\_','%','_'),$token).$wildcard);
$GLOBALS['egw']->db->quote($search_token);
// if we have customfields and this is Api\Storage (not Api\Storage\Base)
if (is_a($this, __NAMESPACE__))
{
// add custom-field search: OR id IN (SELECT id FROM extra_table WHERE extra_value LIKE '$search_token')
$token_filter .= $this->cf_match($search_token);
}
// Compare numeric token as equality for numeric columns
// skip user-wildcards (*,?) in is_numeric test, but not SQL wildcards, which get escaped and give sql-error
@ -1376,10 +1384,10 @@ class Base
}
if(count($numeric_filter) > 0)
{
$token_filter = '(' . $token_filter . ' OR ' . implode(' OR ', $numeric_filter) . ')';
$token_filter .= ' OR ' . implode(' OR ', $numeric_filter);
}
}
$criteria[$op][] = $token_filter;
$criteria[$op][] = '('.$token_filter.')';
$token = strtok($break);
}