Search API: Search nodes based on child node content
This post is about how to manipulate nodes so they can be found through the Drupal 7 search API based on content of their child nodes, also known as entity references.
Example:
You have the following content types
The key here is hook_entity_property_info_alter(). We can add new properties to our parent node which we can fill freely and will be picked up by the search index. The new properties will hold information of the child nodes and by that, the parent node can be searched based on child node content.
The following example gives an example of both a taxonomy list and a text string moved from the child nodes to the parent node. Add this code to your .module file of your custom module.
mymodule.module
More information about the implementation of this hook and other types to define can be found here.
Note that I'm using the entity metadata wrapper class to access the node content. If you're not familiar with this, you can still access the node fields using the common way ($node['field_fruit_ref'][LANGUAGE_NONE][0]...).
Also note that, when using dpm() in this hook for debugging, it will appear when saving a content type. In case you wonder.
Now you've done all this and the Drupal cache is cleared, you can find these fields in the field configuration of your search index. Select the fields to be indexed and during the next indexing, the new properties of the food_collection node will be populated and indexed. You can also configure facets on them, just like on any other field.
Go forth and make a fancy search.
Example:
You have the following content types
- Food collection (parent node)
- Title
- Entity reference to Fruit node
- Fruit ( child node)
- Title (by searching on this field, the parent food collection is supposed to appear as result)
- Taxonomy reference 'color' (by filtering on this field through facets, the parent food collection is supposed to appear as result)
The key here is hook_entity_property_info_alter(). We can add new properties to our parent node which we can fill freely and will be picked up by the search index. The new properties will hold information of the child nodes and by that, the parent node can be searched based on child node content.
The following example gives an example of both a taxonomy list and a text string moved from the child nodes to the parent node. Add this code to your .module file of your custom module.
mymodule.module
/** * Implements hook_entity_property_info_alter(). */ function mymodule_entity_property_info_alter(&$info) { // Extra property for food_collection containing the color terms of the child nodes. $info['node']['bundles']['food_collection']['properties']['child_colors'] = array( 'label' => t('Colors of child nodes'), 'type' => 'list<taxonomy_term>', 'description' => t('Colors of the child nodes.'), 'getter callback' => 'mymodule_get_child_node_data', // Callback to a custom function. 'bundle' => 'food_color', // Machine name of taxonomy list. ); // Extra property for food_collection containing the titles of the child nodes. $info['node']['bundles']['food_collection']['properties']['child_titles'] = array( 'label' => t('Titles of child nodes'), 'type' => 'text', 'description' => t('Titles of child nodes.'), 'getter callback' => 'mymodule_get_child_node_data', // Callback to a custom function. ); } /** * Getter callback for the hook_entity_property_info_alter() implementation. */ function mymodule_get_child_node_data($node, $options, $name, $entity_type) { $parent_node = entity_metadata_wrapper('node', $node); switch ($name) { case 'child_colors': $list = array(); // Loop through child fruit nodes. foreach ($parent_node->field_fruit_ref->getIterator() as $fruit_node) { // Loop through color terms of fruit node. foreach ($fruit_node->field_color->getIterator() as $color) { // Add term ID to list. $list[] = $color->getIdentifier(); } } // Remove duplicate term IDs because some child nodes can have the same terms. $list = array_unique($list); // Return array of term IDs. return $list; case 'child_titles': $text = ''; // Loop through child fruit nodes. foreach ($parent_node->field_fruit_ref->getIterator() as $fruit_node) { // Append fruit title to our text variable. $text .= $fruit_node->label() . ' '; } // Return string of node titles. return $text; } return; }I think the functions are self explanatory. The first function (the hook) defines the new properties of the parent content type 'food_collection'. The second function is the getter callback (pointed to in the first function) which will retrieve the actual data from the child nodes.
More information about the implementation of this hook and other types to define can be found here.
Note that I'm using the entity metadata wrapper class to access the node content. If you're not familiar with this, you can still access the node fields using the common way ($node['field_fruit_ref'][LANGUAGE_NONE][0]...).
Also note that, when using dpm() in this hook for debugging, it will appear when saving a content type. In case you wonder.
Now you've done all this and the Drupal cache is cleared, you can find these fields in the field configuration of your search index. Select the fields to be indexed and during the next indexing, the new properties of the food_collection node will be populated and indexed. You can also configure facets on them, just like on any other field.
Go forth and make a fancy search.
Add new comment