Drupal

Drupal Solr Search with Domain Access filtering

Metal Toad has had the privilege to work over the past two years with DC Comics. What makes this partnership even more exciting, is that the main dccomics.com site also includes sites for Vertigo Comics and Mad Magazine. Most recently Metal Toad was given the task of building the new search feature for all three sites. However, while its an awesome privilege to work with such a well known brand as DC, this does not come without a complex set of issues for the three sites when working with Apache Solr search and Drupal.


Filed under:

Metal Toad has had the privilege to work over the past two years with DC Comics. What makes this partnership even more exciting, is that the main dccomics.com site also includes sites for Vertigo Comics and Mad Magazine. Most recently Metal Toad was given the task of building the new search feature for all three sites. However, while its an awesome privilege to work with such a well known brand as DC, this does not come without a complex set of issues for the three sites when working with Apache Solr search and Drupal.

Problem: Say you have a Drupal installation that presents three separate domains to the public (using the great Domain Access module), and you'd like to set up a search system for these sites. On this project we used Search API (the base search module), with the Search API Solr Search (tying it to the solr server) & Search API Views (letting you use Views to create your Solr queries) modules giving us Solr integration. These modules together give us Solr access and the ability to create one Solr index for all three domains.

I quickly found Search API Solr doesn't currently have a way to store each entity's Domain Access information - example: the Batman character entity has Domain Access assigned to it, which is a list of the domains that entity is available on. You cannot currently query by that information when searching, meaning searches were returning results from all three domains, not the site the user was currently browsing.

One Solution: Add a field to each of the indexed entities with its Domain Access ID's (keep in mind that an entity can belong to multiple domains) on insertion into the Solr index, and alter the Solr query with a Filter Query (fq) on the current domain.

This thread on drupal.org has some good direction on this, but I needed a slightly more generic solution. So without further discussion, here's the code:

/**
 * Alter Solr documents before they are sent to Solr for indexing.
 *
 * @param array $documents
 *   An array of SearchApiSolrDocument objects ready to be indexed, generated
 *   from $items array.
 * @param SearchApiIndex $index
 *   The search index for which items are being indexed.
 * @param array $items
 *   An array of items being indexed.
*/
 
function MY_MODULE_search_api_solr_documents_alter(array &$documents, SearchApiIndex $index, array $items) {
  // Adds a domain field to all documents.
  foreach ($documents as $document) {
    // Assumes the nid is an indexed field
    $nid = $document->getField('is_nid');
    // Grab the node from the db for it's Domain Access info
    $node = node_load($nid['value']);
    // Insert the domains into the document
    foreach ($node->domains as $key => $value) {
      $document->addField('im_domain_access', $value, null);
    }
  }
}
 
/**
 * Lets modules alter a Solr search request before sending it.
 *
 * Apache_Solr_Service::search() is called afterwards with these parameters.
 * Please see this method for details on what should be altered where and what
 * is set afterwards.
 *
 * @param array $call_args
 *   An associative array containing all three arguments to the
 *   SearchApiSolrConnectionInterface::search() call ("query", "params" and
 *   "method") as references.
 * @param SearchApiQueryInterface $query
 *   The SearchApiQueryInterface object representing the executed search query.
*/
function MY_MODULE_search_api_solr_query_alter(array &$call_args, SearchApiQueryInterface $query) {
  $domain = domain_get_domain();
  $id = $domain['domain_id'];
  // Add our domain field to the Solr filter querys
  $call_args['params']['fq'][] = 'im_domain_access:' . $id;
}

I hope this helps someone else who has the same problem to solve. Feel free to make any suggestions/ask any questions in the comments.

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.