This post will continue from part one and also handle our second challenge: Really Clean URLs.
What we wanted to do: Present search filters in an intuitive and legible way as part of the url, not tagged on in coded language at the end.
How we did it: It's since been suggested that this can be done with panels, but we weren't using panels...
At the end of part one, we found ourselves with a search query directed to 'find/onesearchterm|anothersearchterm'. This is all well and good, but apachesolr is listening for queries on 'search/apachesolr_search' and will ignore our query.
In fact, this is part of our plan. This means we can now grab the module's handler for searches and bend it to our will, without touching the module's own code.
First up, we need to define the menu path:
/**
* Implementation of hook_menu().
*/
function guia_solr_menu() {
$items = array();
$items['find/%'] = array(
'page callback' => 'guia_solr_find',
'access callback' => user_access('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
Okay. Probably we should be using %menu_tail here to pass the search term as an argument, but instead we're going to pick them out of the url.
/**
* Handle the search
* Log search terms, execute search and theme results.
* This is a condensed version of apachesolr_search_view().
*/
function guia_solr_find() {
$results = '';
// Get the search terms.
$keys = substr($_GET['q'], 5);
// Get any filters.
if (strpos($keys, '/')) {
$filters = explode('/', $keys);
$keys = trim(array_pop($filters));
$filters = implode('/', $filters);
}
$keys = trim($keys);
if ($keys || $filters) {
// log the search keys:
$log = $keys;
if ($filters) {
$filters = $filters . '/';
$log = $filters . $keys;
}
watchdog(
'search',
'%keys (@type).', array(
'%keys' => $log,
'@type' => t('search')
),
watchdog_notice,
l(t('results'), 'find/' . $filters . '/' . $keys)
);
$results = search_data($keys, 'guia_solr');
}
if ($results) {
$results = theme('box', t('search results'), $results);
} else {
$results = theme('box', t('your search yielded no results'),
guia_solr_noresults());
}
return $results;
}
Here we are preparing the road for filters. These can be set up in the apachesolr configuration pages, can be content types, author, language, date or taxonomy terms, and introduce a lot of flexibility.
In normal use, they are tagged on the end of the query as 'search/apachesolr_search/SEARCHTERM?filters=FILTER'. We want to use them as 'find/FILTER/SEARCHTERM'.
So, we grab them from the url, log them, and pass on just the SEARCHTERM to Drupal's search_data() function, which neatly passes it on to our next custom function.
(Which is copied and pasted from the apachesolr_search.module. The only change is clearly marked - we use our own path instead of the 'search' path. The '$filters' parameter passed here will be empty.)
/*
* Adaptation of apachesolr_search_search().
*/
function guia_solr_search($op = 'search', $keys = NULL) {
switch ($op) {
case 'name':
return t('Search');
case 'reset':
apachesolr_clear_last_index('apachesolr_search');
return;
case 'status':
return apachesolr_index_status('apachesolr_search');
case 'search':
$filters = isset($_GET['filters']) ? $_GET['filters'] : '';
$solrsort = isset($_GET['solrsort']) ? $_GET['solrsort'] : '';
$page = isset($_GET['page']) ? $_GET['page'] : 0;
$path = $_GET['q'];
// $path = arg(1);
try {
$results = apachesolr_search_execute(
$keys,
$filters,
$solrsort,
// START MODIFICATION
$path,
// END MODIFICATION
$page);
return $results;
} catch (Exception $e) {
watchdog(
'Apache Solr',
nl2br(check_plain($e->getMessage())),
NULL, WATCHDOG_ERROR);
apachesolr_failure(t('Solr search'), $keys);
}
break;
} // switch
}
With this call to apachesolr_search_execute(), we rejoin the normal flow. Any further modifications we make are handled by the exposed apachesolr hooks.
There are two such hooks, and the timing of them proves significant. apachesolr_prepare_query() and apachesolr_modify_query(). If you are having trouble with a customization of the query, this is a good place to start looking.
/**
* Implementation of hook_apachesolr_prepare_query().
*/
function guia_solr_apachesolr_prepare_query(&$query) {
$keys = $query->get_keys();
if (strpos($keys, '|')) {
$keys = explode('|', $keys);
$keys = $keys[0];
}
$query->set_keys($keys);
$filters = '';
$path = substr($_GET['q'], 5);
if (strpos($path, '/')) {
$filters = explode('/', $path);
array_pop($filters);
foreach ($filters as $filter) {
$query->add_filter('type', $filter);
}
}
}
Two things going on here. First, we strip out the 'where' part of the query (see part one) and second, we grab any filters we have added to the url and add them to the query.
To make this example simple, the filters we add here are content types. Filters can also be taxonomy terms, language, the date the node was created or modified, or the node's author.
And now, with the settings configured appropriately at 'admin/settings/apachesolr/enabled-filters', you can create links directly to 'find/CONTENTTYPE/SEARCHTERM' and get a respectable result.
At this stage, we're still searching only on the 'what' term - we'll add the 'where' along with our index changes in part three, at the same time as we modify the query to reflect our changes to the index.
Also in part three, we'll look deeper into Indexing. It's not as hard as it might seem, but only after you've taken some time to figure it out.
What we want to do: Index all only a particular subset of information for each entity. (Extent of results depend on payment of subscriptions).
How we did it: Coming soon...
Add comment