Custom Search Facets¶
The problems¶
Search api index feature export is messy¶
The feature exports for the dkan core search api indexes are in fact entity_imports
and the value being imported is a json string. You can’t get around this with features_overrides
, it does not offer a way to override individual items of the feature.
/**
* Implements hook_default_search_api_index().
*/
function dkan_sitewide_search_db_default_search_api_index() {
$items = array();
$items['datasets'] = entity_import('search_api_index', '{
"name" : "datasets",
"machine_name" : "datasets",
...
"fields" : {
"author" : { "type" : "integer", "entity_type" : "user" },
"changed" : { "type" : "date" },
"created" : { "type" : "date" },
"field_author" : { "type" : "string" },
"field_license" : { "type" : "string" },
"field_resources:body:value" : { "type" : "list\\u003Ctext\\u003E" },
"field_resources:field_format" : { "type" : "list\\u003Cinteger\\u003E", "entity_type" : "taxonomy_term" },
"field_tags" : { "type" : "list\\u003Cinteger\\u003E", "entity_type" : "taxonomy_term" },
"field_topic" : { "type" : "list\\u003Cinteger\\u003E", "entity_type" : "taxonomy_term" },
"og_group_ref" : { "type" : "list\\u003Cinteger\\u003E", "entity_type" : "node" },
"search_api_access_node" : { "type" : "list\\u003Cstring\\u003E" },
"search_api_language" : { "type" : "string" },
"search_api_viewed" : { "type" : "text" },
"status" : { "type" : "boolean" },
"title" : { "type" : "string" },
"type" : { "type" : "string" }
},
Facets¶
Facet configuration for fields don’t play nice with features as well
Search page panel¶
The whole search page is exported as a feature
Mise en place¶
Prepare a module to hold the search customizations. For the porpouse of this docs, let’s name it dkan_search_customizations
.
In dkan_search_customizations.module
prepare a helper function to retrieve all the new taxonomies to be added to the index:
function dkan_search_customizations_facet_list() {
return array(
'field_brand',
'field_country',
'field_customer_type',
);
}
This array is going to be used multiple times so it’s good practice to avoid repetition.
Same goes for the searchers/indexes you want to add these facets to. Prepare another helper function to retrieve those:
function dkan_search_customizations_searchers_list() {
return array(
'search_api@datasets',
'search_api@groups_di'
);
}
The solutions¶
Add fields to the index¶
Implement hook_default_search_api_index_alter
/**
* Implements hook_default_search_api_index_alter().
*/
function dkan_search_customizations_default_search_api_index_alter(&$defaults) {
$searchers = dkan_search_customizations_searchers_list();
foreach ($searchers as $searcher) {
$searcher = str_replace('search_api@', '', $searcher);
$facets = dkan_search_customizations_facet_list();
foreach ($facets as $facet) {
if (isset($defaults[$searcher])) {
$defaults[$searcher]->options['fields'][$facet] = array(
'type' => 'list<integer>',
'entity_type' => 'taxonomy_term',
);
}
}
}
}
This will mark the field to be indexed for the given indexes/searchers.
QA¶
- Revert the core search modules
ahoy drush -y fr --force dkan_sitewide_search_db dkan_dataset_groups
- Go to
/admin/config/search/search_api
- All the overriden indexes should appear to be using the Default Configuration (Not overriden)
- Inspect
admin/config/search/search_api/index/<searcher|index>/fields
for all indexes indkan_search_customizations_searchers_list
- All fields in
dkan_search_customizations_facet_list
should be checked to be indexed
Enable facets for the indexes¶
There’s no hook implementation to accomplish this. The facet configuration needs to be saved in the db. In order to do this, the most straigthforward solution is to write a function to take care of enabling the facets and have that function run on deployment through the env-switch hook. It’ll look something like this:
function dkan_search_customizations_enable_facets() {
$searchers = dkan_search_customizations_searchers_list();
foreach ($searchers as $searcher) {
$realm = 'block';
$facets_to_enable = dkan_search_customizations_facet_list();
$adapter = facetapi_adapter_load($searcher);
$facet_info = facetapi_get_facet_info($searcher);
foreach (array_keys($facet_info) as $item) {
$facet = facetapi_facet_load($item, $searcher) ;
if (in_array($item, $facets_to_enable)) {
$facet_settings = $adapter->getFacet($facet)->getSettings($realm);
$facet_settings->enabled = 1;
ctools_export_crud_save('facetapi', $facet_settings);
}
}
}
}
Running this function will mark the facets to be enabled. We also need to make sure this runs on deployments. To do that, implement hook_environment_switch
for your module
function dkan_search_customizations_environment_switch($target_env, $current_env) {
dkan_search_customizations_enable_facets()
}
QA¶
- Run
ahoy drush -y env-switch --force local
- Visit
admin/config/search/search_api/index/<searcher|index>/facets
for all the overriden indexes - Facets should be enabled
Add facets block to search pages¶
This can be accomplished with a hook_panels_pre_render
implementation:
function dkan_search_customizations_panels_pre_render(&$display, $renderer) {
$searcher = FALSE;
// Detect with searcher it is based on the panel display.
if (is_numeric(strpos($display->cache_key, 'page-datasets'))) {
$searcher = 'search_api@datasets';
}
if (is_numeric(strpos($display->cache_key, 'panel_context:node_view::node_view_panel_context_3'))) {
$searcher = 'search_api@groups_di';
}
// If searcher is ment to be overriden retrieve blocks.
if ($searcher) {
$map = facetapi_get_delta_map();
$realms = array();
$panes = array();
$faceted_fields = dkan_search_customizations_facet_list();
// Cross reference fields with facet map
foreach($map as $machine_name => $facet) {
foreach($faceted_fields as $field) {
// Special case for resource taxonomies
$f = str_replace('field_resources:', '', $field);
// If Facet matchs with field and searcher, save realm.
if (is_numeric(strpos($facet, $f)) && is_numeric(strpos($facet, $searcher))) {
$realms[$field] = $machine_name;
}
}
}
// Prepare panes for realms.
foreach($realms as $field => $machine_name) {
// Create a new pane based on the facet realm.
$pane = panels_new_pane('block', $machine_name, TRUE);
$pane->uuid = ctools_uuid_generate();
$pane->pid = 'new-' . $pane->uuid;
$pane->panel = 'sidebar';
$pane->subtype = 'facetapi-' . $machine_name;
unset($pane->did);
$pane->shown = TRUE;
// Customize titles.
switch($field){
case 'field_brand':
$title = 'Brand';
break;
case 'field_country':
$title = 'Country';
break;
case 'field_customer_type':
$title = 'Customer Type';
break;
}
$pane->configuration = array(
'override_title' => TRUE,
'override_title_heading' => 'h2',
'override_title_text' => $title
);
$pane->css = array(
'css_id' => '',
'css_class' => 'pane-facetapi pane-block',
);
$pane->style = array(
'settings' => array(
'pane_title' => '%title',
'pane_collapsed' => TRUE,
'pane_empty_check' => FALSE,
),
'style' => 'collapsible',
);
$panes[$field] = $pane;
}
foreach ($faceted_fields as $field) {
if (isset($panes[$field])) {
$pane = $panes[$field];
// Adds facet block reference to the sidebar.
$display->panels['sidebar'][] = $pane->pid;
// Add pane to the pane content.
$display->content[$pane->pid] = $pane;
}
}
}
}
QA¶
- Rebuild registry and clear cache
- Blocks should render in the search page
Future enhacements¶
- Field and searchers options could be enhance to use a structure like this:
function dkan_search_customizations_facet_list() {
return array(
'field_brand' => array(
'searchers' => array('search_api@datasets', 'search_api@groups_di'),
'title' => 'Brand',
),
'field_country' => array(
'searchers' => array('search_api@datasets'),
'title' => 'Country',
),
'field_customer_type' => array(
'searchers' => array('search_api@groups_di'),
'title' => 'Customer Type',
),
);
}
- These options, with some work, could be wrapped up in a generic module that retrieves the fields and searchers from variables. Variables could be set from
settings.custom.php
.