Drupal 7 Search with Apache Solr Part 3 - Learn to add custom fields and options to the index
- Tutorial
In previous articles, I talked about how to install and configure Apache Solr to organize searches on Drupal. In addition, we learned to add fields and search index settings. In this article I will talk about how to add your own fields and settings, if the standard ones are not enough.
For those who have not read the previous parts, I recommend reading them for a better understanding of the material.
- Drupal 7 Search with Apache Solr Part 1 - Basic Setup
- Drupal 7 Search with Apache Solr Part 2 - Learning to tune the index
- Drupal 7 Search with Apache Solr Part 3 - Learn to add custom fields and options to the index
- Search Drupal 7 with Apache Solr Part 4 - Facet Filters
- Drupal 7 Search with Apache Solr Part 5 - Facet Filter Widgets
- Search on Drupal 7 using Apache Solr Part 6 - configure apache solr + tomcat
- Drupal 7 Search with Apache Solr Part 7 - Full-Text Search in Russian
Fields
So, let's start by adding fields. Suppose the nodes that you index have a reference to a dictionary with geolocation data. Its structure looks like this:
The task is to index only the values of the first level, i.e. areas to which the node belongs. If we add the corresponding field to the index, then all terms will be indexed, regardless of level. Therefore, we need to add our own field. For this we need two functions. The first is an entity_property_info_alter hook. In this hook, you can add new fields for the entity. So, add a new field for the node.
/**
* Implements hook_entity_property_info_alter.
*/
function test_search_entity_property_info_alter(&$info) {
$info['node']['properties']['geo_first_level'] = array(
'type' => 'text',
'label' => t('Geo 1 level'),
'getter callback' => 'test_search_geo_first_level_getter_callback',
);
}
The second function is getter callback, in this case test_search_geo_first_level_getter_callback. It should return a value for our field at the time the node is indexed. This value will be stored in the index.
/**
* Getter callback.
*/
function test_search_geo_first_level_getter_callback($item) {
if ($geo = field_get_items('node', $item, 'field_geo')) {
$parents = taxonomy_get_parents($geo[0]['tid']);
if (empty($parents)) {
if ($term = taxonomy_term_load($geo[0]['tid'])) {
return $term->name;
}
}
}
return NULL;
}
An indexed entity will be passed as $ item, in our case it is a node object. We do a small check on whether this node contains a 1st level term or not and return the name of the term as an indexed value.
Now go to the index settings and select the Fields tab.
Our new field is already available for indexing. Do not forget to add it to the list of fields by which you can search in the view settings.
Sometimes you may need to index multiple values for a field. To do this, specify 'type' => 'list' in the entity_property_info_alter hook, and in the getter callback you will need to return an array of values.
for instance
return array('Москва', 'Санкт-Петербург', 'Новосибирск');
Filters
Now let's try to add a filter by which it will be possible to select nodes for indexing. The filter will perform a similar function - only index those nodes that have a reference to the first level term from the geo dictionary. To do this, declare our filter in the search_api_alter_callback_info hook.
/**
* Implements hook_search_api_alter_callback_info().
*/
function test_search_search_api_alter_callback_info() {
$callbacks['search_api_alter_geo_level'] = array(
'name' => t('Filter by level of geo'),
'description' => t('Index only nodes with first level of term from vocabulary geo'),
'class' => 'SearchApiAlterGeoLevelFilter',
// Filters should be executed first.
'weight' => -10,
);
return $callbacks;
}
I placed the file with the filter class in the includes folder of the test_search module. Do not forget to include it in the .info file of your module. For example, like this:
files[] = includes/callback_geo_level.inc
Below is the code of the filter itself
$item) {
if ($geo = field_get_items('node', $item, 'field_geo')) {
$parents = taxonomy_get_parents($geo[0]['tid']);
// If term has parents.
if (!empty($parents)) {
unset($items[$id]);
}
}
}
}
public function supportsIndex(SearchApiIndex $index) {
return $index->item_type === 'node';
}
}
In the alterItems function, we simply exclude elements that have non-first level terms. These items will not be indexed.
To enable the filter, you need to clean the cache and go to the workflow tab in the index settings.
After you enable the filter, you must reindex the content again. Now only those nodes that have a first-level dictionary term will fall into the index.