Blog Insights
Customizing SearchApiQuery Filters
First, the easiest way to modify these searches is to implement hook_search_api_query_alter() in one of your modules. Since we use Features heavily, we usually have a search focused Features module, and write any related code within the appropriate feature’s .module file. This hook has one parameter, which is the SearchApiQuery object itself.
/**
* Implements hook_search_api_query_alter().
*/
function my_module_search_api_query_alter($query) {
}
Remember: we don’t need to pass objects by reference since objects are always passed by reference, this means we also don’t need to return anything from this hook implementation. The SearchApiQuery object provides several operations (presented in its documentation) to modify its search query, but I’m just going to focus on two for today.
$filter = $query->createFilter($conjunction);
…where $conjunction is a string with a value of either an AND or OR condition. This creates a new SearchApiQueryFilter object that can be added back to the $query later, after we’ve added conditions to it.
$query->filter($filter);
Once we have a filter object, we can add conditions, or other filters to it:
$filter->condition('field_name', 'value');
$query->filter($filter);
As any programmer knows, nesting conditions can be simplified into its basic parts. So the following statement.
if ($this == $that && ($here > $there || $foo == $bar))
…can be broken down into five evaluations (working backwards allows us to resolve the child conditions before the parents):
$foo == $bar
$here > $there
Result of evaluation 1 || Result of evaluation 2
$this == $that
Result of evaluation 4 && Result of evaluation 3
Working with Search API filters is no different than constructing the above nested If statement.
First, we want to create a base filter for our evaluation. Here I am using an example that the node should be published, but this would typically be already added by your view or use of node access integration by checking the “Node access” option sat admin/config/search/search_api/index/[index name]/workflow.
$base_filter = $query->createFilter('AND');
$base_filter->condition('status', 1);
Now to add this sub filter to the base filter, just pass it to filter().
$base_filter->filter($subfilter);
and then add the base filter back to the query object,
$query->filter($base_filter);
That’s all there is to working with the SearchApiQuery interface. The complete example looks something like this:
/**
* Implements hook_search_api_query_alter().
*/
function my_module_search_api_query_alter($query) {
$base_filter = $query->createFilter('AND');
$base_filter->condition('status', 1);
$subfilter = $query->createFilter('OR');
$subfilter->condition('field_my_field', 'value');
$subfilter->condition('field_other_field', 'value2');
$base_filter->filter($subfilter);
$query->filter($base_filter);
}
Implementing your filters this way should really be a last resort. The other option that should be considered first is the node access system, which can define node-level permissions that can get indexed in services like Solr.