Blog

Customizing SearchApiQuery Filters

John Brandenburg

Technical Manager, Forum One

I had the opportunity to play with Search API filters to modify Solr searches lately as we had to implement a complex set of filtering rules for a Drupal project. This becomes necessary because Views filters don’t easily support our conditions, and you really don’t want to rely on the node access system because your goal is to alter the result set instead of forbidding access altogether. Using SearchApiQuery can be tricky at first, but with practice, one can get the hand of using it effectively.

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.

Written By

John Brandenburg

Technical Manager, Forum One