Index: xapian.module =================================================================== --- xapian.module (revision 4) +++ xapian.module (working copy) @@ -16,6 +16,95 @@ define('XAPIAN_FLUSH', TRUE); /** + * Implementation of hook_perm(). + */ +function xapian_perm() { + return array( + 'administer xapian' + ); +} + +/** + * Implementation of hook_search(). + * + * @param unknown_type $op + * @param unknown_type $keys + * @return unknown + */ +function xapian_search($op = 'search', $keys = NULL) { + switch ($op) { + case 'name': + return t('Search'); + break; + + case 'reset': + xapian_clear_index(); + break; + + case 'status': + return xapian_index_status(); + break; + + case 'admin': + return xapian_admin(); + break; + + case 'search': + return xapian_do_search($keys); + break; + } +} + +function xapian_index_status() { + $count_result = db_query("SELECT COUNT(*) FROM {xapian_index_queue}"); + $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')); + $remaining = db_result($count_result); + + return array('remaining' => $remaining, 'total' => $total); +} + +/** + * Implementation of hook_form_alter(). + * + * @param unknown_type $form + * @param unknown_type $form_state + * @param unknown_type $form_id + */ +function xapian_form_alter(&$form, &$form_state, $form_id) { + if (variable_get('xapian_takeover', FALSE)) { + if ($form_id == 'search_theme_form') { + $form['#submit'][] = 'xapian_search_submit'; + } + } +} + +/** + * Implementation of hook_submit(). + * + * @param unknown_type $form + * @param unknown_type $form_state + */ +function xapian_search_submit($form, &$form_state) { + $form_id = $form['form_id']['#value']; + $form_state['redirect'] = 'search/xapian/'. trim($form_state['values'][$form_id]); +} + +/** + * Implementation of hook_menu_alter(). + * + * @param unknown_type $items + */ +function xapian_menu_alter(&$items) { + if (variable_get('xapian_takeover', FALSE)) { + // Remove the std content/node search + unset($items['search/node/%menu_tail']); + + // Move Xapian search to the front + $items['search/xapian/%menu_tail']['weight'] = -1; + } +} + +/** * Provide default error handler for xapian * * @param int $errno @@ -79,41 +168,6 @@ } /** - * Implementation of hook_perm - */ -function xapian_perm() { - return array( - 'administer xapian' - ); -} - -/** - * Implementation of hook_menu - * - * @param bool $may_cache - * @return array - */ -function xapian_menu() { - $items['admin/settings/xapian'] = array( - 'title' => 'Xapian settings', - 'description' => 'Settings for the Xapian search library', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('xapian_admin'), - 'access arguments' => array('administer xapian'), - 'type' => MENU_NORMAL_ITEM, - ); - $items['admin/settings/xapian/wipe'] = array( - 'title' => 'Rebuild index', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('xapian_reindex_all_confirm'), - 'access arguments' => array('administer search'), - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** * implemtation of hook_admin * * @return array @@ -124,14 +178,32 @@ $database_type = variable_get('xapian_database_type', 0); + // Create an all encompasing Xapian fieldset + $form['xapian'] = array( + '#type' => 'fieldset', + '#collapsed' => TRUE, + '#collapsible' => TRUE, + '#weight' => 0, + '#title' => t('Xapian search settings') + ); + + $form['xapian']['xapian_takeover'] = array( + '#type' => 'checkbox', + '#title' => t('Xapian only, hide drupal search'), + '#default_value' => variable_get('xapian_takeover', FALSE), + '#description' => t('Enable this option to replace the standard drupal search entirely.'), + ); + // Create a database fieldset - $form['database'] = array( + $form['xapian']['database'] = array( '#type' => 'fieldset', + '#collapsed' => ($database_type ? FALSE : TRUE), + '#collapsible' => TRUE, '#title' => t('Xapian database') ); // Database type - $form['database']['xapian_database_type'] = array( + $form['xapian']['database']['xapian_database_type'] = array( '#type' => 'radios', '#title' => t('Type'), '#default_value' => $database_type, @@ -139,13 +211,13 @@ ); // local database settings - $form['database']['local_database'] = array( + $form['xapian']['database']['local_database'] = array( '#type' => 'fieldset', '#title' => t('Local database options'), '#collapsible' => TRUE, '#collapsed' => ($database_type != 0) ); - $form['database']['local_database']['xapian_database_path'] = array( + $form['xapian']['database']['local_database']['xapian_database_path'] = array( '#type' => 'textfield', '#title' => t('Path to Xapian database'), '#default_value' => variable_get('xapian_database_path', file_directory_path() .'/xapian_database'), @@ -154,20 +226,20 @@ ); // Remote database settings - $form['database']['remote_database'] = array( + $form['xapian']['database']['remote_database'] = array( '#type' => 'fieldset', '#title' => t('Remote database options'), '#collapsible' => TRUE, '#collapsed' => ($database_type != 1), ); - $form['database']['remote_database']['xapian_database_hostname'] = array( + $form['xapian']['database']['remote_database']['xapian_database_hostname'] = array( '#type' => 'textfield', '#title' => t('Database server'), '#default_value' => variable_get('xapian_database_hostname', ''), '#required' => ($database_type == 1), '#description' => t('IP address or host name of remote server running xapian-tcpsrv.'), ); - $form['database']['remote_database']['xapian_database_port'] = array( + $form['xapian']['database']['remote_database']['xapian_database_port'] = array( '#type' => 'textfield', '#title' => t('Database port'), '#default_value' => variable_get('xapian_database_port', '6431'), @@ -177,49 +249,27 @@ ); // indexing settings - $form['performance'] = array( + $form['xapian']['performance'] = array( '#type' => 'fieldset', - '#title' => t('Performance') + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Xapian Performance') ); - $count_result = db_query("SELECT COUNT(*) FROM {xapian_index_queue}"); - $nodes = db_result($count_result); - $form['performance']['count'] = array( - '#value' => t('

There are %count items waiting to be indexed.

', array('%count' => $nodes)), - ); - $form['performance']['index'] = array( - '#type' => 'submit', - '#value' => t('Re-index site'), - ); - $form['performance']['xapian_index_immediately'] = array( + $form['xapian']['performance']['xapian_index_immediately'] = array( '#type' => 'checkbox', '#title' => t('Index immediately'), '#default_value' => variable_get('xapian_index_immediately', TRUE), '#description' => t('Enable this option to index content immediately as it is created and updated. Disable this option to delay indexing until cron runs. Your should disable this option on larger websites.'), ); - $items = drupal_map_assoc(array(10, 20, 50, 100, 500, 1000, 5000, 10000)); - $form['performance']['xapian_indexing_throttle'] = array( - '#type' => 'select', - '#title' => t('Items to index per cron run'), - '#description' => t('The maximum number of items that will be indexed in one cron run. Set this number lower if your cron is timing out or if PHP is running out of memory.'), - '#options' => $items, - '#default_value' => variable_get('xapian_indexing_throttle', 100), - ); // Display settings - $form['display'] = array( + $form['xapian']['display'] = array( '#type' => 'fieldset', - '#title' => t('Display') + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Xapian Display') ); - $form['display']['xapian_search_results_per_page'] = array( - '#type' => 'textfield', - '#title' => t('Number of search results per page'), - '#size' => 3, - '#maxlength' => 3, - '#default_value' => variable_get('xapian_search_results_per_page', 10), - '#description' => t('This setting determines the number of entries per page displayed on the search results.'), - '#validate' => array('_xapian_validate_positive_integer' => array('xapian_search_results_per_page')), - ); - $form['display']['xapian_node_count_type'] = array( + $form['xapian']['display']['xapian_node_count_type'] = array( '#type' => 'radios', '#title' => t('Result count'), '#description' => t('This setting determines the value that xapian returns for the result count returned from queries (used for number of pages in pagers, etc.)'), @@ -232,11 +282,13 @@ ); // Logging options - $form['diagnostic'] = array( + $form['xapian']['diagnostic'] = array( '#type' => 'fieldset', - '#title' => t('Logging') + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Xapain Logging') ); - $form['diagnostic']['xapian_log_queries'] = array( + $form['xapian']['diagnostic']['xapian_log_queries'] = array( '#type' => 'checkbox', '#title' => t('Log searches'), '#description' => t('Log search queries and time taken for search to the watchdog log.'), @@ -244,18 +296,20 @@ ); // Node Type Settings - $form['node_types'] = array( + $form['xapian']['node_types'] = array( '#type' => 'fieldset', - '#title' => t('Node types') + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Xapian Node types') ); - $form['node_types']['markup'] = array( + $form['xapian']['node_types']['markup'] = array( '#value' => t("

Select the node types to EXCLUDE from Xapian's indexing

"), ); $types = node_get_types('types'); foreach ($types as $key => $type) { $options[$key] = $type->name; } - $form['node_types']['xapian_excluded_nodes'] = array( + $form['xapian']['node_types']['xapian_excluded_nodes'] = array( '#type' => 'select', '#title' => t('Exclude indexing on'), '#options' => $options, @@ -264,13 +318,15 @@ ); // Algorithms - $form['algorithms'] = array( + $form['xapian']['algorithms'] = array( '#type' => 'fieldset', - '#title' => t('Algorithms') + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Xapian Algorithms') ); $languages = _xapian_languages(); // See http://xapian.org/docs/stemming.html - $form['algorithms']['xapian_stem_language'] = array( + $form['xapian']['algorithms']['xapian_stem_language'] = array( '#type' => 'select', '#title' => t('Stemming language'), '#options' => $languages, @@ -280,23 +336,17 @@ $form['#submit'][] = 'xapian_admin_submit'; - return system_settings_form($form); + return $form; } /** - * Validation function for admin form + * Implementation of hook_submit(). * - * @param string $form_id - * @param array $form_values + * @param unknown_type $form + * @param unknown_type $form_state */ -function xapian_admin_validate($form, &$form_state) { - if (t('Re-index site') == $form_state['values']['op']) { - drupal_goto('admin/settings/xapian/wipe'); - } -} - function xapian_admin_submit($form, &$form_state) { - if ($form_state['node_types']['xapian_excluded_nodes']['#values'] != variable_get('xapian_excluded_nodes', array())) { + if ($form_state['values']['xapian_excluded_nodes']['#values'] != variable_get('xapian_excluded_nodes', array())) { drupal_set_message('You should reindex your entire site after changing the types'); } } @@ -434,10 +484,12 @@ $query = $query_parser->parse_query($query_string); // Build subqueries from $extra array. - foreach ($extra as $subq) { - if (!empty($subq)) { - $subquery = new XapianQuery(XapianQuery::OP_OR, $subq); - $query = new XapianQuery(XapianQuery::OP_AND, array($subquery, $query)); + if (is_array($extra)) { + foreach ($extra as $subq) { + if (!empty($subq)) { + $subquery = new XapianQuery(XapianQuery::OP_OR, $subq); + $query = new XapianQuery(XapianQuery::OP_AND, array($subquery, $query)); + } } } @@ -451,9 +503,16 @@ $count++; $document = $i->get_document(); if (is_object($document)) { - $results[$count]->type = 'node'; - $results[$count]->sid = (int)($document->get_data()); - $results[$count]->score = (int)($i->get_percent()); + $node = node_load((int)($document->get_data())); + $results[$count]['link'] = 'node/'. $node->nid; + $results[$count]['type'] = 'node'; + $results[$count]['title'] = $node->title; + $results[$count]['user'] = theme('username', $node); + $results[$count]['date'] = $node->created; + $results[$count]['node'] = $node; + $results[$count]['extra'] = $extra; + $results[$count]['score'] = (int)($i->get_percent()); + $results[$count]['snippet'] = search_excerpt($query_string, $node->body); } $i->next(); } @@ -481,7 +540,7 @@ break; } - return array($count, $results); + return $results; } catch (Exception $e) { watchdog('xapian', $e->getMessage()); @@ -490,39 +549,31 @@ } /** - * Check to see if we can implement our own hook_search + * Our xapian implementation of hook_search + * + * @param unknown_type $keywords + * @return array */ -if (!function_exists('do_search')) { - /** - * Our xapian implementation of hook_search - * - * @param unknown_type $keywords - * @param unknown_type $type - * @param unknown_type $join1 - * @param unknown_type $where1 - * @param unknown_type $arguments1 - * @param unknown_type $select2 - * @param unknown_type $join2 - * @param unknown_type $arguments2 - * @param unknown_type $sort_parameters - * @return unknown - */ - function do_search($keywords, $type, $join1 = '', $where1 = '1', $arguments1 = array(), $select2 = 'i.relevance AS score', $join2 = '', $arguments2 = array(), $sort_parameters = 'ORDER BY score DESC') { - global $pager_total; - global $pager_page_array; +function xapian_do_search($keys) { + global $pager_total; + global $pager_page_array; - if (!empty($_REQUEST['page'])) { - $page = $_REQUEST['page']; - } - else { - $page = 0; - } + if (!empty($_REQUEST['page'])) { + $page = $_REQUEST['page']; + } + else { + $page = 0; + } - $start = variable_get('xapian_search_results_per_page', 10) * $page; - $length = variable_get('xapian_search_results_per_page', 10); + $keywords = search_get_keys(); - $terms = array(); - $types = array(); + $start = variable_get('xapian_search_results_per_page', 10) * $page; + $length = variable_get('xapian_search_results_per_page', 10); + + $terms = array(); + $types = array(); + $extra = array(); + if (is_array($arguments1)) { foreach ($arguments1 as $condition) { // If its a number, its a taxonomy term if (is_numeric($condition)) { @@ -537,14 +588,14 @@ } } $extra = array($terms, $types); + } - list($count, $results) = xapian_query($keywords, $start, $length, $extra); + $results = xapian_query($keywords, $start, $length, $extra); - $pager_total[0] = (int)($count / variable_get('xapian_search_results_per_page', 10)) + 1; - $pager_page_array[0] = $page; + $pager_total[0] = (int)($count / variable_get('xapian_search_results_per_page', 10)) + 1; + $pager_page_array[0] = $page; - return $results; - } + return $results; } /** @@ -751,9 +802,9 @@ // Re-index content that wasn't flushed to disk last time, minus the node // which failed last time. $sql = 'SELECT nid FROM {xapian_index_queue} WHERE status > 0 ORDER BY COALESCE(priority, 0) DESC, added ASC'; - $config = variable_get('xapian_indexing_throttle', 100); + $limit = (int)variable_get('search_cron_limit', 100); $error = $not_flushed - 1; - $limit = $config > $error ? $error : $config; + $limit = $limit > $error ? $error : $limit; } else if ($not_flushed == 1) { // Re-index a node that previously failed to index, removing it from the @@ -765,7 +816,7 @@ } else { $sql = 'SELECT nid FROM {xapian_index_queue} ORDER BY COALESCE(priority, 0) DESC, added ASC'; - $limit = variable_get('xapian_indexing_throttle', 100); + $limit = (int)variable_get('search_cron_limit', 100); } $result = db_query_range($sql, 0, $limit); @@ -835,23 +886,12 @@ /** * Confirm recreation of search index. */ -function xapian_reindex_all_confirm() { - return confirm_form(array(), t('Are you sure you want to re-index the site?'), 'admin/settings/xapian', t('

This will queue up all site content to be re-indexed when cron runs. The search index is not cleared, instead it is systematically updated. Searching will continue to work.

If you have immediate indexing enabled, new content will continue to be indexed. If you do not have immediate indexing enabled, new content will not be indexed until all existing content has been re-indexed. This action cannot be undone.

'), t('Re-index site'), t('Cancel')); +function xapian_clear_index() { + db_query('TRUNCATE {xapian_index_queue}'); + db_query('INSERT INTO {xapian_index_queue} (nid, added) SELECT nid, UNIX_TIMESTAMP() FROM {node}'); } /** - * Reindex all site content. - */ -function xapian_reindex_all_confirm_submit($form, &$form_state) { - if ($form_state['values']['confirm']) { - db_query('TRUNCATE {xapian_index_queue}'); - db_query('INSERT INTO {xapian_index_queue} (nid, added) SELECT nid, UNIX_TIMESTAMP() FROM {node}'); - drupal_set_message(t('The search index will be rebuilt.')); - $form_state['redirect'] = 'admin/settings/xapian'; - } -} - -/** * Default node index function * Indexes title, body, terms in the node (as text), and the taxonomy terms * as well.