Q:

How to remove a specific item from the search index?

I have a content type in a D6 site that allows the creator of a node to mark the node as "withdrawn" -- he can still see it, but attempts by others to see it are caught and redirected somewhere else, like their user page.  When this node gets withdrawn, I'd like to remove it from the local search index (standard Drupal search) as well.
My somewhat brutish way of doing this is:
    db_query("delete from {search_dataset} where sid = %d", $node->nid);
    db_query("delete from {search_index} where sid = %d", $node->nid);

which seems to work -- the node stops showing up in search results.  But I'm worried about {search_total} -- there are entries there that correspond to the words in the node's content.  I can't just remove all the node's terms from {search_total}, of course; is this worth worrying about?  Or am I seriously screwing up the search index by doing this in the first place?  Thanks!

A:

Looking at the code executed from node_delete_multiple() (which is then the function used by node_delete()), I see that the function called is search_reindex().
    // Remove this node from the search index if needed.
    // This code is implemented in node module rather than in search module,
    // because node module is implementing search module's API, not the other
    // way around.
    if (module_exists('search')) {
      search_reindex($nid, 'node');
    }

The code executed from that function to delete rows from the search tables is the following one.
db_delete('search_dataset')
  ->condition('sid', $sid)
  ->condition('type', $module)
  ->execute();

db_delete('search_index')
  ->condition('sid', $sid)
  ->condition('type', $module)
  ->execute();

// Don't remove links if re-indexing.
if (!$reindex) {
  db_delete('search_node_links')
    ->condition('sid', $sid)
    ->condition('type', $module)
    ->execute();
}

I suggest using search_reindex(), but to reply to your question, it doesn't seem that updating the "search_total" table is necessary, at this point; Drupal doesn't do it when a node is deleted. That table is updated from search_update_totals(), which is called from search_cron(). See the comment given for the following code, executed from search_update_totals().
  // Find words that were deleted from search_index, but are still in
  // search_total. We use a LEFT JOIN between the two tables and keep only the
  // rows which fail to join.
  $result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL", array(), array('target' => 'slave'));
  $or = db_or();
  foreach ($result as $word) {
    $or->condition('word', $word->realword);
  }
  if (count($or) > 0) {
    db_delete('search_total')
      ->condition($or)
      ->execute();
  }

In Drupal 6, the equivalent of search_reindex() is search_wipe(), which is called from node_delete(). The function contains the following code to delete the tables' content.
db_query("DELETE FROM {search_dataset} WHERE sid = %d AND type = '%s'", $sid, $type);
db_query("DELETE FROM {search_index} WHERE sid = %d AND type = '%s'", $sid, $type);
// Don't remove links if re-indexing.
if (!$reindex) {
  db_query("DELETE FROM {search_node_links} WHERE sid = %d AND type = '%s'", $sid, $type);
}

Also in this case, I suggest using search_wipe().

