l(t('running cron'), 'admin/reports/status/run-cron', array('query' => array('destination' => 'admin/settings/apachesolr/index'))), '%percentage' => ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) .'%', '@items' => format_plural($remaining, t('is 1 item'), t('are @count items') ))); } } /** * Implementation of hook_menu(). */ function apachesolr_search_menu() { $items['admin/settings/apachesolr/query-fields'] = array( 'title' => 'Query field settings', 'page callback' => 'apachesolr_search_settings_page', 'access arguments' => array('administer site configuration'), 'weight' => 1, 'type' => MENU_LOCAL_TASK, 'file' => 'apachesolr_search.admin.inc', ); return $items; } /** * Implementation of hook_update_index(). */ function apachesolr_search_update_index() { $cron_limit = variable_get('apachesolr_cron_limit', 50); $result = apachesolr_get_nodes_to_index('apachesolr_search', $cron_limit); apachesolr_index_nodes($result, 'apachesolr_search'); } /** * Implementation of hook_search(). */ function apachesolr_search_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': try { // This is the object that knows about the query coming from the user. $query = apachesolr_current_query($keys); if (is_null($query)) { throw new Exception(t('Could not construct a Solr query in function apachesolr_search_search()')); } $results = array(); $params = array( 'fl' => 'id,nid,title,comment_count,type,created,changed,score,url,uid,name', 'rows' => variable_get('apachesolr_rows', 10), 'facet' => 'true', 'facet.mincount' => 1, 'facet.sort' => 'true' ); /** * Highlighting settings * These settings are set in solrconfig.xml. * See the defaults there. * If you wish to override them, you can via settings.php */ $params['hl'] = variable_get('apachesolr_hl_active', NULL); $params['hl.fragsize']= variable_get('apachesolr_hl_textsnippetlength', NULL); $params['hl.simple.pre'] = variable_get('apachesolr_hl_pretag', NULL); $params['hl.simple.post'] = variable_get('apachesolr_hl_posttag', NULL); $params['hl.snippets'] = variable_get('apachesolr_hl_numsnippets', NULL); $params['hl.fl'] = variable_get('apachesolr_hl_fieldtohightlight', NULL); // We default to getting snippets from the body. $hl_fl = is_null($params['hl.fl']) ? 'body' : $params['hl.fl']; if (variable_get('apachesolr_search_spellcheck', FALSE)) { //Add new parameter to the search request $params['spellcheck.q'] = $query->get_query_basic(); $params['spellcheck'] = 'true'; } $facet_query_limits = variable_get('apachesolr_facet_query_limits', array()); // Request all enabled facets. foreach (apachesolr_get_enabled_facets() as $module => $module_facets) { foreach($module_facets as $delta => $facet_field) { $params['facet.field'][] = $facet_field; // Facet limits if (isset($facet_query_limits[$module][$delta])) { $params['f.' . $facet_field . '.facet.limit'] = $facet_query_limits[$module][$delta]; } } } if (!empty($params['facet.field'])) { // Add a default limit for fields where no limit was set. $params['facet.limit'] = variable_get('apachesolr_facet_query_limit_default', 20); } if (isset($_GET['solrsort'])) { $sort = check_plain($_GET['solrsort']); } // Validate sort parameter if (isset($sort) && preg_match('/^([a-z0-9_]+ (asc|desc)(,)?)+$/i', $sort)) { $params['sort'] = $sort; } $page = isset($_GET['page']) ? $_GET['page'] : 0; $params['start'] = $page * $params['rows']; // This is the object that does the communication with the solr server. $solr = apachesolr_get_solr(); // Note - we have query fields set in solrconfig.xml, which will operate when // none are set. $qf = variable_get('apachesolr_search_query_fields', array()); $fields = $solr->getFields(); if ($qf && $fields) { foreach ($fields as $field_name => $field) { if (!empty($qf[$field_name])) { if ($field_name == 'body') { // Body is the only normed field. $qf[$field_name] *= 40.0; } $params['qf'][] = $field_name . '^'. $qf[$field_name]; } } } // Note: we use 2 since 1 fails on Ubuntu Hardy. $data = $solr->getLuke(2); if (isset($data->index->numDocs)) { $total = $data->index->numDocs; } else { $total = db_result(db_query("SELECT COUNT(nid) FROM {node}")); } $date_settings = variable_get('apachesolr_search_date_boost', '4:200.0'); list($date_steepness, $date_boost) = explode(':', $date_settings); // Default date-biasing function, as suggested (but steeper) at // http://wiki.apache.org/solr/DisMaxRequestHandler // rord() returns 1 for the newset doc, and the number in the index for // the oldest doc. The function is thus: $total/(rord()*$steepness + $total). if ($date_boost) { $params['bf'][] = "recip(rord(created),$date_steepness,$total,$total)^$date_boost"; } $comment_settings = variable_get('apachesolr_search_comment_boost', '4:200.0'); list($comment_steepness, $comment_boost) = explode(':', $comment_settings); // Default date-biasing function, as suggested (but steeper) at // http://wiki.apache.org/solr/DisMaxRequestHandler // rord() returns 1 for the newset doc, and the number in the index for // the oldest doc. The function is thus: $total/(rord()*$steepness + $total). if ($comment_boost) { $params['bf'][] = "recip(rord(comment_count),$comment_steepness,$total,$total)^$comment_boost"; } // This hook allows modules to modify the query and params objects. apachesolr_modify_query($query, $params, 'apachesolr_search'); if (!$query) { return array(); } $response = $solr->search($query->get_query_basic(), $params['start'], $params['rows'], $params); // The response is cached so that it is accessible to the blocks and anything // else that needs it beyond the initial search. $total = $response->response->numFound; apachesolr_static_response_cache($response); apachesolr_has_searched(TRUE); pager_query("SELECT %d", $params['rows'], 0, NULL, $total); if ($total > 0) { foreach ($response->response->docs as $doc) { $extra = array(); $snippet = isset($response->highlighting->{$doc->id}->$hl_fl) ? theme('apachesolr_search_snippets', $doc, $response->highlighting->{$doc->id}->$hl_fl) : ''; if (!isset($doc->body)) { $doc->body = $snippet; } $doc->created = strtotime($doc->created); $doc->changed = strtotime($doc->changed); $extra += node_invoke_nodeapi($doc, 'search result'); $extra['score'] = $doc->score; $results[] = array( 'link' => $doc->url, 'type' => node_get_types('name', $doc), 'title' => $doc->title, 'user' => theme('username', $doc), 'date' => $doc->created, 'node' => $doc, 'extra' => $extra, 'score' => $doc->score, 'snippet' => $snippet, ); } // Hook to allow modifications of the retrieved results foreach (module_implements('apachesolr_process_results') as $module) { $function = $module .'_apachesolr_process_results'; call_user_func_array($function, array(&$results)); } } // Set breadcrumb drupal_set_breadcrumb($query->get_breadcrumb()); return $results; } // try catch (Exception $e) { watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR); apachesolr_failure(t('Solr search'), is_null($query) ? $keys : $query->get_query_basic()); } break; } // switch } /** * Implementation of hook_apachesolr_facets(). * * Returns an array keyed by block delta. */ function apachesolr_search_apachesolr_facets() { $facets = array(); $facets['type'] = array( 'info' => t('Apache Solr Search: Filter by content type'), 'facet_field' => 'type', ); $facets['uid'] = array( 'info' => t('Apache Solr Search: Filter by author'), 'facet_field' => 'uid', ); $facets['language'] = array( 'info' => t('Apache Solr Search: Filter by language'), 'facet_field' => 'language', ); // Get taxonomy vocabulary facets. if (module_exists('taxonomy')) { $vocabs = taxonomy_get_vocabularies(); foreach ($vocabs as $vid => $vocab) { // In this case the delta and facet field are the same. $delta = 'imfield_vid_' . $vid; $facets[$delta] = array( 'info' => t('Apache Solr Search: Filter by @name', array('@name' => $vocab->name)), 'facet_field' => $delta, ); } } // Get CCK field facets. $fields = apachesolr_cck_fields(); if ($fields) { foreach ($fields as $name => $field) { // $delta can only be 32 chars, and the CCK field name may be this // long also, so we cannot add anything to it. $facets[$field['field_name']] = array( 'info' => t('Apache Solr Search: Filter by @field', array('@field' => $field['label'])), 'facet_field' => apachesolr_index_key($field), ); } } return $facets; } /** * Implementation of hook_block(). */ function apachesolr_search_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $enabled_facets = apachesolr_get_enabled_facets('apachesolr_search'); $facets = apachesolr_search_apachesolr_facets(); // Add the blocks $blocks = array(); foreach ($enabled_facets as $delta => $facet_field) { $blocks[$delta] = $facets[$delta] + array('cache' => BLOCK_CACHE_PER_PAGE,); } $blocks['currentsearch'] = array( 'info' => t('Apache Solr Search: Current search'), 'cache' => BLOCK_CACHE_PER_PAGE, ); return $blocks; case 'view': if (apachesolr_has_searched()) { // Get the query and response. Without these no blocks make sense. $response = apachesolr_static_response_cache(); if (empty($response)) { return; } $query = apachesolr_current_query(); $facets = apachesolr_get_enabled_facets('apachesolr_search'); if (empty($facets[$delta]) && ($delta != 'currentsearch')) { return; } // Get information needed by the taxonomy blocks about limits. $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array()); $limit_default = variable_get('apachesolr_facet_query_initial_limit_default', 10); // Handle taxonomy vocabulary facets if ((strpos($delta, 'imfield_vid_') === 0) && module_exists('taxonomy')) { if (is_object($response->facet_counts->facet_fields->$delta)) { $contains_active = FALSE; $terms = array(); foreach ($response->facet_counts->facet_fields->$delta as $tid => $count) { $unclick_link = ''; unset($active); $term = taxonomy_get_term($tid); $new_query = clone $query; if ($active = $query->has_field('tid', $tid)) { $contains_active = TRUE; $new_query->remove_field('tid', $term->tid); $path = 'search/' . arg(1) . '/' . $new_query->get_query_basic(); $querystring = $new_query->get_url_querystring(); $unclick_link = theme('apachesolr_unclick_link', $path, $querystring); } else { $new_query->add_field('tid', $term->tid); $path = 'search/' . arg(1) . '/' . $new_query->get_query_basic(); $querystring = $new_query->get_url_querystring(); } $countsort = $count == 0 ? '' : 1 / $count; // if numdocs == 1 and !active, don't add. if ($response->numFound == 1 && !$active) { // skip } else { $terms[$term->vid][$active ? $countsort . $term->name : 1 + $countsort . $term->name] = theme('apachesolr_facet_item', $term->name, $count, $path, $querystring, $active, $unclick_link, $response->numFound); } } } $vid = substr($delta, 12); $vocab = taxonomy_vocabulary_load($vid); if (is_numeric($vid) && is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) { ksort($terms[$vid]); $limit = isset($initial_limits['apachesolr_search'][$delta]) ? $initial_limits['apachesolr_search'][$delta] : $limit_default; return array( 'subject' => t('Filter by @name', array('@name' => $vocab->name)), 'content' => theme('apachesolr_facet_list', $terms[$vid], $limit), ); } return; } switch ($delta) { case 'currentsearch': $fields = $query->get_fields(); $search_keys = $query->get_query_basic(); $path = 'search/' . arg(1) . '/' . $search_keys; $options = array(); if (!$fields) { $options['attributes']['class'] = 'active'; } $links[] = apachesolr_l($search_keys, $path, $options); foreach($fields as $field) { if ($field['#name']) { $new_query = clone $query; $new_query->remove_field($field['#name'], $field['#value']); $path = 'search/'. arg(1) .'/'. $new_query->get_query_basic(); $querystring = $new_query->get_url_querystring(); $unclick_link = theme('apachesolr_unclick_link', $path, $querystring); if (! $fielddisplay = theme("apachesolr_breadcrumb_". $field['#name'], $field['#value'])) { $fielddisplay = $field['#value']; } $links[] = theme('apachesolr_facet_item', $fielddisplay, NULL, $path, $querystring, $active, $unclick_link, $response->numFound); } } $content = theme('apachesolr_currentsearch', $response->response->numFound, $links); return array('subject' => t('Current search'), 'content' => $content); case 'language': return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by language'), 'locale_language_name'); case 'uid': return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by author'), 'apachesolr_search_get_username'); case 'type': return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by type'), 'apachesolr_search_get_type'); default: if ($fields = apachesolr_cck_fields()) { foreach ($fields as $name => $field) { if ($field['field_name'] == $delta) { $index_key = apachesolr_index_key($field); return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $index_key, t('Filter by @field', array('@field' => $field['label']))); } } } } break; } break; case 'configure': if ($delta != 'currentsearch') { return apachesolr_facetcount_form('apachesolr_search', $delta); } break; case 'save': if ($delta != 'currentsearch') { apachesolr_facetcount_save($edit); } break; } } /** * Callback function for the 'Filter by name' facet block. */ function apachesolr_search_get_username($facet) { if ($facet == 0) { return variable_get('anonymous', t('Anonymous')); } else { return db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $facet)); } } /** * Callback function for the 'Filter by type' facet block. */ function apachesolr_search_get_type($facet) { return node_get_types('name', $facet); } /** * Implementation of hook_form_[form_id]_alter(). * * This adds spelling suggestions to the search form. */ function apachesolr_search_form_search_form_alter(&$form, $form_state) { if (($form['module']['#value'] == 'apachesolr_search') && variable_get('apachesolr_search_spellcheck', FALSE) && apachesolr_has_searched() && ($response = apachesolr_static_response_cache())) { //Get spellchecker suggestions into an array. $suggestions = get_object_vars($response->spellcheck->suggestions); if ($suggestions) { //Get the original query and replace words. $query = apachesolr_current_query(); foreach($suggestions as $word => $value) { $replacements[$word] = $value->suggestion[0]; } $new_keywords = strtr($query->get_query_basic(), $replacements); $form['basic']['suggestion'] = array( '#prefix' => '