diff -Naur modules/search/search.module modules/search/search.module
--- modules/search/search.module	2007-07-26 15:16:48.000000000 -0400
+++ modules/search/search.module	2007-11-04 19:49:55.000000000 -0500
@@ -272,6 +272,8 @@
     // When re-indexing, keep link references
     db_query("DELETE FROM {search_index} WHERE sid = %d AND type = '%s'". ($reindex ? " AND fromsid = 0" : ''), $sid, $type);
   }
+  // Cached search results may no longer be valid.
+  cache_clear_all(NULL, 'cache_search');
 }
 
 /**
@@ -304,6 +306,34 @@
   foreach (module_list() as $module) {
     module_invoke($module, 'update_index');
   }
+
+  // Don't allow search result caching if any node access modules such as organic
+  // groups have placed viewing restrictions on any nodes. Even if you have had
+  // organic groups enabled and have since disabled it, search caching will
+  // be prevented. If you find yourself in this position, you can restore the
+  // original un-restricted state of node_access like this:
+  // TRUNCATE node_access;
+  // INSERT INTO node_access VALUES (0,0,'all',1,0,0);
+  $r1 = db_result(db_query("SELECT count(*) FROM {node_access}"));
+  $r2 = db_result(db_query("SELECT count(*) FROM {node_access} WHERE grant_view = 1"));
+  if (($r1 == 1) && ($r2 == 1)) {
+    // Recalculate the list of popular search terms
+    $tops = array();
+    $result = db_query("SELECT location, count(*) AS count FROM {watchdog} WHERE type = 'search' GROUP BY location HAVING count(*) > 2");
+    while ($row = db_fetch_object($result)) {
+      $tops[] = strtolower($row->location);
+    }
+    // Since we just potentially indexed new or updated content, clear the cache.
+    cache_clear_all(NULL, 'cache_search');
+
+    // Set the search_cache_queries to the new top queries.
+    cache_set('search_cache_queries', 'cache_search', serialize($tops));
+  }
+  else {
+    // No guarantee that the cache complies with node access restrictions, so
+    // wipe it.
+    cache_clear_all(NULL, 'cache_search');
+  }
 }
 
 /**
@@ -890,6 +920,7 @@
  * Menu callback; presents the search form and/or search results.
  */
 function search_view() {
+  global $base_root, $user;
   $type = arg(1);
 
   // Search form submits with POST but redirects to GET. This way we can keep
@@ -909,8 +940,29 @@
       // Log the search keys:
       watchdog('search', t('%keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'search', 'name'))), WATCHDOG_NOTICE, l(t('results'), 'search/'. $type .'/'. $keys));
 
-      // Collect the search results:
-      $results = search_data($keys, $type);
+      // Look to the cache to see if this is a popular (thus cached) query
+      $request_uri = strtolower($base_root . request_uri());
+      $results = NULL;
+      // Only cache results if the user is not an admin, is authenticated, has
+      // exactly one role, and the query is in the popular search queries array.
+      if ($cachable = ($user->uid > 1 && count($user->roles) === 1 && in_array('authenticated user', $user->roles))) {
+        $queries_cache = cache_get('search_cache_queries', 'cache_search');
+      }
+      if ($cachable && $queries_cache) {
+        $search_cache_queries = unserialize($queries_cache->data);
+        if (in_array($request_uri, $search_cache_queries)) {
+          if ($search_cache = cache_get($request_uri, 'cache_search')) {
+            $results = $search_cache->data;
+          }
+        }
+      }
+      if (empty($results)) {
+        // Collect the search results:
+        $results = search_data($keys, $type);
+        if ($cachable) {
+          cache_set($request_uri, 'cache_search', $results);
+        }
+      }
 
       if ($results) {
         $results = theme('box', t('Search results'), $results);
