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 * To be or-ed to query for $_pattern in regular columns of main-table.
* returning all auto-ids of custom-fields matching the search-criteria
* *
* @param string $_pattern search pattern incl. * or ? as wildcard, if no wildcards used we append and prepend one! * @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! * @return string with SQL fragment running on main table: "id IN (SELECT id FROM extra-table WHERE extra_value like '$pattern')"
* @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
*/ */
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 '. $sql = ' OR '.$this->table_name.'.'.$this->autoinc_id.' IN (SELECT '.$this->autoinc_id.
$this->table_name.'.'.$this->autoinc_id.' IN (SELECT '.$this->autoinc_id.
' FROM '.$this->extra_table.' WHERE '.$this->extra_value.' '. ' FROM '.$this->extra_table.' WHERE '.$this->extra_value.' '.
$this->db->capabilities[Db::CAPABILITY_CASE_INSENSITIV_LIKE].' '. $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 * Parse an array of search criteria into something that can be passed on
* to the DB * to the DB
* *
* @param array $criteria * @param array $_criteria
* @param string $wildcard ='' appended befor and after each 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 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 * @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
* @return Array * @return Array
* @throws Api\Db\Exception * @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) foreach($criteria as $col => $val)
{ {
if (is_int($col)) if (is_int($col))
@ -1351,9 +1351,17 @@ class Base
$op = 'OR'; $op = 'OR';
break; break;
} }
$search_token = $wildcard.str_replace(array('%','_','*','?'),array('\\%','\\_','%','_'),$token).$wildcard;
$token_filter = ' '.call_user_func_array(array($GLOBALS['egw']->db,'concat'),$columns).' '. $token_filter = ' '.call_user_func_array(array($GLOBALS['egw']->db,'concat'),$columns).' '.
$this->db->capabilities['case_insensitive_like'] . ' ' . $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 // 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 // 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) 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); $token = strtok($break);
} }