Skip to content

Instantly share code, notes, and snippets.

@jhoffmann
Created October 12, 2015 19:39
Show Gist options
  • Save jhoffmann/a4efad333d7d82b612c0 to your computer and use it in GitHub Desktop.
Save jhoffmann/a4efad333d7d82b612c0 to your computer and use it in GitHub Desktop.
<?php
/**
* This allows a user to utilize ElasticSearch (FTS) query syntax when filtering records
* for a module's list view
*
* For the supported syntax, see:
* http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html#_simple_query_string_syntax
*
* @link PSD-1310
* @author jwh
*/
require_once('clients/base/api/FilterApi.php');
class FTSFilterApi extends FilterApi
{
// Point the existing endpoint at our custom filter method.
public function registerApiRest()
{
return array(
'filterModuleGet' => array(
'reqType' => 'GET',
'path' => array('<module>', 'filter'),
'pathVars' => array('module', ''),
'method' => 'filterCustomList',
'jsonParams' => array('filter'),
'shortHelp' => 'Lists filtered records.',
'longHelp' => 'include/api/help/module_filter_get_help.html',
),
);
}
/**
* Custom filter endpoint to allow for boolean searches of full text fields using FTS.
*
* @link PSD-1310
* @author jwh
*/
public function filterCustomList(ServiceBase $api, array $args, $acl = 'list')
{
if (!empty($args['filter'])) {
$args['filter'] = $this->transformBooleans($args['filter'], $args['module']);
}
return $this->filterList($api, $args, $acl);
}
/**
* Go through our filter arguments and find our $boolean search strings,
* pass them to FTS, and use the result as a $in filter. We also have to clear our
* custom $boolean keys or it breaks SugarQuery.
*
* @link PSD-1310
* @author jwh
*/
protected function transformBooleans(array $params, $module, &$parent = null)
{
foreach ($params as $k => &$v) {
if (is_array($v) && in_array('$boolean', array_keys($v), true)) {
// Clear out our pseudo filter and populate it with the results from our Elastic search
$matches = $this->filterUsingFTS(current($v), $module);
// Seed the $in array with a 'none' string so if we don't get results from FTS we don't just
// show the results using the remaining filter params
$parent[-99]['id']['$in'] = array_merge((array) $parent[-99]['id']['$in'], array('none'), $matches);
unset($params[$k]);
} elseif (is_array($v)) {
$params[$k] = $this->transformBooleans($v, $module, $params);
}
}
return $params;
}
/**
* Pass our query string over to FTS and return a set of matching records. We limit the result set to 500
* records for performance reasons, keeping in mind other filter params and ACL access may reduce the visible
* list as well. We can't tell FTS to only return records where the status is 'Open' for example, but the user
* might be filtering on that.
*
* @link PSD-1310
* @author jwh
*/
protected function filterUsingFTS($q, $module) {
$searchEngine = SugarSearchEngineFactory::getInstance(SugarSearchEngineFactory::getFTSEngineNameFromConfig());
if ($searchEngine instanceof SugarSearchEngineAbstractBase) {
$rs = $searchEngine->search($q, 0, 500, array('moduleFilter' => array($module)));
if ($rs !== null) {
foreach ($rs as $result) {
$esMatches[] = $result->getId();
}
}
return (array) $esMatches;
}
return array();
}
// Uncomment in order to log the SQL being generated for debug purposes
// public function filterListSetup(ServiceBase $api, array $args, $acl = 'list')
// {
// $p = parent::filterListSetup($api, $args, $acl);
// _ppl($p[1]->compileSql());
// return $p;
// }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment