Drupal 8 Search API Tips
Here are some quick tips for configuring Search API for Drupal 8.
Easy Indexing
If you’re like me you rely heavily on Display Modes (aka View Modes) for your entity types. The most convenient way to get Search API to index your data is to use the Search Index view mode on any entity types you will index. If you’re using Display Suite you may want to choose a layout that doesn’t include authoring information, published date, etc. Just fill this view mode, in each entity type, with the fields you want to index. If you plan to use this to drive the output, you should also hide labels and do whatever else will ensure clean output; more on this later.
I suggest indexing the Title (in the case of content types) separately. More on that in the Boost notes below.
You can then use this Search Index view mode with Search API by adding a Rendered HTML output field to your Search API index:
Warning: If you add additional content types to the index, you will have to come back here and choose the view mode for them.
Boost / Weight / Importance
If you want to, for example, give the content Title field more importance than the rendered Search Index data, you should explicitly add the Title field to the search index. You can then give this field a higher Boost value than the Rendered HTML output field. The other added bonus here is that you may not always have Title available to you in the Search Index view mode, or you may not want it as part of the results.
Rendering Results
Using Views makes it easy to create a search page. If you chose to use the Search Index view mode as discussed above, and you would like to also use this for the output, things are even simpler.
Here’s how the View looks. The Title is set to link to the content. The Fulltext search (and) is configured to search both the Title field and the Rendered HTML output field.
If you need the search box to appear in the header of the site you can “detach” it with the “Exposed form in block” settings in the View.
If you want your output to look consistent across all data types you might consider stripping the HTML tags from the Rendered HTML output field; it’s just a setting for that field in the View.
Use Search API (tag-based) as the caching type in the View! See cache notes below.
Indexing Paragraphs
Paragraph content will be automatically indexed if you used the Search Index view mode as discussed above, and that view mode contains rendered paragraph fields. Make sure you choose the right “Rendered as” view mode. Here’s an example of a Body field that is made up of paragraph entities. In this case my Body renders paragraph entities, all of which have a Full view mode setup.
Caching
Be careful with caching on the View. The results may be different after a cache clear, so during development, please don’t hesitate to clear the caches.
If you have permissions per content type or entity you may not be able to use caching at all. Please carefully test search results for your different user roles.
Rendered HTML Output
In my debugging I discovered that \Drupal\search_api\Plugin\search_api\processor\RenderedItem::addFieldValues() gets called both during indexing and during display (if you’re using a rendered html output field in your views output).
Permissions
If you are using hook_node_grants and hook_node_access_records to handle access to nodes you may have to do some sneaky things for paragraphs of “protected” nodes to get indexed properly. I have a taxonomy-based grant system in place so that certain basic page nodes are locked to be viewable to only certain grant IDs. Search API will hide these nodes (in the search results) from users who cannot access the nodes, but the child paragraphs of those nodes won’t be indexed or shown to users who do have access to the nodes. Non-paragraph fields are indexed and displayed correctly, however.
To solve this I had to get creative. I set my Rendered HTML output field to use authenticated and anonymous roles at the same time (something that never happens in the real world). I could then use hook_paragraph_access
to allow Search API to access specific paragraph types. Hopefully the code below tells more of the story:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/** * Implements hook_ENTITY_TYPE_access(). */ function mymodule_paragraph_access(EntityInterface $entity, $operation, AccountInterface $account) { if ($operation == 'view') { if (mymodule_search_api_allowed_paragraph($entity, $account)) { return AccessResult::allowed(); } } return AccessResult::neutral(); } /** * Returns TRUE if account is Search API and paragraph should be allowed. * * Node grants will prevent the parent node from appearing if the user * doesn't have access. We always want to index the data, and this seems * to be able the only way to trick Search API into doing it. Regular * fields on "protected" nodes are indexed and shown, but paragraph * entities are not for whatever reason. * * If uid = 0 and it has anonymous and authenticated roles this should * be the Search API account proxy (uid 0 normally isn't authenticated role). * The two roles are set in the Rendered HTML Output field in the Search API * configuration. Search API will always use a uid of 0 but with these roles. * See $this->currentUser->setAccount() in * \Drupal\search_api\Plugin\search_api\processor\RenderedItem::addFieldValues. * * @param EntityInterface $entity * Paragraph entity. * @param AccountInterface $account * User account. * * @return bool * TRUE if account is Search API and paragraph should be allowed. */ function mymodule_search_api_allowed_paragraph(EntityInterface $entity, AccountInterface $account) { if ($account->id() === 0 && in_array('authenticated', $account->getRoles())) { $search_api_always_allowed_types = ['p_wysiwyg']; if (in_array($entity->getType(), $search_api_always_allowed_types)) { return TRUE; } } return FALSE; } |
Not Seeing Correct Number of Indexed Nodes?
If you’re not seeing the correct number of nodes you may have to trigger a re-track of items. I came across this by change my search to use “none” as the server, which also disabled the search. Then, I re-enabled the search and a “Track” button appeared. I clicked that and then the total count reflected all of the nodes I expected to see.
2 Comments
Joe
You saved me with this one, thank you very much for writing this up. I was struggling with paragraphs and getting views to render things out with a link to the source node, thought there might be a solution similar to this but was unsure how to set it up.
Joe
Another thing, turn on case insensitivity -> https://drupal.stackexchange.com/questions/228062/how-to-make-full-text-search-case-insensitive
Also I’ve tried getting the highlighting to work but unsuccessful so far.