Index: contrib/search_api_facets/search_api_facets.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.install,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 search_api_facets.install
--- contrib/search_api_facets/search_api_facets.install	27 Sep 2010 18:09:19 -0000	1.1.2.3
+++ contrib/search_api_facets/search_api_facets.install	26 Oct 2010 21:09:17 -0000
@@ -16,9 +16,9 @@
         'default' => '0',
       ),
       'index_id' => array(
-        'description' => 'The {search_api_index}.id this facet belongs to.',
-        'type' => 'int',
-        'unsigned' => TRUE,
+        'description' => 'The {search_api_index}.machine_name this facet belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
         'not null' => TRUE,
       ),
       'field' => array(
@@ -63,3 +63,31 @@
 function search_api_facets_uninstall() {
   variable_del('search_api_facets_search_ids');
 }
+
+/**
+ * Implements hook_update_N().
+ *
+ * Make {search_api_facet}.index_id the index' machine name.
+ */
+function search_api_facets_update_7101() {
+  // Update of search_api_item:
+  db_drop_index('search_api_facet', 'field');
+  $spec = array(
+    'description' => 'The {search_api_index}.machine_name this facet belongs to.',
+    'type' => 'varchar',
+    'length' => 50,
+    'not null' => TRUE,
+  );
+  db_change_field('search_api_facet', 'index_id', 'index_id', $spec);
+  db_add_index('search_api_facet', 'field', array('index_id', array('field', 50)));
+
+  foreach (db_query('SELECT id, machine_name FROM {search_api_index}') as $index) {
+    // We explicitly forbid numeric machine names, therefore we don't have to worry about conflicts here.
+    db_update('search_api_facet')
+      ->fields(array(
+        'index_id' => $index->machine_name,
+      ))
+      ->condition('index_id', $index->id)
+      ->execute();
+  }
+}
Index: contrib/search_api_facets/search_api_facets.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.admin.inc,v
retrieving revision 1.1.2.7
diff -u -r1.1.2.7 search_api_facets.admin.inc
--- contrib/search_api_facets/search_api_facets.admin.inc	29 Sep 2010 10:15:08 -0000	1.1.2.7
+++ contrib/search_api_facets/search_api_facets.admin.inc	26 Oct 2010 21:09:17 -0000
@@ -11,16 +11,13 @@
 function search_api_facets_index_select(array $form, array &$form_state, SearchApiIndex $index) {
   module_load_include('admin.inc', 'search_api');
   drupal_set_title(search_api_admin_item_title($index));
+  $form_state['index'] = $index;
 
   if (!$index->server() || !$index->server()->supportsFeature('search_api_facets')) {
     // We can't offer any facets, but maybe the user can delete old facet data.
     drupal_set_message(t("The server this index currently lies on doesn't support facets. " .
         'To use facets, you will have to move this index to a server supporting this feature.'), 'error');
-    if (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE index_id = :id', array(':id' => $index->id))->fetchField()) {
-      $form['index'] = array(
-        '#type' => 'value',
-        '#value' => $index->id,
-      );
+    if (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE index_id = :id', array(':id' => $index->machine_name))->fetchField()) {
       $form['description'] = array(
         '#type' => 'item',
         '#title' => t('Delete facet settings'),
@@ -66,7 +63,7 @@
     if ($disabled = empty($index->enabled)) {
       drupal_set_message('Since this index is at the moment disabled, no facet blocks can be activated.', 'warning');
     }
-    foreach (search_api_facet_load(array('index_id' => $index->id)) as $facet) {
+    foreach (search_api_facet_load(array('index_id' => $index->machine_name)) as $facet) {
       $facets[$facet->field][] = $facet;
     }
     $types = search_api_field_types();
@@ -78,7 +75,7 @@
       $type = isset($types[$type]) ? $types[$type] : $type;
       if (empty($facets[$key])) {
         $facets[$key][] = (object) array(
-          'index_id' => $index->id,
+          'index_id' => $index->machine_name,
           'field' => $key,
           'name' => t('!index: Filter on !field', array('!field' => $field['name'], '!index' => $index->name)),
           'enabled' => 0,
@@ -126,10 +123,6 @@
     }
   }
 
-  $form['index'] = array(
-    '#type' => 'value',
-    '#value' => $index,
-  );
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save settings'),
@@ -167,7 +160,7 @@
  * Submit callback for search_api_facets_index_select().
  */
 function search_api_facets_index_select_submit(array $form, array &$form_state) {
-  $index = $form_state['values']['index'];
+  $index = $form_state['index'];
   $count = 0;
   $clone_count = 0;
   $delete_count = 0;
@@ -227,15 +220,15 @@
  * "Delete facet settings".
  */
 function search_api_facets_index_select_submit_delete(array $form, array &$form_state) {
-  $index_id = $form_state['values']['index'];
-  $ret = search_api_facet_delete(array('index_id' => $index_id));
+  $index = $form_state['index'];
+  $ret = search_api_facet_delete(array('index_id' => $index->machine_name));
   if ($ret) {
     drupal_set_message(t('All facet data stored for this index was deleted.'));
   }
   else {
     drupal_set_message(t('There was no data to delete.'), 'warning');
   }
-  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index_id;
+  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name;
 }
 
 /**
Index: contrib/search_api_facets/search_api_facets.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.module,v
retrieving revision 1.1.2.21
diff -u -r1.1.2.21 search_api_facets.module
--- contrib/search_api_facets/search_api_facets.module	29 Sep 2010 10:15:08 -0000	1.1.2.21
+++ contrib/search_api_facets/search_api_facets.module	26 Oct 2010 21:09:18 -0000
@@ -106,7 +106,7 @@
   $form['index_description'] = array(
     '#type' => 'item',
     '#title' => t('Search index'),
-    '#description' => l($index->name, 'admin/config/search/search_api/index/' . $index->id),
+    '#description' => l($index->name, 'admin/config/search/search_api/index/' . $index->machine_name),
   );
   $form['field_description'] = array(
     '#type' => 'item',
@@ -142,7 +142,7 @@
   );
 
   $search_ids = variable_get('search_api_facets_search_ids', array());
-  if (empty($search_ids[$index->id]) || count($search_ids[$index->id]) <= 1) {
+  if (empty($search_ids[$index->machine_name]) || count($search_ids[$index->machine_name]) <= 1) {
     $form['default_true'] = array(
       '#type' => 'value',
       '#value' => $options['default_true'],
@@ -165,8 +165,8 @@
     $form['ids_list'] = array(
       '#type' => 'select',
       '#title' => t('Search IDs'),
-      '#options' => $search_ids[$index->id],
-      '#size' => min(4, count($search_ids[$index->id])),
+      '#options' => $search_ids[$index->machine_name],
+      '#size' => min(4, count($search_ids[$index->machine_name])),
       '#multiple' => TRUE,
       '#default_value' => $options['ids_list'],
     );
@@ -225,7 +225,6 @@
   }
   $search_id = $search_ids[$delta];
   list($query, $results) = search_api_current_search($search_id);
-  $index = $query->getIndex();
 
   $facet = search_api_facet_load($delta);
   $options = $facet->options;
@@ -328,7 +327,7 @@
     return;
   }
   // Add facet fields to query.
-  $facets = search_api_facet_load(array('index_id' => $query->getIndex()->id, 'b.status' => 1));
+  $facets = search_api_facet_load(array('index_id' => $query->getIndex()->machine_name, 'b.status' => 1));
   $fields = array();
   // Save the active facets for later retrieval when generating blocks.
   $active = &drupal_static(__FUNCTION__, array());
@@ -336,9 +335,9 @@
   foreach ($facets as $facet) {
     if (array_search($search_id, $facet->options['ids_list']) === FALSE) {
       $search_ids = variable_get('search_api_facets_search_ids', array());
-      if (empty($search_ids[$query->getIndex()->id][$search_id])) {
+      if (empty($search_ids[$query->getIndex()->machine_name][$search_id])) {
         // Remember this search ID.
-        $search_ids[$query->getIndex()->id][$search_id] = $search_id;
+        $search_ids[$query->getIndex()->machine_name][$search_id] = $search_id;
         variable_set('search_api_facets_search_ids', $search_ids);
       }
       if (!$facet->options['default_true']) {
@@ -401,7 +400,7 @@
  * Implements hook_search_api_index_update().
  */
 function search_api_facets_search_api_index_update(SearchApiIndex $index, $op = 'edit') {
-  $id = $index->id;
+  $id = $index->machine_name;
   if ($op == 'enable') {
     if (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE index_id = :id', array(':id' => $id))->fetchField()) {
       drupal_set_message(t('There are saved facets for this index. ' .
@@ -439,7 +438,7 @@
  */
 function search_api_facets_search_api_index_delete(SearchApiIndex $index) {
   db_delete('search_api_facet')
-    ->condition('index_id', $index->id)
+    ->condition('index_id', $index->machine_name)
     ->execute();
 }
 
@@ -531,8 +530,9 @@
     'type'         => '',
   );
   if (!empty($facet->delta) && empty($facet->is_new)) {
+    $enabled_changed = $facet->enabled != search_api_facet_load($facet->delta, TRUE)->enabled;
     drupal_write_record('search_api_facet', $facet, 'delta');
-    if ($facet->enabled != search_api_facet_load($facet->delta, TRUE)->enabled) {
+    if ($enabled_changed) {
       block_flush_caches();
     }
     return;
@@ -541,7 +541,7 @@
     // Construct a delta using index id and field.
     // We have to ensure that it's both unique and at most 32 characters long,
     // leading to a bit of complexity.
-    $delta = 'index' . $facet->index_id . '_' . preg_replace('/\W+/', '_', $facet->field);
+    $delta = substr($facet->index_id, 0, 20) . '_' . preg_replace('/\W+/', '_', $facet->field);
     $base = $delta = substr($delta, 0, 32);
     $i = 0;
     while (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE delta = :delta', array(':delta' => $delta))->fetchField()) {
Index: contrib/search_api_db/search_api_db.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_db/Attic/search_api_db.install,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 search_api_db.install
--- contrib/search_api_db/search_api_db.install	15 Oct 2010 14:33:22 -0000	1.1.2.4
+++ contrib/search_api_db/search_api_db.install	26 Oct 2010 21:09:17 -0000
@@ -5,12 +5,53 @@
  * Implements hook_uninstall().
  */
 function search_api_db_uninstall() {
-  db_delete('search_api_server')
-    ->condition('class', 'search_api_db_service')
-    ->execute();
+  if (module_exists('search_api')) {
+    db_delete('search_api_server')
+      ->condition('class', 'search_api_db_service')
+      ->execute();
+  }
   foreach (db_find_tables(Database::getConnection()->prefixTables('{search_api_db}') . '%') as $table) {
     if (preg_match('/search_api_db_.*$/', $table, $matches)) {
       db_drop_table($matches[0]);
     }
   }
 }
+
+/**
+ * Implements hook_update_N().
+ *
+ * Re-arranges the $server->options['indexes'] array to be keyed by machine_name
+ * instead of by id.
+ */
+function search_api_db_update_7101() {
+  $query = db_select('search_api_server', 's');
+  $query->addField('s', 'machine_name');
+  $query->condition('class', 'search_api_db_service');
+  $index_names = db_select('search_api_index', 'i')
+    ->fields('i', array('id', 'machine_name'))
+    ->condition('server', clone $query, 'IN')
+    ->execute()
+    ->fetchAllKeyed();
+  $query->addField('s', 'options');
+  $servers = $query->execute();
+  foreach ($servers->fetchAllKeyed() as $name => $options) {
+    $options = unserialize($options);
+    if (empty($options['indexes'])) {
+      continue;
+    }
+    $indexes = array();
+    foreach ($options['indexes'] as $id => $info) {
+      if (isset($index_names[$id])) {
+        $indexes[$index_names[$id]] = $info;
+      }
+    }
+    $options['indexes'] = $indexes;
+    $options = serialize($options);
+    db_update('search_api_server')
+      ->fields(array(
+        'options' => $options,
+      ))
+      ->condition('machine_name', $name)
+      ->execute();
+  }
+}
Index: contrib/search_api_db/service.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_db/Attic/service.inc,v
retrieving revision 1.1.2.29
diff -u -r1.1.2.29 service.inc
--- contrib/search_api_db/service.inc	19 Oct 2010 12:50:08 -0000	1.1.2.29
+++ contrib/search_api_db/service.inc	26 Oct 2010 21:09:17 -0000
@@ -81,56 +81,54 @@
   public function addIndex(SearchApiIndex $index) {
     $this->options += array('indexes' => array());
     $indexes = &$this->options['indexes'];
-    if (isset($indexes[$index->id])) {
+    if (isset($indexes[$index->machine_name])) {
       // Easiest and safest method to ensure all of the index' data is properly re-added.
       $this->removeIndex($index);
     }
     if (empty($index->options['fields']) || !is_array($index->options['fields'])) {
       // No fields, no work.
-      $indexes[$index->id] = array();
-      search_api_server_edit($this->server->id, array('options' => $this->options));
+      $indexes[$index->machine_name] = array();
+      $this->server->save();
       return $this;
     }
 
-    $prefix = 'search_api_db_' . $this->server->id . '_' . $index->id . '_';
-    $indexes[$index->id] = array();
+    $prefix = 'search_api_db_' . $index->machine_name. '_';
+    $indexes[$index->machine_name] = array();
     foreach ($index->options['fields'] as $name => $field) {
       if (empty($field['indexed'])) {
         continue;
       }
       $table = $this->findFreeTable($prefix, $name);
       $this->createFieldTable($field, $table);
-      $indexes[$index->id][$name]['table'] = $table;
-      $indexes[$index->id][$name]['type']  = $field['type'];
-      $indexes[$index->id][$name]['boost'] = $field['boost'];
+      $indexes[$index->machine_name][$name]['table'] = $table;
+      $indexes[$index->machine_name][$name]['type']  = $field['type'];
+      $indexes[$index->machine_name][$name]['boost'] = $field['boost'];
     }
 
-    search_api_server_edit($this->server->id, array('options' => $this->options));
+    $this->server->save();
   }
 
   /**
-   * Helper method for finding table names for fields.
+   * Helper method for finding free table names for fields.
+   *
+   * MySQL 5.0 imposes a 64 characters length limit for table names, PostgreSQL
+   * 8.3 only allows 63 characters. Therefore, always return a name at most 63
+   * characters long.
    */
   protected function findFreeTable($prefix, $name) {
-    $subst = '';
-    do {
-      $subst .= '_';
-      $ret = $prefix . preg_replace('/[^a-z0-9_]/i', $subst, $name);
-      if (strlen($subst) > 10) {
-        throw new SearchApiException(t('Possible infinite loop detected because of existing table !table. The initial cause might be stale data due to an unsuccessful update. Please remove the corresponding index from the server and afterwards add it again.', array('!table' => $prefix . preg_replace('/[^a-z0-9_]/i', '_', $name))));
-      }
-    } while (db_table_exists($ret));
-    return $ret;
+    $base = $table = drupal_substr($prefix . drupal_strtolower(preg_replace('/[^a-z0-9]/i', '_', $name)), 0, 63);
+    $i = 0;
+    while (db_table_exists($table)) {
+      $suffix = '_' . ++$i;
+      $table = drupal_substr($base, 0, 63 - drupal_strlen($suffix)) . $suffix;
+    }
+    return $table;
   }
 
   /**
    * Helper method for creating the table for a field.
    */
   protected function createFieldTable($field, $name) {
-    $type = $field['type'];
-    if ($list = search_api_is_list_type($type)) {
-      $type = search_api_extract_inner_type($type);
-    }
     $table = array(
       'name' => $name,
       'module' => 'search_api_db',
@@ -143,6 +141,7 @@
         ),
       ),
     );
+    $type = search_api_extract_inner_type($field['type']);
     if ($type == 'text') {
       $table['fields']['word'] = array(
         'description' => 'The text of the indexed token.',
@@ -161,7 +160,8 @@
     else {
       $table['fields']['value'] = $this->sqlType($type);
       $table['fields']['value'] += array('description' => "The field's value for this item.");
-      if ($list) {
+      if ($type != $field['type']) {
+        // This is a list type.
         $table['fields']['value']['not null'] = TRUE;
         $table['primary key'] = array('item_id', 'value');
       }
@@ -199,14 +199,13 @@
   }
 
   public function fieldsUpdated(SearchApiIndex $index) {
-    $fields = &$this->options['indexes'][$index->id];
+    $fields = &$this->options['indexes'][$index->machine_name];
     $new_fields = $index->options['fields'];
     foreach ($new_fields as $name => $field) {
       if (empty($field['indexed'])) {
         unset($new_fields[$name]);
       }
     }
-    $new_fields = $new_fields;
 
     $reindex = FALSE;
     $cleared = FALSE;
@@ -219,7 +218,7 @@
       }
       $old_type = $field['type'];
       $new_type = $new_fields[$name]['type'];
-      if ($field['type'] != $new_fields[$name]['type']) {
+      if ($old_type != $new_type) {
         if ($old_type == 'text' || $new_type == 'text') {
           // A change in fulltext status necessitates completely clearing the index.
           $reindex = TRUE;
@@ -240,7 +239,7 @@
           $reindex = TRUE;
         }
       }
-      if (!$reindex && $new_type == 'text' && $field['boost'] != $new_fields[$name]['boost']) {
+      elseif (!$reindex && $new_type == 'text' && $field['boost'] != $new_fields[$name]['boost']) {
         $multiplier = $new_fields[$name]['boost'] / $field['boost'];
         db_update($field['table'], $this->query_options)
           ->expression('score', 'score * :mult', array(':mult' => $multiplier))
@@ -248,7 +247,7 @@
       }
       unset($new_fields[$name]);
     }
-    $prefix = 'search_api_db_' . $this->server->id . '_' . $index->id . '_';
+    $prefix = 'search_api_db_' . $index->machine_name. '_';
     // These are new fields that were previously not indexed.
     foreach ($new_fields as $name => $field) {
       $reindex = TRUE;
@@ -262,12 +261,12 @@
       $this->resetDb();
     }
 
-    search_api_server_edit($this->server->id, array('options' => $this->options));
+    $this->server->save();
     return $reindex;
   }
 
   public function removeIndex($index) {
-    $id = is_object($index) ? $index->id : $index;
+    $id = is_object($index) ? $index->machine_name : $index;
     if (!isset($this->options['indexes'][$id])) {
       return;
     }
@@ -279,12 +278,12 @@
       $this->resetDb();
     }
     unset($this->options['indexes'][$id]);
-    search_api_server_edit($this->server->id, array('options' => $this->options));
+    $this->server->save();
   }
 
   public function indexItems(SearchApiIndex $index, array $items) {
-    if (empty($this->options['indexes'][$index->id])) {
-      throw new SearchApiException(t('No field settings for index with id !id.', array('!id' => $index->id)));
+    if (empty($this->options['indexes'][$index->machine_name])) {
+      throw new SearchApiException(t('No field settings for index with id !id.', array('!id' => $index->machine_name)));
     }
     $indexed = array();
     $set = $this->setDb();
@@ -306,7 +305,7 @@
   }
 
   protected function indexItem(SearchApiIndex $index, $id, array $item) {
-    $fields = $this->options['indexes'][$index->id];
+    $fields = $this->options['indexes'][$index->machine_name];
     $txn = db_transaction('search_api_indexing', $this->query_options);
     try {
       foreach ($item as $name => $field) {
@@ -470,11 +469,11 @@
       return;
     }
 
-    if (empty($this->options['indexes'][$index->id])) {
+    if (empty($this->options['indexes'][$index->machine_name])) {
       return;
     }
     $set = $this->setDb();
-    foreach ($this->options['indexes'][$index->id] as $field) {
+    foreach ($this->options['indexes'][$index->machine_name] as $field) {
       if (is_array($ids)) {
         db_delete($field['table'], $this->query_options)
           ->condition('item_id', $ids, 'IN')
@@ -493,10 +492,10 @@
     $time_method_called = microtime(TRUE);
     $set = $this->setDb();
     $index = $query->getIndex();
-    if (empty($this->options['indexes'][$index->id])) {
-      throw new SearchApiException(t('Unknown index !id.', array('!id' => $index->id)));
+    if (empty($this->options['indexes'][$index->machine_name])) {
+      throw new SearchApiException(t('Unknown index !id.', array('!id' => $index->machine_name)));
     }
-    $fields = $this->options['indexes'][$index->id];
+    $fields = $this->options['indexes'][$index->machine_name];
 
     $keys = &$query->getKeys();
     $keys_set = (boolean) $keys;
Index: contrib/search_api_views/search_api_views.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_views/Attic/search_api_views.install,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 search_api_views.install
--- contrib/search_api_views/search_api_views.install	18 Sep 2010 18:53:02 -0000	1.1.2.1
+++ contrib/search_api_views/search_api_views.install	26 Oct 2010 21:09:19 -0000
@@ -5,5 +5,5 @@
  * Implements hook_uninstall().
  */
 function search_api_views_uninstall() {
-  variable_del('search_api_views.module');
+  variable_del('search_api_views_max_fields_depth');
 }
Index: contrib/search_api_views/search_api_views.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_views/Attic/search_api_views.module,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 search_api_views.module
--- contrib/search_api_views/search_api_views.module	18 Sep 2010 18:53:02 -0000	1.1.2.1
+++ contrib/search_api_views/search_api_views.module	26 Oct 2010 21:09:19 -0000
@@ -39,7 +39,7 @@
  */
 function _search_api_views_index_unavailable(SearchApiIndex $index) {
   $names = array();
-  $table = 'search_api_index_' . $index->id;
+  $table = 'search_api_index_' . $index->machine_name;
   foreach (views_get_all_views() as $name => $view) {
     if (empty($view->disabled) && $view->base_table == $table) {
       $names[] = $name;
Index: contrib/search_api_views/search_api_views.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_views/Attic/search_api_views.views.inc,v
retrieving revision 1.1.2.14
diff -u -r1.1.2.14 search_api_views.views.inc
--- contrib/search_api_views/search_api_views.views.inc	19 Oct 2010 16:34:35 -0000	1.1.2.14
+++ contrib/search_api_views/search_api_views.views.inc	26 Oct 2010 21:09:19 -0000
@@ -6,19 +6,19 @@
  */
 function search_api_views_views_data() {
   $data = array();
-  foreach (search_api_list_indexes() as $index) {
+  foreach (entity_load('search_api_index', FALSE) as $index) {
     // We need the complete indexed data.
     $array = array();
     $index->dataAlter($array);
 
     // Base data
-    $key = 'search_api_index_' . $index->id;
+    $key = 'search_api_index_' . $index->machine_name;
     $table = & $data[$key];
     $entity_info = entity_get_info($index->entity_type);
     $table['table']['group'] = $entity_info['label'];
     $table['table']['base'] = array(
       'field' => 'search_api_id',
-      'index' => $index->id,
+      'index' => $index->machine_name,
       'title' => $index->name,
       'help' => t('Use the %name search index for filtering and retrieving data.', array('%name' => $index->name)),
       'query class' => 'search_api_views_query',
@@ -69,7 +69,7 @@
               $table[$key]['group'] = $prefix_name;
             }
             $table[$key]['title'] = $info['label'];
-            $table[$key]['help'] = empty($info['description']) ? '' : $info['description'];
+            $table[$key]['help'] = empty($info['description']) ? t('(No information available)') : $info['description'];
             $table[$key]['type'] = $type;
             $table[$key]['field']['handler'] = _search_api_views_field_handler($type, $inner_type);
             if ($inner_type == 'options') {
Index: contrib/search_api_page/search_api_page.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_page/Attic/search_api_page.install,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 search_api_page.install
--- contrib/search_api_page/search_api_page.install	20 Aug 2010 20:55:10 -0000	1.1.2.1
+++ contrib/search_api_page/search_api_page.install	26 Oct 2010 21:09:18 -0000
@@ -15,9 +15,9 @@
         'not null' => TRUE,
       ),
       'index_id' => array(
-        'description' => 'The {search_api_index}.id this page will search on.',
-        'type' => 'int',
-        'unsigned' => TRUE,
+        'description' => 'The {search_api_index}.machine_name this page will search on.',
+        'type' => 'varchar',
+        'length' => 50,
         'not null' => TRUE,
       ),
       'path' => array(
@@ -63,3 +63,31 @@
 
   return $schema;
 }
+
+/**
+ * Implements hook_update_N().
+ *
+ * Make {search_api_page}.index_id the index' machine name.
+ */
+function search_api_page_update_7101() {
+  // Update of search_api_item:
+  db_drop_index('search_api_page', 'index_id');
+  $spec = array(
+    'description' => 'The {search_api_index}.machine_name this page will search on.',
+    'type' => 'varchar',
+    'length' => 50,
+    'not null' => TRUE,
+  );
+  db_change_field('search_api_page', 'index_id', 'index_id', $spec);
+  db_add_index('search_api_page', 'index_id', array('index_id'));
+
+  foreach (db_query('SELECT id, machine_name FROM {search_api_index}') as $index) {
+    // We explicitly forbid numeric machine names, therefore we don't have to worry about conflicts here.
+    db_update('search_api_page')
+      ->fields(array(
+        'index_id' => $index->machine_name,
+      ))
+      ->condition('index_id', $index->id)
+      ->execute();
+  }
+}
Index: contrib/search_api_page/search_api_page.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_page/Attic/search_api_page.module,v
retrieving revision 1.1.2.10
diff -u -r1.1.2.10 search_api_page.module
--- contrib/search_api_page/search_api_page.module	15 Oct 2010 15:55:25 -0000	1.1.2.10
+++ contrib/search_api_page/search_api_page.module	26 Oct 2010 21:09:18 -0000
@@ -37,7 +37,7 @@
     foreach (entity_load('search_api_page', FALSE, array('enabled' => 1), TRUE) as $page) {
       $items[$page->path] = array(
         'title' => $page->name,
-        'description' => $page->description ? $page->description : $page->name,
+        'description' => $page->description ? $page->description : '',
         'page callback' => 'search_api_page_view',
         'page arguments' => array((string) $page->id),
         'access arguments' => array('access search_api_page'),
@@ -50,21 +50,20 @@
   return $items;
 }
 
-/**
- * Implements hook_block_info().
- */
-function search_api_page_block_info() {
-  // @todo Implement.
-  return array();
-}
-
-/**
- * Implements hook_block_view().
- */
-function search_api_page_block_view() {
-  // @todo Implement.
-  return array();
-}
+// @todo Implement a search block like provided by the search module.
+///**
+// * Implements hook_block_info().
+// */
+//function search_api_page_block_info() {
+//  return array();
+//}
+//
+///**
+// * Implements hook_block_view().
+// */
+//function search_api_page_block_view() {
+//  return array();
+//}
 
 /**
  * Implements hook_theme().
@@ -132,7 +131,7 @@
     ),
     'index_id' => array(
       'label' => t('Index ID'),
-      'type' => 'integer',
+      'type' => 'token',
       'description' => t('The ID of the index this search page uses.'),
     ),
     'index' => array(
@@ -168,7 +167,7 @@
  */
 function search_api_page_search_api_index_update(SearchApiIndex $index, $op = 'edit') {
   if ($op == 'disable') {
-    foreach (entity_load('search_api_page', FALSE, array('index_id' => $index->id, 'enabled' => 1)) as $page) {
+    foreach (entity_load('search_api_page', FALSE, array('index_id' => $index->machine_name, 'enabled' => 1)) as $page) {
       search_api_page_edit($page->id, array('enabled' => 0));
     }
   }
@@ -178,7 +177,7 @@
  * Implements hook_search_api_index_delete().
  */
 function search_api_page_search_api_index_delete(SearchApiIndex $index) {
-  foreach (entity_load('search_api_page', FALSE, array('index_id' => $index->id, 'enabled' => 1)) as $page) {
+  foreach (entity_load('search_api_page', FALSE, array('index_id' => $index->machine_name, 'enabled' => 1)) as $page) {
     search_api_page_delete($page->id);
   }
 }
@@ -206,7 +205,7 @@
 function search_api_page_load($id, $reset = FALSE) {
   $ret = entity_load('search_api_page', array($id), array(), $reset);
   if (!$ret) {
-    return NULL;
+    return FALSE;
   }
   $ret = array_shift($ret);
   if (is_string($ret->options)) {
Index: contrib/search_api_page/search_api_page.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_page/Attic/search_api_page.pages.inc,v
retrieving revision 1.1.2.8
diff -u -r1.1.2.8 search_api_page.pages.inc
--- contrib/search_api_page/search_api_page.pages.inc	12 Oct 2010 16:11:04 -0000	1.1.2.8
+++ contrib/search_api_page/search_api_page.pages.inc	26 Oct 2010 21:09:18 -0000
@@ -165,6 +165,9 @@
   $entities = $variables['entities'];
   $keys = $variables['keys'];
 
+  $index->dataAlter($entities);
+  entity_prepare_view($index->entity_type, $entities);
+
   $output = '<p class="search-performance">' . format_plural($results['result count'],
       'The search found 1 result in @sec seconds.',
       'The search found @count results in @sec seconds.',
@@ -199,7 +202,6 @@
   $index = $variables['index'];
   $id = $variables['result']['id'];
   $entity = $variables['entity'];
-  entity_prepare_view($index->entity_type, array($entity));
 
   $wrapper = entity_metadata_wrapper($index->entity_type, $entity);
 
@@ -219,7 +221,7 @@
   }
 
   $output = '<h3>' . ($url ? l($name, $url['path'], $url['options']) : check_plain($name)) . "</h3>\n";
-  if ($text) {
+  if ($text && function_exists('text_summary')) {
     $output .= text_summary($text);
   }
 
Index: contrib/search_api_page/search_api_page.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_page/Attic/search_api_page.admin.inc,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 search_api_page.admin.inc
--- contrib/search_api_page/search_api_page.admin.inc	24 Sep 2010 22:55:37 -0000	1.1.2.3
+++ contrib/search_api_page/search_api_page.admin.inc	26 Oct 2010 21:09:18 -0000
@@ -36,7 +36,7 @@
   $edit = '/edit';
   $delete = '/delete';
 
-  $indexes = search_api_list_indexes();
+  $indexes = entity_load('search_api_index', FALSE);
   foreach (entity_load('search_api_page', FALSE) as $page) {
     $url = $pre . $page->id;
     $index = $indexes[$page->index_id];
@@ -44,7 +44,7 @@
       $page->enabled ? $t_enabled : $t_disabled,
       l($page->name, $page->path),
       l($page->path, $page->path),
-      l($index->name, $pre_index . $index->id),
+      l($index->name, $pre_index . $index->machine_name),
       l($t_edit, $url . $edit),
     );
   }
@@ -63,7 +63,7 @@
 function search_api_page_admin_add(array $form, array &$form_state) {
   $form = array();
   if (empty($form_state['step_one'])) {
-    $indexes = search_api_list_indexes();
+    $indexes = entity_load('search_api_index', FALSE);
     if (!$indexes) {
       drupal_set_message(t('There are no searches indexes which can be searched. Please <a href="@url">create an index</a> first.', array('@url' => url('admin/config/search/search_api/add_index'))), 'warning');
       return array();
@@ -71,12 +71,12 @@
     $index_options = array();
     foreach ($indexes as $index) {
       if ($index->enabled) {
-        $index_options[$index->id] = $index->name;
+        $index_options[$index->machine_name] = $index->name;
       }
     }
     foreach ($indexes as $index) {
       if (!$index->enabled) {
-        $index_options[$index->id] = $index->name . ' (' . t('disabled') . ')';
+        $index_options[$index->machine_name] = $index->name . ' (' . t('disabled') . ')';
       }
     }
 
@@ -141,13 +141,13 @@
     '#default_value' => 'terms',
   );
 
+  $fields = array();
   if (!empty($index->options['fields'])) {
-    $fields = array();
-    foreach ($index->getFulltextFields() as $name) {
+    foreach ($index->getFulltextFields(TRUE) as $name) {
       $fields[$name] = $index->options['fields'][$name]['name'];
     }
   }
-  if (!empty($fields)) {
+  if (count($fields) > 1) {
     $form['fields'] = array(
       '#type' => 'select',
       '#title' => t('Searched fields'),
@@ -214,7 +214,7 @@
  */
 function search_api_page_admin_edit(array $form, array &$form_state, stdClass $page) {
   $index = search_api_index_load($page->index_id);
-  $form = array();
+  $form_state['page'] = $page;
 
   $form['name'] = array(
     '#type' => 'textfield',
@@ -238,7 +238,7 @@
   $form['index'] = array(
     '#type' => 'item',
     '#title' => t('Index'),
-    '#description' => l($index->name, 'admin/config/search/search_api/index/' . $index->id),
+    '#description' => l($index->name, 'admin/config/search/search_api/index/' . $index->machine_name),
   );
   $form['path'] = array(
     '#type' => 'textfield',
@@ -280,13 +280,13 @@
     '#default_value' => $page->options['mode'],
   );
 
+  $fields = array();
   if (!empty($index->options['fields'])) {
-    $fields = array();
-    foreach ($index->getFulltextFields() as $name) {
+    foreach ($index->getFulltextFields(TRUE) as $name) {
       $fields[$name] = $index->options['fields'][$name]['name'];
     }
   }
-  if (!empty($fields)) {
+  if (count($fields) > 1) {
     $form['options']['fields'] = array(
       '#type' => 'select',
       '#title' => t('Searched fields'),
@@ -312,17 +312,12 @@
     '#default_value' => $page->options['per_page'],
   );
 
-  $form['id'] = array(
-    '#type' => 'value',
-    '#value' => $page->id,
-  );
-
-  $form['submit'] = array(
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save changes'),
   );
-
-  $form['delete'] = array(
+  $form['actions']['delete'] = array(
     '#type' => 'fieldset',
     '#title' => t('Delete search page'),
     '#description' => t('This will delete the search page along with all of its settings.'),
@@ -344,8 +339,8 @@
   if ($form_state['values']['op'] == t('Save changes')) {
     $form_state['values']['path'] = drupal_strtolower(trim($form_state['values']['path']));
     $pages = entity_load('search_api_page', FALSE, array('path' => $form_state['values']['path']));
-    if (count($pages) > 1 || (($page = array_shift($pages)) && $page->id != $form_state['values']['id'])) {
-      form_set_error('options][path', t('The entered path is already in use. Please enter a unique path.'));
+    if (count($pages) > 1 || (($page = array_shift($pages)) && $page->id != $form_state['page']->id)) {
+      form_set_error('path', t('The entered path is already in use. Please enter a unique path.'));
     }
   }
 }
@@ -356,16 +351,13 @@
 function search_api_page_admin_edit_submit(array $form, array &$form_state) {
   $op = $form_state['values']['op'];
   form_state_values_clean($form_state);
-  $values = $form_state['values'];
-  $id = $values['id'];
-  unset($values['id']);
+  $form_state['redirect'] = 'admin/config/search/search_api/page';
+
   if ($op == t('Delete search page')) {
-    search_api_page_delete($id);
+    search_api_page_delete($form_state['page']->id);
     drupal_set_message(t('The search page was successfully deleted.'));
-    $form_state['redirect'] = 'admin/config/search/search_api/page';
     return;
   }
-  search_api_page_edit($id, $values);
+  search_api_page_edit($form_state['page']->id, $form_state['values']);
   drupal_set_message(t('The changes were successfully saved.'));
-  $form_state['redirect'] = 'admin/config/search/search_api/page';
 }
Index: contrib/search_api_solr/service.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_solr/Attic/service.inc,v
retrieving revision 1.1.2.16
diff -u -r1.1.2.16 service.inc
--- contrib/search_api_solr/service.inc	19 Oct 2010 16:18:38 -0000	1.1.2.16
+++ contrib/search_api_solr/service.inc	26 Oct 2010 21:09:19 -0000
@@ -158,7 +158,7 @@
   }
 
   public function removeIndex($index) {
-    $id = is_object($index) ? $index->id : $index;
+    $id = is_object($index) ? $index->machine_name : $index;
     try {
       $this->connect();
       $this->solr->deleteByQuery("index_id:" . $id);
@@ -171,7 +171,7 @@
   public function indexItems(SearchApiIndex $index, array $items) {
     $documents = array();
     $ret = array();
-    $index_id = $index->id;
+    $index_id = $index->machine_name;
     $fields = $this->getFieldNames($index);
 
     foreach ($items as $id => $item) {
@@ -192,7 +192,7 @@
         $ret[] = $id;
       }
       catch (Exception $e) {
-        watchdog('search_api_solr', t('An error occurred while indexing: !msg.', array('!msg' => $e->getMessage())));
+        watchdog('search_api_solr', t('An error occurred while indexing !type with ID !id: !msg.', array('!type' => $index->entity_type, '!id' => $id, '!msg' => $e->getMessage())), NULL, WATCHDOG_WARNING);
       }
     }
 
@@ -210,7 +210,7 @@
           array('!status' => $response->getHttpStatus(), '!msg' => $response->getHttpStatusMessage())));
     }
     catch (Exception $e) {
-      watchdog('search_api_solr', t('An error occurred while indexing: !msg.', array('!msg' => $e->getMessage())));
+      watchdog('search_api_solr', t('An error occurred while indexing: !msg.', array('!msg' => $e->getMessage())), NULL, WATCHDOG_ERROR);
     }
     return array();
   }
@@ -302,7 +302,7 @@
   public function deleteItems($ids = 'all', SearchApiIndex $index = NULL) {
     $this->connect();
     if ($index) {
-      $index_id = $index->id;
+      $index_id = $index->machine_name;
       if (is_array($ids)) {
         $solr_ids = array();
         foreach ($ids as $id) {
@@ -346,7 +346,7 @@
     // Extract filters
     $filter = $query->getFilter();
     $fq = $this->createFilterQueries($filter, $fields, $index->options['fields']);
-    $fq[] = 'index_id:' . $index->id;
+    $fq[] = 'index_id:' . $index->machine_name;
 
     // Extract sort
     $sort = array();
@@ -426,7 +426,7 @@
               }
             }
             foreach ($terms as $term => $count) {
-              if ($count > $min_count) {
+              if ($count >= $min_count) {
                 $term = $term == '_empty_' ? '!' : '"' . $term . '"';
                 $results['search_api_facets'][$delta][] = array(
                   'filter' => $term,
Index: contrib/search_api_solr/schema.xml
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_solr/Attic/schema.xml,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 schema.xml
--- contrib/search_api_solr/schema.xml	28 Sep 2010 15:37:11 -0000	1.1.2.4
+++ contrib/search_api_solr/schema.xml	26 Oct 2010 21:09:19 -0000
@@ -307,14 +307,14 @@
 <!--
      The document IDs.
      - item_id contains the entity ID, e.g. a node's nid.
-     - index_id is the ID of the search index this entry belongs to.
+     - index_id is the machine name of the search index this entry belongs to.
      - id is a combination of the two, for uniquely identifying a document.
        The format used is "$index_id-$item_id".
 -->
 
    <field name="id"       type="string" indexed="true" stored="true" required="true" />
    <field name="item_id"  type="long"   indexed="true" stored="true" required="true" />
-   <field name="index_id" type="long"   indexed="true" stored="true" required="true" />
+   <field name="index_id" type="string" indexed="true" stored="true" required="true" />
 
    <!-- Since sorting by ID is explicitly allowed, store item_id also in a sortable way. -->
    <copyField source="item_id" dest="is_search_api_id" />
Index: contrib/search_api_solr/README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_solr/Attic/README.txt,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 README.txt
--- contrib/search_api_solr/README.txt	30 Sep 2010 18:42:27 -0000	1.1.2.4
+++ contrib/search_api_solr/README.txt	26 Oct 2010 21:09:18 -0000
@@ -20,7 +20,7 @@
 as the query to Solr. For details about Lucene's query syntax, see [1]. There
 are also some Solr additions to this, listed at [2]. Note however that, by
 default, this module uses the dismax query handler, so searches like
-"field:value" won't with the "direct" mode.
+"field:value" won't work with the "direct" mode.
 
 [1] http://lucene.apache.org/java/2_9_1/queryparsersyntax.html
 [2] http://wiki.apache.org/solr/SolrQuerySyntax
Index: contrib/search_api_solr/search_api_solr.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_solr/Attic/search_api_solr.install,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 search_api_solr.install
--- contrib/search_api_solr/search_api_solr.install	15 Oct 2010 14:33:22 -0000	1.1.2.4
+++ contrib/search_api_solr/search_api_solr.install	26 Oct 2010 21:09:19 -0000
@@ -49,7 +49,7 @@
     if ($unavailable) {
       if ($unavailable == 1) {
         $ret['search_api_solr']['description'] = t('The Solr server of <a href="!url">%name</a> could not be reached.',
-            array('!url' => url('admin/config/search/search_api/server/' . $last->id), '%name' => $last->name));
+            array('!url' => url('admin/config/search/search_api/server/' . $last->machine_name), '%name' => $last->name));
       }
       else {
         $ret['search_api_solr']['description'] = t('@count Solr servers could not be reached.', array('@count' => $unavailable));
@@ -68,7 +68,35 @@
  * Implements hook_uninstall().
  */
 function search_api_solr_uninstall() {
-  db_delete('search_api_server')
-    ->condition('class', 'search_api_solr_service')
-    ->execute();
+  if (module_exists('search_api')) {
+    db_delete('search_api_server')
+      ->condition('class', 'search_api_solr_service')
+      ->execute();
+  }
+}
+
+/**
+ * Implements hook_update_N().
+ *
+ * Implements transition from using the index IDs to using machine names.
+ */
+function search_api_solr_update_7001() {
+  // @todo What is the best way to do this?
+//  foreach (db_query('SELECT * FROM {search_api_server} WHERE class = :class', array(':class' => 'search_api_solr_service'), array('fetch' => PDO::FETCH_ASSOC)) as $values) {
+//    $server = new SearchApiServer($values);
+//    $server->deleteItems('all');
+//    $query = db_select('search_api_index', 'i')
+//      ->fields('i', array('machine_name'))
+//      ->condition('server', $server->machine_name);
+//    db_update('search_api_item')
+//      ->fields(array(
+//        'changed' => REQUEST_TIME,
+//      ))
+//      ->condition('index_id', $query, 'IN')
+//      ->execute();
+//  }
+
+  return t('The Solr search module was updated. ' .
+      'Please stop your Solr servers, replace their schema.xml with the new version and then start them again. ' .
+      'All data indexed on Solr servers will have to be reindexed.');
 }
Index: search_api.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/Attic/search_api.admin.inc,v
retrieving revision 1.1.2.58
diff -u -r1.1.2.58 search_api.admin.inc
--- search_api.admin.inc	19 Oct 2010 17:47:37 -0000	1.1.2.58
+++ search_api.admin.inc	26 Oct 2010 21:09:15 -0000
@@ -9,19 +9,16 @@
   drupal_add_css($base_path . 'search_api.admin.css');
 
   $header = array(
-    // @todo Wait for http://drupal.org/node/109493
-//    array('data' => t('Status'), 'field' => 'enabled', 'descending' => TRUE, 'sort' => 'desc'),
-    array('data' => t('Status'), 'field' => 'enabled', 'sort' => 'desc'),
+    t('Status'),
     array('data' => t('Type'), 'colspan' => 2),
-    array('data' => t('Name'), 'field' => 'name'),
+    t('Name'),
     array('data' => t('Operations'), 'colspan' => 3),
   );
 
-  $servers = search_api_list_servers(FALSE, $header);
-  $all_indexes = search_api_list_indexes(array('only_enabled' => FALSE), $header);
+  $servers = entity_load('search_api_server', FALSE);
   $indexes = array();
-  foreach ($all_indexes as $index) {
-    $indexes[$index->server][$index->id] = $index;
+  foreach (entity_load('search_api_index', FALSE) as $index) {
+    $indexes[$index->server][$index->machine_name] = $index;
   }
 
   $rows = array();
@@ -52,7 +49,7 @@
   $edit = '/edit';
   $delete = '/delete';
   foreach ($servers as $server) {
-    $url = $pre . $server->id;
+    $url = $pre . $server->machine_name;
     $rows[] = array(
       $server->enabled ? $t_enabled : $t_disabled,
       $t_server,
@@ -61,9 +58,9 @@
       l($t_edit, $url . $edit),
       l($t_delete, $url . $delete),
     );
-    if (!empty($indexes[$server->id])) {
-      foreach ($indexes[$server->id] as $index) {
-        $url = $pre_index . $index->id;
+    if (!empty($indexes[$server->machine_name])) {
+      foreach ($indexes[$server->machine_name] as $index) {
+        $url = $pre_index . $index->machine_name;
         $rows[] = array(
           $index->enabled ? $t_enabled : $t_disabled,
           '',
@@ -80,7 +77,7 @@
   }
   if (!empty($indexes[''])) {
     foreach ($indexes[''] as $index) {
-      $url = $pre_index . $index->id;
+      $url = $pre_index . $index->machine_name;
       $link = l($index->name, $url);
       $rows[] = array(
         $t_disabled,
@@ -256,8 +253,8 @@
   unset($values['options']);
   $form_state['server']  = $server = new SearchApiServer($values);
   $server->configurationFormSubmit($form['options']['form'], $options, $form_state);
-  $id = search_api_server_insert((array) $server);
-  $form_state['redirect'] = 'admin/config/search/search_api/server/' . $id;
+  $server->save();
+  $form_state['redirect'] = 'admin/config/search/search_api/server/' . $server->machine_name;
   drupal_set_message(t('The server was successfully created.'));
 }
 
@@ -276,26 +273,22 @@
  * @param $action
  *   One of 'enable', 'disable', 'delete'; or NULL if the server is only viewed.
  */
-function search_api_admin_server_view(SearchApiServer $server = NULL, $action = NULL) {
-  if (empty($server)) {
-    return MENU_NOT_FOUND;
-  }
-
+function search_api_admin_server_view(SearchApiServer $server, $action = NULL) {
   if (!empty($action)) {
     if ($action == 'enable') {
-      if (search_api_server_enable($server->id)) {
+      if ($server->enable()) {
         drupal_set_message(t('The server was successfully enabled.'));
       }
       else {
         drupal_set_message(t('The server could not be enabled. Check the logs for details.'), 'error');
       }
-      drupal_goto('admin/config/search/search_api/server/' . $server->id);
+      drupal_goto('admin/config/search/search_api/server/' . $server->machine_name);
     }
     else {
       $ret = drupal_get_form('search_api_admin_confirm', 'server', $action, $server);
-    }
-    if ($ret) {
-      return $ret;
+      if ($ret) {
+        return $ret;
+      }
     }
   }
 
@@ -342,10 +335,10 @@
   $output .= '<dt>' . t('Status') . '</dt>' . "\n";
   $output .= '<dd>';
   if ($enabled) {
-    $output .= t('enabled (!disable_link)', array('!disable_link' => l(t('disable'), 'admin/config/search/search_api/server/' . $id . '/disable')));
+    $output .= t('enabled (!disable_link)', array('!disable_link' => l(t('disable'), 'admin/config/search/search_api/server/' . $machine_name . '/disable')));
   }
   else {
-    $output .= t('disabled (!enable_link)', array('!enable_link' => l(t('enable'), 'admin/config/search/search_api/server/' . $id . '/enable')));
+    $output .= t('disabled (!enable_link)', array('!enable_link' => l(t('enable'), 'admin/config/search/search_api/server/' . $machine_name . '/enable')));
   }
   $output .= '</dd>' . "\n";
 
@@ -444,8 +437,8 @@
   }
   $server->configurationFormSubmit($form['options']['form'], $values['options'], $form_state);
 
-  $ret = search_api_server_edit($server->id, $values);
-  $form_state['redirect'] = 'admin/config/search/search_api/server/' . $server->id;
+  $ret = $server->update($values);
+  $form_state['redirect'] = 'admin/config/search/search_api/server/' . $server->machine_name;
   if ($ret) {
     drupal_set_message(t('The search server was successfully edited.'));
   }
@@ -505,16 +498,16 @@
     '#default_value' => '',
     '#options' => array('' => t('< No server >'))
   );
-  $servers = search_api_list_servers(FALSE);
+  $servers = entity_load('search_api_server', FALSE);
   // List enabled servers first.
   foreach ($servers as $server) {
     if ($server->enabled) {
-      $form['server']['#options'][$server->id] = $server->name;
+      $form['server']['#options'][$server->machine_name] = $server->name;
     }
   }
   foreach ($servers as $server) {
     if (!$server->enabled) {
-      $form['server']['#options'][$server->id] = $server->name . ' (' . t('disabled') . ')';
+      $form['server']['#options'][$server->machine_name] = t('@server_name (disabled)', array('@server_name' => $server->name));
     }
   }
   $form['cron_limit'] = array(
@@ -547,7 +540,7 @@
   $cron_limit = $form_state['values']['cron_limit'];
   if ($cron_limit != '' . ((int) $cron_limit)) {
     // We don't enforce stricter rules and treat all negative values as -1.
-    form_set_error('cron_limit', t('The cron limit must be a number.'));
+    form_set_error('cron_limit', t('The cron limit must be an integer.'));
   }
 }
 
@@ -565,7 +558,7 @@
   $id = search_api_index_insert($values);
 
   drupal_set_message(t('The index was successfully created. Please set up its workflow now.'));
-  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $id . '/workflow';
+  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $values['machine_name'] . '/workflow';
 }
 
 /**
@@ -581,19 +574,19 @@
 
   if (!empty($action)) {
     if ($action == 'enable') {
-      if (search_api_index_enable($index->id)) {
+      if ($index->enable()) {
         drupal_set_message(t('The index was successfully enabled.'));
       }
       else {
         drupal_set_message(t('The index could not be enabled. Check the logs for details.'), 'error');
       }
-      drupal_goto('admin/config/search/search_api/index/' . $index->id);
+      drupal_goto('admin/config/search/search_api/index/' . $index->machine_name);
     }
     else {
       $ret = drupal_get_form('search_api_admin_confirm', 'index', $action, $index);
-    }
-    if ($ret) {
-      return $ret;
+      if ($ret) {
+        return $ret;
+      }
     }
   }
 
@@ -642,10 +635,10 @@
   $output .= '<dt>' . t('Status') . '</dt>' . "\n";
   $output .= '<dd>';
   if ($enabled) {
-    $output .= t('enabled (!disable_link)', array('!disable_link' => l(t('disable'), 'admin/config/search/search_api/index/' . $id . '/disable')));
+    $output .= t('enabled (!disable_link)', array('!disable_link' => l(t('disable'), 'admin/config/search/search_api/index/' . $machine_name . '/disable')));
   }
   elseif ($server && $server->enabled) {
-    $output .= t('disabled (!enable_link)', array('!enable_link' => l(t('enable'), 'admin/config/search/search_api/index/' . $id . '/enable')));
+    $output .= t('disabled (!enable_link)', array('!enable_link' => l(t('enable'), 'admin/config/search/search_api/index/' . $machine_name . '/enable')));
   }
   else {
     $output .= t('disabled');
@@ -667,7 +660,7 @@
 
   if (!empty($server)) {
     $output .= '<dt>' . t('Server') . '</dt>' . "\n";
-    $output .= '<dd>' . l($server->name, 'admin/config/search/search_api/server/' . $server->id);
+    $output .= '<dd>' . l($server->name, 'admin/config/search/search_api/server/' . $server->machine_name);
     if (!empty($server->description)) {
       $output .= '<p class="description">' . nl2br(check_plain($server->description)) . '</p>';
     }
@@ -726,10 +719,7 @@
   $server = $index->server();
 
   $form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css';
-  $form['id'] = array(
-    '#type' => 'value',
-    '#value' => $index->id,
-  );
+  $form_state['index'] = $index;
 
   $form['status_message'] = array(
     '#type' => 'item',
@@ -842,9 +832,8 @@
 function search_api_admin_index_status_form_submit(array $form, array &$form_state) {
   $redirect = &$form_state['redirect'];
   $values = $form_state['values'];
-  $id = $values['id'];
-  $base = 'admin/config/search/search_api';
-  $pre = $base . '/index/' . $id;
+  $index = $form_state['index'];
+  $pre = 'admin/config/search/search_api/index/' . $index->machine_name;
   switch ($values['op']) {
     case t('Enable'):
       $redirect = $pre . '/enable';
@@ -853,30 +842,30 @@
       $redirect = $pre . '/disable';
       break;
     case t('Index now'):
-      $index = search_api_index_load($id);
-      if ($index) {
-        $limit = $values['limit'];
-        // @todo Batch API?
+      $limit = $values['limit'];
+      // @todo Batch API?
+      try {
         $ret = search_api_index_items($index, $limit);
-        if ($ret) {
-          drupal_set_message(format_plural($ret, 'Successfully indexed 1 item.', 'Successfully indexed @count items.'));
-          if (($limit > 0 && $ret < min(array($limit, $values['remaining'])))
-              || ($limit < 0 && $ret < $values['remaining'])) {
-            drupal_set_message(t("Some items couldn't be indexed. Check the logs for details."), 'warning');
-          }
-        }
-        else {
-          drupal_set_message(t("Couldn't index items. Check the logs for details."), 'error');
+      }
+      catch (SearchApiException $e) {
+        $ret = FALSE;
+        watchdog('search_api', t('An error occurred while trying to index items on index !name: !message',
+            array('!name' => $index->machine_name, '!message' => $e->getMessage())), NULL, WATCHDOG_ERROR);
+      }
+      if ($ret) {
+        drupal_set_message(format_plural($ret, 'Successfully indexed 1 item.', 'Successfully indexed @count items.'));
+        if (($limit > 0 && $ret < min(array($limit, $values['remaining'])))
+            || ($limit < 0 && $ret < $values['remaining'])) {
+          drupal_set_message(t("Some items couldn't be indexed. Check the logs for details."), 'warning');
         }
-        $redirect = $pre . '/status';
       }
       else {
-        drupal_set_message(t('This index does not exist.'), 'warning');
-        $redirect = $base;
+        drupal_set_message(t("Couldn't index items. Check the logs for details."), 'error');
       }
+      $redirect = $pre . '/status';
       break;
     case t('Re-index content'):
-      if (search_api_index_reindex($id)) {
+      if ($index->reindex()) {
         drupal_set_message(t('The index was successfully scheduled for re-indexing.'));
       }
       else {
@@ -885,7 +874,7 @@
       $redirect = $pre . '/status';
       break;
     case t('Clear index'):
-      if (search_api_index_clear($id)) {
+      if ($index->clear()) {
         drupal_set_message(t('The index was successfully cleared.'));
       }
       else {
@@ -906,6 +895,8 @@
  *   The index to edit.
  */
 function search_api_admin_index_edit(array $form, array &$form_state, SearchApiIndex $index) {
+  $form_state['index'] = $index;
+
   $form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css';
   $form['name'] = array(
     '#type' => 'textfield',
@@ -926,16 +917,16 @@
     '#default_value' => $index->server,
     '#options' => array('' => t('< No server >'))
   );
-  $servers = search_api_list_servers(FALSE);
+  $servers = entity_load('search_api_server', FALSE);
   // List enabled servers first.
   foreach ($servers as $server) {
     if ($server->enabled) {
-      $form['server']['#options'][$server->id] = $server->name;
+      $form['server']['#options'][$server->machine_name] = $server->name;
     }
   }
   foreach ($servers as $server) {
     if (!$server->enabled) {
-      $form['server']['#options'][$server->id] =  t('@server_name (disabled)', array('@server_name' => $server->name));
+      $form['server']['#options'][$server->machine_name] = t('@server_name (disabled)', array('@server_name' => $server->name));
     }
   }
   $form['cron_limit'] = array(
@@ -948,11 +939,6 @@
     '#attributes' => array('class' => array('search-api-cron-limit')),
   );
 
-  $form['id'] = array(
-    '#type' => 'value',
-    '#value' => $index->id,
-  );
-
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save settings'),
@@ -979,14 +965,13 @@
   form_state_values_clean($form_state);
 
   $values = $form_state['values'];
-  $index = search_api_index_load($values['id']);
+  $index = $form_state['index'];
   $values['options'] = $index->options;
   $values['options']['cron_limit'] = $values['cron_limit'];
   unset($values['cron_limit']);
-  unset($values['id']);
 
-  $ret = search_api_index_edit($index->id, $values);
-  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->id;
+  $ret = $index->update($values);
+  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name;
   if ($ret) {
     drupal_set_message(t('The search index was successfully edited.'));
   }
@@ -1008,6 +993,7 @@
   $processor_info = search_api_get_processors();
   $options = empty($index->options) ? array() : $index->options;
 
+  $form_state['index'] = $index;
   $form['#tree'] = TRUE;
   $form['#attached']['js'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.js';
 
@@ -1159,7 +1145,6 @@
     }
   }
 
-  $form['id'] = array('#type' => 'value', '#value' => $index->id);
   $form['actions'] = array('#type' => 'actions');
   $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
 
@@ -1200,9 +1185,10 @@
 function search_api_admin_index_workflow_submit(array $form, array &$form_state) {
   $values = $form_state['values'];
   unset($values['processors']['settings']);
-  $index = search_api_index_load($values['id']);
+  $index = $form_state['index'];
 
   $options = empty($index->options) ? array() : $index->options;
+  $fields_set = !empty($options['fields']);
 
   // Set default indexed status for newly added fields.
   foreach ($values['callbacks'] as $func => $callback) {
@@ -1213,11 +1199,16 @@
       }
       $items = array();
       $properties = $func($index, $items);
+      $types = search_api_field_types();
       if ($properties) {
         foreach ($properties as $key => $field) {
           $type = $field['type'];
           $inner = search_api_extract_inner_type($type);
-          if ($inner == 'token' || !empty($field['options list'])) {
+          if ($inner != 'token' && empty($types[$inner])) {
+            // Someone apparently added a structure or entity as a property in a data-alter callback.
+            continue;
+          }
+          if ($inner == 'token' || (search_api_is_text_type($inner) && !empty($field['options list']))) {
             $old = $type;
             $type = 'string';
             while (search_api_is_list_type($old)) {
@@ -1243,9 +1234,9 @@
   uasort($options['data_alter_callbacks'], 'search_api_admin_element_compare');
   uasort($options['processors'], 'search_api_admin_element_compare');
 
-  $ret = search_api_index_edit($index->id, array('options' => $options));
+  $ret = $index->update(array('options' => $options));
   if ($ret) {
-    search_api_index_reindex($index->id);
+    $index->reindex();
     drupal_set_message(t("The search index' workflow was successfully edited. " .
         'All content was scheduled for re-indexing so the new settings can take effect.'));
   }
@@ -1253,12 +1244,12 @@
     drupal_set_message(t('No values were changed.'));
   }
 
-  if (empty($options['fields'])) {
+  if (!$fields_set) {
     drupal_set_message(t('Please set the fields that should be indexed.'));
-    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->id . '/fields';
+    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name . '/fields';
   }
   else {
-    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->id;
+    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name;
   }
 }
 
@@ -1298,6 +1289,7 @@
   }
   $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
 
+  $form_state['index'] = $index;
   $form['#theme'] = 'search_api_admin_fields_table';
   $form['#tree'] = TRUE;
   $form['description'] = array(
@@ -1349,11 +1341,6 @@
     }
   }
 
-  $form['id'] = array(
-    '#type' => 'value',
-    '#value' => $index->id,
-  );
-
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save changes'),
@@ -1523,15 +1510,17 @@
  * Submit function for search_api_admin_index_fields.
  */
 function search_api_admin_index_fields_submit(array $form, array &$form_state) {
-  $id = $form_state['values']['id'];
+  $index = $form_state['index'];
+  $options = isset($index->options) ? $index->options : array();
   if ($form_state['values']['op'] == t('Save changes')) {
     $fields = $form_state['values']['fields'];
     foreach ($fields as $name => &$field) {
       unset($field['description']);
     }
-    $ret = search_api_index_edit_fields($id, $fields);
+    $options['fields'] = $fields;
+    $ret = $index->update(array('options' => $options));
 
-    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $id;
+    $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name;
     if ($ret) {
       drupal_set_message(t('The indexed fields were successfully changed. ' .
           'The index was cleared and will have to be re-indexed with the new settings.'));
@@ -1542,9 +1531,7 @@
     return;
   }
   // Adding a related entity's fields
-  $index = search_api_index_load($id);
-
-  $wrapper = $orig_wrapper = entity_metadata_wrapper($index->entity_type);
+  $wrapper = $orig_wrapper = entity_metadata_wrapper($index->entity_type, NULL, array('property info alter' => array($index, 'propertyInfoAlter')));
   $prefix = $form_state['values']['additional']['field'];
   $types = search_api_field_types();
 
@@ -1560,8 +1547,8 @@
     return;
   }
 
-  if (!empty($index->options['fields'])) {
-    $fields = $index->options['fields'];
+  if (!empty($options['fields'])) {
+    $fields = $options['fields'];
   }
   else {
     $fields = _search_api_admin_get_fields($index, $orig_wrapper);
@@ -1576,8 +1563,8 @@
       $fields[$key] = array('type' => $info['type'], 'boost' => '1.0', 'indexed' => 0);
     }
   }
-  $index->options['fields'] = $fields;
-  $ret = search_api_index_edit($id, array('options' => $index->options));
+  $options['fields'] = $fields;
+  $ret = $index->update(array('options' => $options));
 
   if ($ret) {
     drupal_set_message(t('The available fields were successfully changed.'));
@@ -1585,7 +1572,7 @@
   else {
     drupal_set_message(t('No values were changed.'));
   }
-  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $id . '/fields';
+  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name . '/fields';
 }
 
 
@@ -1660,7 +1647,7 @@
     ),
     'id' => array(
       '#type' => 'value',
-      '#value' => $info->id,
+      '#value' => $info->machine_name,
     ),
     'message' => array(
       '#type' => 'value',
@@ -1668,7 +1655,7 @@
     ),
   );
   $desc = "<h3>{$text[1]}</h3><p>{$text[2]}</p>";
-  return confirm_form($form, $text[0], "admin/config/search/search_api/$type/{$info->id}", $desc);
+  return confirm_form($form, $text[0], "admin/config/search/search_api/$type/{$info->machine_name}", $desc);
 }
 
 /**
Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/Attic/CHANGELOG.txt,v
retrieving revision 1.1.2.8
diff -u -r1.1.2.8 CHANGELOG.txt
--- CHANGELOG.txt	19 Oct 2010 17:47:37 -0000	1.1.2.8
+++ CHANGELOG.txt	26 Oct 2010 21:09:14 -0000
@@ -3,6 +3,7 @@
 Search API 1.0, dev (xx/xx/xxxx):
 ------------------------------------
 API changes:
+- #939482: Fix exportables.
 - #939092: Several API changes regarding service class methods.
 - #939414: Enhanced service class descriptions. [soft API change]
 - #939464: Documented Entity API's module and status properties.
@@ -10,6 +11,7 @@
 - #936360: Make servers and indexes exportable.
 
 Others:
+- #951830: "List of language IDs" context suspicious
 - #939414: Rename "data-alter callbacks" to "data alterations".
 - #939460: Views integration troubles.
 - #945754: Fix server and index machine name inputs.
Index: search_api.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/search_api.module,v
retrieving revision 1.1.2.65
diff -u -r1.1.2.65 search_api.module
--- search_api.module	15 Oct 2010 15:55:25 -0000	1.1.2.65
+++ search_api.module	26 Oct 2010 21:09:16 -0000
@@ -206,7 +206,7 @@
  * Will index $options['cron-limit'] items for each enabled index.
  */
 function search_api_cron() {
-  foreach (search_api_list_indexes(array('enabled' => TRUE)) as $index) {
+  foreach (entity_load('search_api_index', FALSE, array('enabled' => TRUE)) as $index) {
     $limit = isset($index->options['cron_limit'])
         ? $index->options['cron_limit']
         : SEARCH_API_DEFAULT_CRON_LIMIT;
@@ -234,7 +234,6 @@
     'metadata controller class' => FALSE,
     'entity class' => 'SearchApiServer',
     'base table' => 'search_api_server',
-    'load hook' => 'search_api_server_load',
     'uri callback' => 'search_api_server_url',
     'module' => 'search_api',
     'exportable' => TRUE,
@@ -250,7 +249,6 @@
     'metadata controller class' => FALSE,
     'entity class' => 'SearchApiIndex',
     'base table' => 'search_api_index',
-    'load hook' => 'search_api_index_load',
     'uri callback' => 'search_api_index_url',
     'module' => 'search_api',
     'exportable' => TRUE,
@@ -328,6 +326,11 @@
       'description' => t("A string describing the index' use to users."),
       'sanitize' => 'filter_xss',
     ),
+    'server' => array(
+      'label' => t('Server ID'),
+      'type' => 'token',
+      'description' => t('The machine name of the search_api_server with which data should be indexed.'),
+    ),
     'server_entity' => array(
       'label' => t('Server'),
       'type' => 'search_api_server',
@@ -363,14 +366,15 @@
  */
 function search_api_entity_insert($entity, $type) {
   if ($type == 'search_api_index') {
-    // This comment is a lie.
+    // When inserting a new search index, the new index was already inserted into search_api_item.
+    // This would lead to a duplicate-key issue, if we would continue.
     return;
   }
   $info = entity_get_info($type);
   $id = $info['entity keys']['id'];
 
   $query = db_select('search_api_index', 'i');
-  $query->addField('i', 'id', 'index_id');
+  $query->addField('i', 'machine_name', 'index_id');
   $query->addExpression(':item_id', 'item_id', array(':item_id' => $entity->$id));
   $query->addExpression('1', 'changed');
   $query->condition('entity_type', $type);
@@ -395,7 +399,7 @@
   $id = $info['entity keys']['id'];
 
   $query = db_select('search_api_index', 'ind')
-    ->fields('ind', array('id'))
+    ->fields('ind', array('machine_name'))
     ->condition('entity_type', $type);
 
   db_update('search_api_item')
@@ -422,10 +426,10 @@
   $info = entity_get_info($type);
   $id_field = $info['entity keys']['id'];
   $id = $entity->$id_field;
-  foreach (search_api_list_indexes(array('entity_type' => $type)) as $index) {
+  foreach (entity_load('search_api_index', FALSE, array('entity_type' => $type)) as $index) {
     db_delete('search_api_item')
       ->condition('item_id', $id)
-      ->condition('index_id', $index->id)
+      ->condition('index_id', $index->machine_name)
       ->execute();
     if ($index->server) {
       $server = $index->server();
@@ -434,7 +438,7 @@
       }
       else {
         $tasks = variable_get('search_api_tasks', array());
-        $tasks[$server->id][$index->id] = array('delete-' . $id);
+        $tasks[$server->machine_name][$index->machine_name] = array('delete-' . $id);
         variable_set('search_api_tasks', $tasks);
       }
     }
@@ -497,6 +501,10 @@
  * @param $limit
  *   The number of items which should be indexed at most. -1 means no limit.
  *
+ * @throws SearchApiException
+ *   If the index' entity type is unknown or another error occurs during
+ *   indexing.
+ *
  * @return
  *   Number of successfully indexed items.
  */
@@ -537,7 +545,7 @@
   }
   $select = db_select('search_api_item', 'i');
   $select->addField('i', 'item_id');
-  $select->condition('index_id', $index->id);
+  $select->condition('index_id', $index->machine_name);
   $select->condition('changed', 0, '<>');
   $select->orderBy('changed', 'ASC');
   if ($limit > 0) {
@@ -564,7 +572,7 @@
       ->fields(array(
         'changed' => 0,
       ))
-      ->condition('index_id', $index->id)
+      ->condition('index_id', $index->machine_name)
       ->condition('item_id', $ids, 'IN')
       ->execute();
 }
@@ -576,6 +584,9 @@
  *   The index on which the items are indexed.
  * @param array $items
  *   An array of objects containing the entity data.
+ *
+ * @return array
+ *   The definition for the new search_api_url field.
  */
 function search_api_alter_add_url(SearchApiIndex $index, array &$items) {
   foreach ($items as $id => &$item) {
@@ -603,21 +614,16 @@
  *   The index on which the items are indexed.
  * @param array $items
  *   An array of objects containing the entity data.
+ *
+ * @return array
+ *   The definition for the new search_api_fulltext field.
  */
 function search_api_alter_add_fulltext(SearchApiIndex $index, array &$items) {
   if (empty($index->options['fields'])) {
     $fields = FALSE;
   }
   else {
-    $fields = $index->options['fields'];
-    foreach ($fields as $field => $info) {
-      if ($info['type'] != 'text') {
-        unset($fields[$field]);
-      }
-    }
-    if (empty($fields)) {
-      return array();
-    }
+    $fields = array_intersect_key($index->options['fields'], drupal_map_assoc($index->getFulltextFields()));
   }
   foreach ($items as $id => &$item) {
     $wrapper = entity_metadata_wrapper($index->entity_type, $item);
@@ -628,7 +634,7 @@
           $tmp = $wrapper;
           foreach (explode(':', $field) as $property) {
             if (!isset($tmp->$property)) {
-              // This can especially happen for fields added by data alter callbacks - e.g., this one.
+              // This can especially happen for fields added by data alter callbacks – e.g., this one.
               $tmp = FALSE;
               break;
             }
@@ -657,7 +663,7 @@
   return array(
     'search_api_fulltext' => array(
       'label' => t('Fulltext'),
-      'description' => t('The aggregated content of all indexed fulltext fields.'),
+      'description' => t('The aggregated content of all fulltext fields.'),
       'type' => 'text',
     ),
   );
@@ -667,7 +673,7 @@
  * Creates a search query on a specified search index.
  *
  * @param $id
- *   The ID of the index to execute the search on.
+ *   The ID or machine name of the index to execute the search on.
  * @param $options
  *   An associative array of options. The following are recognized:
  *   - filters: Either a SearchApiQueryFilterInterface object or an array of
@@ -691,7 +697,7 @@
  */
 function search_api_query($id, array $options = array()) {
   $index = search_api_index_load($id);
-  if (!$id) {
+  if (!$index) {
     throw new SearchApiException(t('Unknown index with ID !id.', array('!id' => $id)));
   }
   return $index->query($options);
@@ -713,7 +719,7 @@
  *   When storing an executed search, the returned results as specified by
  *   SearchApiQueryInterface::execute(). An empty array, otherwise.
  *
- * @return
+ * @return array
  *   If a search with the specified ID was executed, an array containing
  *   ($query, $results) as used in this function's parameters. If $search_id is
  *   NULL, an array of all executed searches will be returned, keyed by ID.
@@ -904,19 +910,27 @@
 // @todo This could probably be optimized.
 function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields) {
   foreach ($fields as $field => &$info) {
+    // Set "defaults" in case an error occurs later.
+    $info['value'] = NULL;
+    $info['original_type'] = $info['type'];
     try {
       $tmp = $wrapper;
       foreach (explode(':', $field) as $property) {
+        if (!isset($tmp->$property)) {
+          $tmp = NULL;
+          break;
+        }
         $tmp = $tmp->$property;
       }
+      if (!$tmp) {
+        continue;
+      }
       $info['value'] = $tmp->value();
       $property_info = $tmp->info();
       $info['original_type'] = $property_info['type'];
     }
     catch (Exception $e) {
       // This might happen for entity fields that are NULL, e.g. comments without parent.
-      $info['value'] = NULL;
-      $info['original_type'] = $info['type'];
     }
   }
   return $fields;
@@ -925,6 +939,10 @@
 /**
  * Returns a list of all (enabled) search servers.
  *
+ * @deprecated
+ *   This function doesn't take exported entities in code into account. Use
+ *   entity_load() instead and sort manually, if you must.
+ *
  * @param $only_enabled
  *   Whether to retrieve only enabled servers.
  * @param $header
@@ -934,6 +952,7 @@
  *   An array of objects representing all (or, if $enabled is TRUE, only
  *   enabled) search servers.
  */
+// @todo Remove when changing the API.
 function search_api_list_servers($only_enabled = TRUE, $header = NULL) {
   $servers = &drupal_static(__FUNCTION__);
 
@@ -951,7 +970,7 @@
     $servers[$enabled] = array();
     foreach ($results as $row) {
       $row->options = unserialize($row->options);
-      $servers[$enabled][$row->id] = $row;
+      $servers[$enabled][$row->machine_name] = $row;
     }
 
     module_invoke_all('search_api_server_load', $servers[$enabled]);
@@ -974,7 +993,7 @@
  */
 function search_api_server_load($id, $reset = FALSE) {
   $ret = entity_load('search_api_server', array($id), array(), $reset);
-  return $ret ? array_shift($ret) : NULL;
+  return $ret ? reset($ret) : FALSE;
 }
 
 /**
@@ -982,7 +1001,8 @@
  */
 function search_api_server_url(SearchApiServer $server) {
   return array(
-    'path' => 'admin/config/search/search_api/server/' . $server->id,
+    'path' => 'admin/config/search/search_api/server/' . $server->machine_name,
+    'options' => array(),
   );
 }
 
@@ -996,48 +1016,20 @@
  *   The newly inserted server's id, or FALSE on error.
  */
 function search_api_server_insert(array $values) {
-  foreach (array('name', 'machine_name', 'class', 'options') as $var) {
-    if (empty($values[$var])) {
-      throw new SearchApiException(t('Property !field has to be set for new server!', array('!field' => $var)));
-    }
-  }
-  if (empty($values['description'])) {
-    $values['description'] = NULL;
-  }
-  $fields = array(
-    'name' => $values['name'],
-    'machine_name' => $values['machine_name'],
-    'description' => $values['description'],
-    'enabled' => empty($values['enabled']) ? 0 : 1,
-    'class' => $values['class'],
-    'options' => serialize($values['options']),
-  );
-  if (!empty($values['id'])) {
-    $fields['id'] = $values['id'];
-  }
-
-  $id = db_insert('search_api_server')->fields($fields)->execute();
-
-  if (!isset($fields['id'])) {
-    $fields['id'] = $id;
-  }
-
-  $server = new SearchApiServer($fields);
-
-  $server->postCreate();
-  module_invoke_all('search_api_server_insert', $server);
-  module_invoke_all('entity_insert', $server, 'search_api_server');
-
-  return $id;
+  $server = new SearchApiServer($values);
+  $server->is_new = TRUE;
+  $server->save();
+  return $server->id;
 }
 
 /**
  * Changes a server's settings.
  *
  * @param $id
- *   The ID of the server whose values should be changed.
+ *   The ID or machine name of the server whose values should be changed.
  * @param array $fields
- *   The new field values to set.
+ *   The new field values to set. The enabled field can't be set this way, use
+ *   search_api_server_enable() and search_api_server_disable() instead.
  *
  * @return
  *   1 if fields were changed, 0 if the fields already had the desired values.
@@ -1045,47 +1037,8 @@
  */
 function search_api_server_edit($id, array $fields) {
   $server = search_api_server_load($id, TRUE);
-  $changeable = array('name' => 1, 'description' => 1, 'options' => 1, 'enabled' => 1);
-  foreach ($fields as $field => $value) {
-    if (!isset($changeable[$field]) || $value === $server->$field) {
-      unset($fields[$field]);
-    }
-  }
-  // If there are no new values, just return 0.
-  if (empty($fields)) {
-    return 0;
-  }
-
-  if (isset($fields['options'])) {
-    $fields['options'] = serialize($fields['options']);
-  }
-  $ret = db_update('search_api_server')
-    ->fields($fields)
-    ->condition('id', $id)
-    ->execute();
-
-  if (!$ret) {
-    return $ret;
-  }
-
-  // Notify server of changed values.
-  if (isset($fields['options'])) {
-    $fields['options'] = unserialize($fields['options']);
-  }
-  $reindex = $server->postUpdate($fields);
-
-  $server = search_api_server_load($server->id, TRUE);
-
-  module_invoke_all('search_api_server_update', $server, 'edit');
-  module_invoke_all('entity_update', $server, 'search_api_server');
-
-  if ($reindex) {
-    foreach (search_api_list_indexes(array('server' => $server->id)) as $index) {
-      search_api_index_reindex($index->id);
-    }
-  }
-
-  return $ret;
+  $ret = $server->update($fields);
+  return $ret ? 1 : $ret;
 }
 
 /**
@@ -1093,151 +1046,53 @@
  * and execute them.
  *
  * @param $id
- *   The ID of the server to enable.
+ *   The ID or machine name of the server to enable.
  *
  * @return
  *   1 on success, 0 or FALSE on failure.
  */
 function search_api_server_enable($id) {
   $server = search_api_server_load($id, TRUE);
-  if ($server->enabled) {
-    return 0;
-  }
-
-  $server->postEnable();
-
-  $ret = db_update('search_api_server')
-    ->fields(array('enabled' => 1))
-    ->condition('id', $server->id)
-    ->execute();
-  if (!$ret) {
-    return $ret;
-  }
-
-  $server->enabled = 1;
-  module_invoke_all('search_api_server_update', $server, 'enable');
-  module_invoke_all('entity_update', $server, 'search_api_server');
-
-  // Were there any changes in the server's indexes while it was disabled?
-  $tasks = variable_get('search_api_tasks', array());
-  if (isset($tasks[$server->id])) {
-    foreach ($tasks[$server->id] as $index_id => $index_tasks) {
-      $index = search_api_index_load($index_id);
-      foreach ($index_tasks as $task) {
-        switch ($task) {
-          case 'add':
-            $server->addIndex($index);
-            break;
-          case 'clear':
-            $server->deleteItems('all', $index);
-            break;
-          case 'fields':
-            if ($server->fieldsUpdated($index)) {
-              _search_api_index_reindex($index_id);
-            }
-            break;
-          case 'remove':
-            $server->removeIndex($index ? $index : $index_id);
-            break;
-          default:
-            if (substr($task, 0, 7) == 'delete-') {
-              $id = substr($task, 7);
-              $server->deleteItems(array($id), $index);
-            }
-            else {
-              watchdog('search_api', t('Unknown task "!task" for server "!name".', array('!task' => $task, '!server' => $server->name)), NULL, 'warning');
-            }
-        }
-      }
-    }
-    unset($tasks[$server->id]);
-    variable_set('search_api_tasks', $tasks);
-  }
-
-  return $ret;
+  $ret = $server->enable();
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Disables a search server, along with all associated indexes.
  *
  * @param $id
- *   The ID of the server to disable.
+ *   The ID or machine name of the server to disable.
  *
  * @return
  *   1 on success, 0 or FALSE on failure.
  */
 function search_api_server_disable($id) {
   $server = search_api_server_load($id, TRUE);
-  if (!$server->enabled) {
-    return 0;
-  }
-
-  $server->postDisable();
-
-  $id = $server->id;
-  $indexes = search_api_list_indexes(array('server' => $id));
-  foreach ($indexes as $index) {
-    search_api_index_disable($index->id);
-  }
-  $ret = db_update('search_api_server')
-    ->fields(array('enabled' => 0))
-    ->condition('id', $id)
-    ->execute();
-  if (!$ret) {
-    return $ret;
-  }
-
-  $server->enabled = 0;
-  module_invoke_all('search_api_server_update', $server, 'disable');
-  module_invoke_all('entity_update', $server, 'search_api_server');
-
-  return $ret;
+  $ret = $server->enable(FALSE);
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Deletes a search server and disables all associated indexes.
  *
  * @param $id
- *   The ID of the server to delete.
+ *   The ID or machine name of the server to delete.
  *
  * @return
  *   1 on success, 0 or FALSE on failure.
  */
 function search_api_server_delete($id) {
   $server = search_api_server_load($id, TRUE);
-  // Call hook method before doing anything.
-  $server->preDelete();
-
-  $id = $server->id;
-  $indexes = search_api_list_indexes(array('server' => $id));
-  foreach ($indexes as $index) {
-    search_api_index_disable($index->id);
-    search_api_index_reindex($index->id);
-  }
-  db_update('search_api_index')
-    ->fields(array('server' => NULL))
-    ->condition('server', $id)
-    ->execute();
-  $ret = db_delete('search_api_server')
-    ->condition('id', $id)
-    ->execute();
-  if (!$ret) {
-    return $ret;
-  }
-
-  $tasks = variable_get('search_api_tasks', array());
-  unset($tasks[$server->id]);
-  variable_set('search_api_tasks', $tasks);
-
-  module_invoke_all('search_api_server_delete', $server);
-  module_invoke_all('entity_delete', $server, 'search_api_server');
-
-  return $ret;
+  return $server->delete();
 }
 
 /**
  * Returns a list of search indexes.
  *
+ * @deprecated
+ *   This function doesn't take exported entities in code into account. Use
+ *   entity_load() instead and sort manually, if you must.
+ *
  * @param $options
  *   An associative array of conditions on the returned indexes.
  *   - enabled: When set to TRUE, only enabled indexes will be returned.
@@ -1272,7 +1127,7 @@
   $indexes = array();
   foreach ($results as $row) {
     $row->options = unserialize($row->options);
-    $indexes[$row->id] = $row;
+    $indexes[$row->machine_name] = $row;
   }
 
   module_invoke_all('search_api_index_load', $indexes);
@@ -1294,14 +1149,14 @@
  */
 function search_api_index_load($id, $reset = FALSE) {
   $ret = entity_load('search_api_index', array($id), array(), $reset);
-  return $ret ? array_shift($ret) : NULL;
+  return $ret ? reset($ret) : FALSE;
 }
 
 /**
  * Determines a search index' indexing status.
  *
  * @param $index
- *   Either an index object, or the index' id.
+ *   Either an index object, or the index' machine name.
  *
  * @return array
  *   An associative array containing two keys (in this order):
@@ -1309,7 +1164,7 @@
  *   - total: The total number of items that have to be indexed for this index.
  */
 function search_api_index_status($index) {
-  $id = is_object($index) ? $index->id : $index;
+  $id = is_object($index) ? $index->machine_name : $index;
   $indexed = db_select('search_api_item', 'i')
     ->condition('index_id', $id)
     ->condition('changed', 0)
@@ -1329,7 +1184,8 @@
  */
 function search_api_index_url(SearchApiIndex $index) {
   return array(
-    'path' => 'admin/config/search/search_api/index/' . $index->id,
+    'path' => 'admin/config/search/search_api/index/' . $index->machine_name,
+    'options' => array(),
   );
 }
 
@@ -1354,100 +1210,10 @@
  *   The newly inserted index' id, or FALSE on error.
  */
 function search_api_index_insert(array $values) {
-  foreach (array('name', 'machine_name', 'entity_type', 'options') as $var) {
-    if (empty($values[$var])) {
-      throw new SearchApiException(t('Field !field has to be set for new index!', array('!field' => $var)));
-      return FALSE;
-    }
-  }
-  if (empty($values['description'])) {
-    $values['description'] = NULL;
-  }
-  if ($values['server'] === '') {
-    $values['server'] = NULL;
-  }
-
-  if (isset($values['server'])) {
-    $server = search_api_server_load($values['server']);
-    if (!$server) {
-      throw new SearchApiException(t('Unknown server specified for new index (id !id)!', array('!id' => $values['server'])));
-    }
-    if (!$server->enabled) {
-      // Indexes on a disabled server cannot be enabled.
-      $values['enabled'] = 0;
-    }
-    else {
-      $values['enabled'] = (isset($values['enabled']) && !$values['enabled']) ? 0 : 1;
-    }
-  }
-  else {
-    // Indexes with no server cannot be enabled.
-    $values['enabled'] = 0;
-  }
-
-  $fields = array(
-    'name' => $values['name'],
-    'machine_name' => $values['machine_name'],
-    'description' => $values['description'],
-    'enabled' => $values['enabled'],
-    'server' => $values['server'],
-    'entity_type' => $values['entity_type'],
-    'options' => serialize($values['options']),
-  );
-  if (!empty($values['id'])) {
-    $fields['id'] = $values['id'];
-  }
-
-  $id = db_insert('search_api_index')->fields($fields)->execute();
-
-  // Remember items to index
-  $entity_info = entity_get_info($values['entity_type']);
-  if (!empty($entity_info['base table'])) {
-    // Use a subselect, which will probably be much faster than entity_load().
-    // @todo Can we assume this always works when 'base table' is set?
-    $id_field = $entity_info['entity keys']['id'];
-    $table = $entity_info['base table'];
-    $query = db_select($table, 't');
-    $query->addField('t', $id_field, 'item_id');
-    $query->addExpression(':index_id', 'index_id', array(':index_id' => $id));
-    $query->addExpression('1', 'changed');
-
-    db_insert('search_api_item')->from($query)->execute();
-  }
-  else {
-    // We have to use the slow entity_load().
-    $entities = entity_load($values['entity_type'], FALSE);
-    $query = db_insert('search_api_item')->fields('item_id', 'index_id', 'changed');
-    foreach ($entities as $item_id => $entity) {
-      $query->values(array(
-        'item_id' => $item_id,
-        'index_id' => $id,
-        'changed' => 1,
-      ));
-    }
-    $query->execute();
-  }
-
-  // Call hooks
-  if (!isset($fields['id'])) {
-    $fields['id'] = $id;
-  }
-  $index = new SearchApiIndex($fields);
-  if ($index->server) {
-    if ($server->enabled) {
-      $server->addIndex($index);
-    }
-    else {
-      $tasks = variable_get('search_api_tasks', array());
-      // When we add or remove an index, we can ignore all other tasks.
-      $tasks[$server->id][$id] = array('add');
-      variable_set('search_api_tasks', $tasks);
-    }
-  }
-  module_invoke_all('search_api_index_insert', $index);
-  module_invoke_all('entity_insert', $index, 'search_api_index');
-
-  return $id;
+  $index = new SearchApiIndex($values);
+  $index->is_new = TRUE;
+  $index->save();
+  return $index->id;
 }
 
 /**
@@ -1463,241 +1229,92 @@
  *   FALSE on failure.
  */
 function search_api_index_edit($id, array $fields) {
-  if (isset($fields['server']) && $fields['server'] === '') {
-    $fields['server'] = NULL;
-  }
-  $index = search_api_index_load($id, TRUE);
-  $changeable = array('name' => 1, 'description' => 1, 'server' => 1, 'options' => 1, 'enabled' => 1);
-  foreach ($fields as $field => $value) {
-    if (!isset($changeable[$field]) || $value === $index->$field) {
-      unset($fields[$field]);
-    }
-  }
-  // If there are no new values, just return 0.
-  if (empty($fields)) {
-    return 0;
-  }
-
-  if (isset($fields['options'])) {
-    $fields['options'] = serialize($fields['options']);
-  }
-
-  if (array_key_exists('server', $fields)) {
-    // Server changed - inform old and new ones.
-    if ($index->server) {
-      $old_server = $index->server();
-      if ($old_server->enabled) {
-        $old_server->removeIndex($index);
-      }
-      else {
-        $tasks = variable_get('search_api_tasks', array());
-        // When we add or remove an index, we can ignore all other tasks.
-        $tasks[$old_server->id][$index->id] = array('remove');
-        variable_set('search_api_tasks', $tasks);
-      }
-    }
-
-    if ($fields['server']) {
-      $new_server = search_api_server_load($fields['server']);
-      if (!$new_server->enabled) {
-        if ($index->enabled) {
-          $fields['enabled'] = 0;
-        }
-        $tasks = variable_get('search_api_tasks', array());
-        // When we add or remove an index, we can ignore all other tasks.
-        $tasks[$new_server->id][$index->id] = array('add');
-        variable_set('search_api_tasks', $tasks);
-        unset($new_server);
-      }
-    }
-    elseif ($index->enabled) {
-      $fields['enabled'] = 0;
-    }
-
-    // We also have to re-index all content
-    _search_api_index_reindex($index->id);
-  }
-
-  $ret = db_update('search_api_index')
-    ->fields($fields)
-    ->condition('id', $index->id)
-    ->execute();
-
-  if (!$ret) {
-    return $ret;
-  }
-
   $index = search_api_index_load($id, TRUE);
-
-  if (isset($new_server)) {
-    $new_server->addIndex($index);
-  }
-  module_invoke_all('search_api_index_update', $index, 'edit');
-  if (isset($fields['enabled'])) {
-    module_invoke_all('search_api_index_update', $index, $fields['enabled'] ? 'enable' : 'disable');
-  }
-  if (isset($fields['fields'])) {
-    module_invoke_all('search_api_index_update', $index, 'fields');
-  }
-  module_invoke_all('entity_update', $index, 'search_api_index');
-
-  return $ret;
+  $ret = $index->update($fields);
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Changes an index' indexed field settings.
  *
  * @param $id
- *   The ID of the index whose fields should be changed.
+ *   The ID or machine name of the index whose fields should be changed.
  * @param array $fields
  *   The new indexed field settings.
  *
  * @return
  *   1 if the field settings were changed, 0 if they already had the desired
- *   values.
+ *   values. FALSE on failure.
  */
 function search_api_index_edit_fields($id, array $fields) {
   $index = search_api_index_load($id, TRUE);
-  // If there are no new values, just return 0.
-  $options = $index->options + array('fields' => array());
-  if ($fields == $options['fields']) {
-    return 0;
-  }
+  $options = $index->options;
   $options['fields'] = $fields;
-
-  $ret = db_update('search_api_index')
-    ->fields(array('options' => serialize($options)))
-    ->condition('id', $id)
-    ->execute();
-
-  if (!$ret) {
-    return $ret;
-  }
-
-  $index = search_api_index_load($id, TRUE);
-
-  if ($index->server) {
-    $server = $index->server();
-    if ($server->enabled) {
-      if ($server->fieldsUpdated($index)) {
-        _search_api_index_reindex($id);
-      }
-    }
-    else {
-      $tasks = variable_get('search_api_tasks', array());
-      if (!isset($tasks[$server->id][$id])
-              // We don't need to notify the server twice of field changes.
-          || (array_search('fields', $tasks[$server->id][$id]) === FALSE
-              // Adding a server also re-examines the field settings.
-              && array_search('add', $tasks[$server->id][$id]) === FALSE)) {
-        $tasks[$server->id][$id][] = 'fields';
-        variable_set('search_api_tasks', $tasks);
-      }
-    }
-  }
-
-  module_invoke_all('search_api_index_update', $index, 'fields');
-  module_invoke_all('entity_update', $index, 'search_api_index');
-
-  return $ret;
+  $ret = $index->update(array('options' => $options));
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Enables a search index.
  *
  * @param $id
- *   The ID of the index to enable.
+ *   The ID or machine name of the index to enable.
+ *
+ * @throws SearchApiException
+ *   If the index' server isn't enabled.
  *
  * @return
  *   1 on success, 0 or FALSE on failure.
  */
 function search_api_index_enable($id) {
   $index = search_api_index_load($id, TRUE);
-  $server = $index->server();
-  if (!$server || !$server->enabled) {
-    throw new SearchApiException(t("Attempt to enable index #!id which isn't on an enabled server.", array('!id' => $index->id)));
-  }
-  $ret = db_update('search_api_index')
-    ->fields(array('enabled' => 1))
-    ->condition('id', $id)
-    ->execute();
-  if (!$ret) {
-    return $ret;
-  }
-
-  $index->enabled = 1;
-  module_invoke_all('search_api_index_update', $index, 'enable');
-  module_invoke_all('entity_update', $index, 'search_api_index');
-
-  return $ret;
+  $ret = $index->enable();
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Disables a search index.
  *
  * @param $id
- *   The ID of the index to disable.
+ *   The ID or machine name of the index to disable.
  *
  * @return
  *   1 on success, 0 or FALSE on failure.
  */
 function search_api_index_disable($id) {
   $index = search_api_index_load($id, TRUE);
-  $ret = db_update('search_api_index')
-    ->fields(array('enabled' => 0))
-    ->condition('id', $id)
-    ->execute();
-  if (!$ret) {
-    return $ret;
-  }
-
-  $index->enabled = 0;
-  module_invoke_all('search_api_index_update', $index, 'disable');
-  module_invoke_all('entity_update', $index, 'search_api_index');
-
-  return $ret;
+  $ret = $index->enable(FALSE);
+  return $ret ? 1 : $ret;
 }
 
 /**
  * Schedules a search index for re-indexing.
  *
  * @param $id
- *   The ID of the index to re-index.
+ *   The ID or machine name of the index to re-index.
  *
  * @return
  *   TRUE on success, FALSE on failure.
  */
 function search_api_index_reindex($id) {
-  $index = search_api_index_load($id, TRUE);
-  if (!$index->server) {
-    return TRUE;
-  }
-  $ret = _search_api_index_reindex($id);
-
-  if(!$ret) {
-    // If there just were no unchanged items, we don't want to report failure
-    return TRUE;
-  }
-
-  module_invoke_all('search_api_index_update', $index, 'reindex');
-
-  return TRUE;
+  $index = search_api_index_load($id);
+  return $index->reindex();
 }
 
 /**
  * Helper method for marking all items on an index as needing re-indexing.
  *
- * @param $id
- *   The ID of the index to re-index.
+ * @param $machine_name
+ *   The machine name of the index to re-index.
  *
  * @return
  *   The number of items affected.
  */
-function _search_api_index_reindex($id) {
+function _search_api_index_reindex($machine_name) {
   return db_update('search_api_item')
     ->fields(array('changed' => REQUEST_TIME))
     ->condition('changed', 0)
-    ->condition('index_id', $id)
+    ->condition('index_id', $machine_name)
     ->execute();
 }
 
@@ -1705,80 +1322,26 @@
  * Clears a search index and schedules all of its items for re-indexing.
  *
  * @param $id
- *   The ID of the index to clear.
+ *   The ID or machine name of the index to clear.
  *
  * @return
  *   TRUE on success, FALSE on failure.
  */
 function search_api_index_clear($id) {
-  $index = search_api_index_load($id, TRUE);
-  if (!$index->server) {
-    return TRUE;
-  }
-  $server = $index->server();
-  if ($server->enabled) {
-    $server->deleteItems('all', $index);
-  }
-  else {
-    $tasks = variable_get('search_api_tasks', array());
-    // If the index was cleared or newly added since the server was last enabled, we don't need to do anything.
-    if (!isset($tasks[$server->id][$id])
-        || (array_search('add', $tasks[$server->id][$id]) === FALSE
-            && array_search('clear', $tasks[$server->id][$id]) === FALSE)) {
-      $tasks[$server->id][$id][] = 'clear';
-      variable_set('search_api_tasks', $tasks);
-    }
-  }
-
-  $ret = _search_api_index_reindex($id);
-
-  if(!$ret) {
-    // If there just were no unchanged items, we don't want to report failure.
-    return TRUE;
-  }
-
-  module_invoke_all('search_api_index_update', $index, 'clear');
-
-  return TRUE;
+  $index = search_api_index_load($id);
+  return $index->clear();
 }
 
 /**
  * Deletes a search index.
  *
  * @param $id
- *   The ID of the index to delete.
+ *   The ID or machine name of the index to delete.
  *
  * @return
  *   TRUE on success, FALSE on failure.
  */
 function search_api_index_delete($id) {
-  $index = search_api_index_load($id, TRUE);
-  if ($index->server) {
-    $server = $index->server();
-    if ($server->enabled) {
-      $server->removeIndex($index);
-    }
-    else {
-      $tasks = variable_get('search_api_tasks', array());
-      $tasks[$server->id][$index->id] = array('remove');
-      variable_set('search_api_tasks', $tasks);
-    }
-  }
-
-  $ret = db_delete('search_api_index')
-    ->condition('id', $index->id)
-    ->execute();
-
-  if (!$ret) {
-    return FALSE;
-  }
-
-  db_delete('search_api_item')
-    ->condition('index_id', $index->id)
-    ->execute();
-
-  module_invoke_all('search_api_index_delete', $index);
-  module_invoke_all('entity_delete', $index, 'search_api_index');
-
-  return TRUE;
+  $index = search_api_index_load($id);
+  return $index->delete();
 }
Index: search_api.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/Attic/search_api.api.php,v
retrieving revision 1.1.2.17
diff -u -r1.1.2.17 search_api.api.php
--- search_api.api.php	15 Oct 2010 12:30:39 -0000	1.1.2.17
+++ search_api.api.php	26 Oct 2010 21:09:15 -0000
@@ -139,7 +139,7 @@
  */
 function hook_search_api_query_alter(SearchApiQueryInterface $query) {
   $info = entity_get_info($index->entity_type);
-  $query->addCondition($info['entity keys']['id'], 0, '!=');
+  $query->condition($info['entity keys']['id'], 0, '!=');
 }
 
 /**
@@ -152,7 +152,7 @@
   foreach ($servers as $server) {
     db_insert('example_search_server_access')
       ->fields(array(
-        'server' => $server->id,
+        'server' => $server->machine_name,
         'access_time' => REQUEST_TIME,
       ))
       ->execute();
@@ -168,7 +168,7 @@
 function hook_search_api_server_insert(SearchApiServer $server) {
   db_insert('example_search_server')
     ->fields(array(
-      'server' => $server->id,
+      'server' => $server->machine_name,
       'insert_time' => REQUEST_TIME,
     ))
     ->execute();
@@ -186,7 +186,7 @@
 function hook_search_api_server_update(SearchApiServer $server, $op = 'edit') {
   db_insert('example_search_server_update')
     ->fields(array(
-      'server' => $server->id,
+      'server' => $server->machine_name,
       'update_time' => REQUEST_TIME,
     ))
     ->execute();
@@ -201,12 +201,12 @@
 function hook_search_api_server_delete(SearchApiServer $server) {
   db_insert('example_search_server_update')
     ->fields(array(
-      'server' => $server->id,
+      'server' => $server->machine_name,
       'update_time' => REQUEST_TIME,
     ))
     ->execute();
   db_delete('example_search_server')
-    ->condition('server', $server->id)
+    ->condition('server', $server->machine_name)
     ->execute();
 }
 
@@ -220,7 +220,7 @@
   foreach ($indexes as $index) {
     db_insert('example_search_index_access')
       ->fields(array(
-        'index' => $index->id,
+        'index' => $index->machine_name,
         'access_time' => REQUEST_TIME,
       ))
       ->execute();
@@ -236,7 +236,7 @@
 function hook_search_api_index_insert(SearchApiIndex $index) {
   db_insert('example_search_index')
     ->fields(array(
-      'index' => $index->id,
+      'index' => $index->machine_name,
       'insert_time' => REQUEST_TIME,
     ))
     ->execute();
@@ -257,7 +257,7 @@
 function hook_search_api_index_update(SearchApiIndex $index, $op = 'edit') {
   db_insert('example_search_index_update')
     ->fields(array(
-      'index' => $index->id,
+      'index' => $index->machine_name,
       'update_time' => REQUEST_TIME,
     ))
     ->execute();
@@ -272,12 +272,12 @@
 function hook_search_api_index_delete(SearchApiIndex $index) {
   db_insert('example_search_index_update')
     ->fields(array(
-      'index' => $index->id,
+      'index' => $index->machine_name,
       'update_time' => REQUEST_TIME,
     ))
     ->execute();
   db_delete('example_search_index')
-    ->condition('index', $index->id)
+    ->condition('index', $index->machine_name)
     ->execute();
 }
 
@@ -317,7 +317,7 @@
   }
 
   return array(
-    'example-id-plus-rand' => array(
+    'example_id_plus_rand' => array(
       'label' => t('Useless field'),
       'description' => t('A really, really useless field, consisting of the entity ID and a random number.'),
       'type' => 'text',
Index: search_api.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/Attic/search_api.test,v
retrieving revision 1.1.2.9
diff -u -r1.1.2.9 search_api.test
--- search_api.test	19 Oct 2010 14:14:09 -0000	1.1.2.9
+++ search_api.test	26 Oct 2010 21:09:17 -0000
@@ -113,8 +113,10 @@
     $this->assertText(t('!name field is required.', array('!name' => t('Index name'))));
     $this->assertText(t('!name field is required.', array('!name' => t('Entity type'))));
 
+    $this->index_id = $id = 'test_index';
     $values = array(
       'name' => 'Search API test index',
+      'machine_name' => $id,
       'entity_type' => 'search_api_test',
       'enabled' => 1,
       'description' => 'An index used for testing.',
@@ -124,9 +126,8 @@
     $this->drupalPost(NULL, $values, t('Create index'));
 
     $this->assertText(t('The index was successfully created. Please set up its workflow now.'), t('The index was successfully created.'));
-    $found = preg_match('#admin/config/search/search_api/index/(\d+)#', $this->getUrl(), $m);
+    $found = strpos($this->getUrl(), 'admin/config/search/search_api/index/' . $id) !== FALSE;
     $this->assertTrue($found, t('Correct redirect.'));
-    $this->index_id = $id = $m[1];
     $index = search_api_index_load($id, TRUE);
     $this->assertEqual($index->name, $values['name'], t('Name correctly inserted.'));
     $this->assertEqual($index->entity_type, $values['entity_type'], t('Index entity type correctly inserted.'));
@@ -218,8 +219,10 @@
     $this->assertText(t('!name field is required.', array('!name' => t('Server name'))));
     $this->assertText(t('!name field is required.', array('!name' => t('Service class'))));
 
+    $this->server_id = $id = 'test_server';
     $values = array(
       'name' => 'Search API test server',
+      'machine_name' => $id,
       'enabled' => 1,
       'description' => 'A server used for testing.',
       'class' => 'search_api_test_service',
@@ -232,9 +235,8 @@
     $this->drupalPost(NULL, $values2, t('Create server'));
 
     $this->assertText(t('The server was successfully created.'));
-    $found = preg_match('#admin/config/search/search_api/server/(\d+)#', $this->getUrl(), $m);
+    $found = strpos($this->getUrl(), 'admin/config/search/search_api/server/' . $id) !== FALSE;
     $this->assertTrue($found, t('Correct redirect.'));
-    $this->server_id = $id = $m[1];
     $server = search_api_server_load($id, TRUE);
     $this->assertEqual($server->name, $values['name'], t('Name correctly inserted.'));
     $this->assertTrue($server->enabled, t('Status correctly inserted.'));
Index: search_api.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/search_api.install,v
retrieving revision 1.1.2.13
diff -u -r1.1.2.13 search_api.install
--- search_api.install	15 Oct 2010 13:08:47 -0000	1.1.2.13
+++ search_api.install	26 Oct 2010 21:09:15 -0000
@@ -92,9 +92,9 @@
         'not null' => FALSE,
       ),
       'server' => array(
-        'description' => 'The {search_api_server}.id with which data should be indexed.',
-        'type' => 'int',
-        'unsigned' => TRUE,
+        'description' => 'The {search_api_server}.machine_name with which data should be indexed.',
+        'type' => 'varchar',
+        'length' => 50,
         'not null' => FALSE,
       ),
       'entity_type' => array(
@@ -138,9 +138,9 @@
         'not null' => TRUE,
       ),
       'index_id' => array(
-        'description' => 'The {search_api_index}.id this item belongs to.',
-        'type' => 'int',
-        'unsigned' => TRUE,
+        'description' => 'The {search_api_index}.machine_name this item belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
         'not null' => TRUE,
       ),
       'changed' => array(
@@ -169,7 +169,7 @@
   $name = t('Default node index');
   $values = array(
     'name' => $name,
-    'machine_name' => preg_replace('/\W+/', '_', $name),
+    'machine_name' => drupal_strtolower(preg_replace('/[^a-z0-9]+/i', '_', $name)),
     'description' => t('An automatically created search index for indexing node data. Might be configured to specific needs.'),
     'server' => NULL,
     'entity_type' => 'node',
@@ -427,13 +427,33 @@
  * Implements hook_enable().
  */
 function search_api_enable() {
-  // Mark all old items as "dirty", since we can't know whether they are.
-  db_update('search_api_item')
-    ->fields(array('changed' => REQUEST_TIME))
-    ->condition('changed', 0)
+  // Mark all items as "dirty", since we can't know whether they are.
+  db_delete('search_api_item')
     ->execute();
-  // Add all new items.
-  // @todo
+  $types = array();
+  foreach (entity_load('search_api_index', FALSE) as $index) {
+    $types[$index->entity_type][] = $index->machine_name;
+  }
+  if (!$types) {
+    return;
+  }
+  $insert = db_insert('search_api_item')
+    ->fields(array('item_id', 'index_id', 'changed'));
+  foreach ($types as $type => $indexes) {
+    $info = entity_get_info($type);
+    $id_key = $info['entity keys']['id'];
+    foreach (entity_load($type, FALSE) as $entity) {
+      $id = $entity->$id_key;
+      foreach ($indexes as $index_id) {
+        $insert->values(array(
+          'item_id' => $id,
+          'index_id' => $index_id,
+          'changed' => 1,
+        ));
+      }
+    }
+  }
+  $insert->execute();
 }
 
 /**
@@ -521,3 +541,64 @@
     throw new DrupalUpdateException(t('An exception occurred during the update: !msg.', array('!msg' => $e->getMessage())));
   }
 }
+
+/**
+ * Implements hook_update_N().
+ *
+ * Update replacing IDs with machine names for foreign keys.
+ * {search_api_index}.server and {search_api_item}.index_id are altered.
+ */
+function search_api_update_7102() {
+  // Update of search_api_index:
+  $indexes = array();
+  $select = db_select('search_api_index', 'i')->fields('i');
+  foreach ($select->execute() as $index) {
+    $indexes[$index->id] = $index;
+  }
+  $servers = db_select('search_api_server', 's')->fields('s')->execute()->fetchAllAssoc();
+
+  db_drop_index('search_api_index', 'server');
+  db_drop_field('search_api_index', 'server');
+  $spec = array(
+    'description' => 'The {search_api_server}.machine_name with which data should be indexed.',
+    'type' => 'varchar',
+    'length' => 50,
+    'not null' => FALSE,
+  );
+  db_add_field('search_api_index', 'server', $spec);
+
+  foreach ($indexes as $index) {
+    db_update('search_api_index')
+      ->fields(array('server' => $servers[$index->server]['machine_name']))
+      ->condition('id', $index->id)
+      ->execute();
+  }
+  db_add_index('search_api_index', 'server', array('server'));
+
+  // Update of search_api_item:
+  db_drop_index('search_api_item', 'indexing');
+  db_drop_primary_key('search_api_item');
+  $spec = array(
+    'description' => 'The {search_api_index}.machine_name this item belongs to.',
+    'type' => 'varchar',
+    'length' => 50,
+    'not null' => TRUE,
+  );
+  $keys_new = array(
+    'indexes' => array(
+      'indexing' => array('index_id', 'changed'),
+    ),
+    'primary key' => array('item_id', 'index_id'),
+  );
+  db_change_field('search_api_item', 'index_id', 'index_id', $spec, $keys_new);
+
+  foreach ($indexes as $index) {
+    // We explicitly forbid numeric machine names, therefore we don't have to worry about conflicts here.
+    db_update('search_api_item')
+      ->fields(array(
+        'index_id' => $index->machine_name,
+      ))
+      ->condition('index_id', $index->id)
+      ->execute();
+  }
+}
Index: includes/entity.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/includes/Attic/entity.inc,v
retrieving revision 1.1.2.36
diff -u -r1.1.2.36 entity.inc
--- includes/entity.inc	19 Oct 2010 08:02:19 -0000	1.1.2.36
+++ includes/entity.inc	26 Oct 2010 21:09:20 -0000
@@ -70,6 +70,191 @@
   }
 
   /**
+   * Saves this server to the database, either creating a new record or updating
+   * an existing one.
+   *
+   * @param $op
+   *   If the server is not newly inserted, this will be passed to
+   *   hook_search_api_server_update().
+   *
+   * @return
+   *   Failure to save the server will return FALSE. Otherwise, SAVED_NEW or
+   *   SAVED_UPDATED is returned depending on the operation performed. $this->id
+   *   will be set if a new server was inserted.
+   */
+  public function save($op = 'edit') {
+    foreach (array('name', 'machine_name', 'class', 'options') as $var) {
+      if (empty($this->$var)) {
+        throw new SearchApiException(t('Property !field has to be set for a server!', array('!field' => $var)));
+      }
+    }
+    if (empty($this->description)) {
+      $this->description = NULL;
+    }
+
+    $ret = parent::save();
+    if ($ret == SAVED_NEW) {
+      module_invoke_all('search_api_server_insert', $this);
+      $this->postCreate();
+    }
+    elseif ($ret == SAVED_UPDATED) {
+      module_invoke_all('search_api_server_update', $this, $op);
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Wrapper for save() to correctly call postUpdate().
+   *
+   * NOTE: You shouldn't change any properties of this object before calling
+   * this method, as this might lead to the fields not being saved correctly.
+   *
+   * @param array $fields
+   *   The new field values. $this->enabled cannot be set this way, use
+   *   $this->enable() instead.
+   *
+   * @return
+   *   SAVE_UPDATED on success, FALSE on failure, 0 if the fields already had
+   *   the specified values.
+   */
+  public function update(array $fields) {
+    $changeable = array('name' => 1, 'description' => 1, 'options' => 1);
+    foreach ($fields as $field => $value) {
+      if (!isset($changeable[$field]) || $value === $this->$field) {
+        unset($fields[$field]);
+      }
+    }
+    // If there are no new values, just return 0.
+    if (empty($fields)) {
+      return 0;
+    }
+    $reindex = $this->postUpdate($fields);
+    foreach ($fields as $field => $value) {
+      $this->$field = $value;
+    }
+    $ret = $this->save('edit');
+    if($ret) {
+      if ($reindex) {
+        foreach (entity_load('search_api_index', FALSE, array('server' => $this->machine_name)) as $index) {
+          $index->reindex();
+        }
+      }
+    }
+    return $ret;
+  }
+
+  /**
+   * Enables or disables this server.
+   *
+   * @param $enabled
+   *   The new enabled status of the server.
+   *
+   * @return
+   *   SAVE_UPDATED on success, FALSE on failure, 0 if the field already had
+   *   the specified value.
+   */
+  public function enable($enabled = TRUE) {
+    if ($this->enabled == $enabled) {
+      return 0;
+    }
+    $this->enabled = $enabled;
+    $ret = $this->save($enabled ? 'enable' : 'disable');
+    if (!$ret) {
+      return $ret;
+    }
+
+    if ($enabled) {
+      $this->postEnable();
+      // Were there any changes in the server's indexes while it was disabled?
+      $tasks = variable_get('search_api_tasks', array());
+      if (isset($tasks[$this->machine_name])) {
+        foreach ($tasks[$this->machine_name] as $index_id => $index_tasks) {
+          $index = search_api_index_load($index_id);
+          foreach ($index_tasks as $task) {
+            switch ($task) {
+              case 'add':
+                $this->addIndex($index);
+                break;
+              case 'clear':
+                $this->deleteItems('all', $index);
+                break;
+              case 'fields':
+                if ($this->fieldsUpdated($index)) {
+                  _search_api_index_reindex($index->machine_name);
+                }
+                break;
+              case 'remove':
+                $this->removeIndex($index ? $index : $index_id);
+                break;
+              default:
+                if (substr($task, 0, 7) == 'delete-') {
+                  $id = substr($task, 7);
+                  $this->deleteItems(array($id), $index);
+                }
+                else {
+                  watchdog('search_api', t('Unknown task "!task" for server "!name".', array('!task' => $task, '!name' => $this->machine_name)), NULL, 'warning');
+                }
+            }
+          }
+        }
+        unset($tasks[$this->machine_name]);
+        variable_set('search_api_tasks', $tasks);
+      }
+    }
+    else {
+      $this->postDisable();
+      foreach (entity_load('search_api_index', FALSE, array('server' => $this->machine_name)) as $index) {
+        $index->enable(FALSE);
+      }
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Deletes this server.
+   *
+   * @return
+   *   1 on success, 0 or FALSE on failure.
+   */
+  public function delete() {
+    // Call hook method before doing anything else.
+    $this->preDelete();
+
+    foreach (entity_load('search_api_index', FALSE, array('server' => $this->machine_name)) as $index) {
+      $index->enable(FALSE);
+      $index->update(array('server' => NULL));
+      $index->reindex();
+    }
+
+    parent::delete();
+    module_invoke_all('search_api_server_delete', $this);
+
+    $tasks = variable_get('search_api_tasks', array());
+    unset($tasks[$this->machine_name]);
+    variable_set('search_api_tasks', $tasks);
+
+    return 1;
+  }
+
+  /**
+   * Magic method for determining which fields should be serialized.
+   *
+   * Serialize all properties except the proxy object.
+   *
+   * @return array
+   *   An array of properties to be serialized.
+   */
+  public function __sleep() {
+    $ret = parent::__sleep();
+    if (($pos = array_search('proxy', $ret)) !== FALSE) {
+      unset($ret[$pos]);
+    }
+    return $ret;
+  }
+
+  /**
    * Helper method for ensuring the proxy object is set up.
    */
   protected function ensureProxy() {
@@ -83,15 +268,16 @@
         $this->proxy = new $class['class']($this);
       }
       if (!($this->proxy instanceof SearchApiServiceInterface)) {
-        throw new SearchApiException(t('Search server with id !id specifies illegal service class !class.', array('!id' => $this->id, '!class' => $this->class)));
+        throw new SearchApiException(t('Search server with machine name !name specifies illegal service class !class.', array('!name' => $this->machine_name, '!class' => $this->class)));
       }
     }
   }
 
-  // If the service class defines additional methods, not specified in the
-  // SearchApiServiceInterface interface, then they are called via this magic
-  // method.
-
+  /**
+   * If the service class defines additional methods, not specified in the
+   * SearchApiServiceInterface interface, then they are called via this magic
+   * method.
+   */
   public function __call($name, $arguments = array()) {
     $this->ensureProxy();
     return call_user_func_array(array($this->proxy, $name), $arguments);
@@ -202,7 +388,7 @@
   /**
    * @var array
    */
-  protected $processors = array();
+  protected $processors = NULL;
 
   /**
    * @var array
@@ -212,7 +398,7 @@
   /**
    * @var array
    */
-  protected $fulltext_fields = NULL;
+  protected $fulltext_fields = array();
 
   // Database values that will be set when object is loaded
 
@@ -237,7 +423,7 @@
   public $description;
 
   /**
-   * @var integer
+   * @var string
    */
   public $server;
 
@@ -264,6 +450,291 @@
   }
 
   /**
+   * Saves this index to the database, either creating a new record or updating
+   * an existing one.
+   *
+   * @param $op
+   *   If the index is not newly inserted, this will be passed to
+   *   hook_search_api_index_update().
+   *
+   * @return
+   *   Failure to save the index will return FALSE. Otherwise, SAVED_NEW or
+   *   SAVED_UPDATED is returned depending on the operation performed. $this->id
+   *   will be set if a new index was inserted.
+   */
+  public function save($op = 'edit') {
+    foreach (array('name', 'machine_name', 'entity_type', 'options') as $var) {
+      if (empty($this->$var)) {
+        throw new SearchApiException(t('Property !field has to be set for an index!', array('!field' => $var)));
+      }
+    }
+    if (empty($this->description)) {
+      $this->description = NULL;
+    }
+    if (empty($this->server)) {
+      $this->server = NULL;
+      $this->enabled = FALSE;
+    }
+    // This will also throw an exception if the server doesn't exist – which is good.
+    elseif (!$this->server()->enabled) {
+      $this->enabled = FALSE;
+    }
+
+    $ret = parent::save();
+    if ($ret == SAVED_NEW) {
+      module_invoke_all('search_api_index_insert', $this);
+
+      // Remember items to index
+      $entities = entity_load($this->entity_type, FALSE);
+      $query = db_insert('search_api_item')->fields(array('item_id', 'index_id', 'changed'));
+      foreach ($entities as $item_id => $entity) {
+        $query->values(array(
+          'item_id' => $item_id,
+          'index_id' => $this->machine_name,
+          'changed' => 1,
+        ));
+      }
+      $query->execute();
+
+      $server = $this->server();
+      if ($server) {
+        if ($server->enabled) {
+          $server->addIndex($this);
+        }
+        else {
+          $tasks = variable_get('search_api_tasks', array());
+          // When we add or remove an index, we can ignore all other tasks.
+          $tasks[$server->machine_name][$this->machine_name] = array('add');
+          variable_set('search_api_tasks', $tasks);
+        }
+      }
+    }
+    elseif ($ret == SAVED_UPDATED) {
+      module_invoke_all('search_api_index_update', $this, $op);
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Wrapper for save() to correctly call hooks.
+   *
+   * NOTE: You shouldn't change any properties of this object before calling
+   * this method, as this might lead to the fields not being saved correctly.
+   *
+   * @param array $fields
+   *   The new field values. $this->enabled cannot be set this way, use
+   *   $this->enable() instead.
+   *
+   * @return
+   *   SAVE_UPDATED on success, FALSE on failure, 0 if the fields already had
+   *   the specified values.
+   */
+  public function update(array $fields) {
+    $changeable = array('name' => 1, 'description' => 1, 'server' => 1, 'options' => 1);
+    foreach ($fields as $field => $value) {
+      if (!isset($changeable[$field]) || $value === $this->$field) {
+        unset($fields[$field]);
+      }
+    }
+    // If there are no new values, just return 0.
+    if (empty($fields)) {
+      return 0;
+    }
+
+    if (array_key_exists('server', $fields)) {
+      // Server changed - inform old and new ones.
+      if ($this->server) {
+        $old_server = $this->server();
+        if ($old_server->enabled) {
+          $old_server->removeIndex($this);
+        }
+        else {
+          $tasks = variable_get('search_api_tasks', array());
+          // When we add or remove an index, we can ignore all other tasks.
+          $tasks[$old_server->machine_name][$this->machine_name] = array('remove');
+          variable_set('search_api_tasks', $tasks);
+        }
+      }
+
+      if ($fields['server']) {
+        $new_server = search_api_server_load($fields['server']);
+        // If the server is disabled, we save the task. Otherwise we call addIndex() after saving the entry.
+        if (!$new_server->enabled) {
+          if ($this->enabled) {
+            $fields['enabled'] = 0;
+          }
+          $tasks = variable_get('search_api_tasks', array());
+          // When we add or remove an index, we can ignore all other tasks.
+          $tasks[$new_server->machine_name][$this->machine_name] = array('add');
+          variable_set('search_api_tasks', $tasks);
+          unset($new_server);
+        }
+      }
+      elseif ($this->enabled) {
+        $fields['enabled'] = 0;
+      }
+
+      // We also have to re-index all content
+      unset($this->server_object);
+      _search_api_index_reindex($this->machine_name);
+    }
+
+    // Determine whether the indexed fields were changed for later calling appropriate hooks.
+    $fields_changed = isset($fields['options']['fields'])
+        && isset($this->options['fields'])
+        && $fields['options']['fields'] != $this->options['fields'];
+    foreach ($fields as $field => $value) {
+      $this->$field = $value;
+    }
+    $ret = parent::save('edit');
+    if (!$ret) {
+      return $ret;
+    }
+
+    if (isset($new_server)) {
+      $new_server->addIndex($this);
+    }
+    if ($fields_changed) {
+      if ($this->server && $this->server()->fieldsUpdated($this)) {
+        _search_api_index_reindex($this->machine_name);
+      }
+      module_invoke_all('search_api_index_update', $this, 'fields');
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Enables or disables this index.
+   *
+   * @param $enabled
+   *   The new enabled status of the index.
+   *
+   * @throws SearchApiException
+   *   If $enabled is TRUE and the index' server isn't enabled.
+   *
+   * @return
+   *   SAVE_UPDATED on success, FALSE on failure, 0 if the field already had
+   *   the specified value.
+   */
+  public function enable($enabled = TRUE) {
+    if ($this->enabled == $enabled) {
+      return 0;
+    }
+    if ($enabled && !($this->server() && $this->server()->enabled)) {
+      throw new SearchApiException(t("Attempt to enable index !name which isn't on an enabled server.", array('!name' => $this->machine_name)));
+    }
+    $this->enabled = $enabled;
+    return $this->save($enabled ? 'enable' : 'disable');
+  }
+
+  /**
+   * Schedules this search index for re-indexing.
+   *
+   * @return
+   *   TRUE on success, FALSE on failure.
+   */
+  public function reindex() {
+    if (!$this->server) {
+      return TRUE;
+    }
+    $ret = _search_api_index_reindex($this->machine_name);
+    if($ret) {
+      module_invoke_all('search_api_index_update', $this, 'reindex');
+    }
+    return TRUE;
+  }
+
+  /**
+   * Clears this search index and schedules all of its items for re-indexing.
+   *
+   * @return
+   *   TRUE on success, FALSE on failure.
+   */
+  public function clear() {
+    if (!$this->server) {
+      return TRUE;
+    }
+
+    $server = $index->server();
+    if ($server->enabled) {
+      $server->deleteItems('all', $index);
+    }
+    else {
+      $tasks = variable_get('search_api_tasks', array());
+      // If the index was cleared or newly added since the server was last enabled, we don't need to do anything.
+      if (!isset($tasks[$server->machine_name][$id])
+          || (array_search('add', $tasks[$server->machine_name][$id]) === FALSE
+              && array_search('clear', $tasks[$server->machine_name][$id]) === FALSE)) {
+        $tasks[$server->machine_name][$id][] = 'clear';
+        variable_set('search_api_tasks', $tasks);
+      }
+    }
+
+    $ret = _search_api_index_reindex($this->machine_name);
+    if($ret) {
+      module_invoke_all('search_api_index_update', $index, 'clear');
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Deletes this search index.
+   *
+   * @return
+   *   TRUE on success, FALSE on failure.
+   */
+  public function delete() {
+    if ($this->server) {
+      $server = $this->server();
+      if ($server->enabled) {
+        $server->removeIndex($this);
+      }
+      else {
+        $tasks = variable_get('search_api_tasks', array());
+        $tasks[$server->machine_name][$this->machine_name] = array('remove');
+        variable_set('search_api_tasks', $tasks);
+      }
+    }
+
+    parent::delete();
+
+    db_delete('search_api_item')
+      ->condition('index_id', $this->machine_name)
+      ->execute();
+
+    module_invoke_all('search_api_index_delete', $this);
+
+    return TRUE;
+  }
+
+  /**
+   * Magic method for determining which fields should be serialized.
+   *
+   * Don't serialize properties that are basically only caches.
+   *
+   * @return array
+   *   An array of properties to be serialized.
+   */
+  public function __sleep() {
+    $ret = parent::__sleep();
+    $ignore = array('server_object' => 1, 'processors' => 1, 'added_properties' => 1, 'fulltext_fields' => 1);
+    foreach ($ret as $i => $prop) {
+      if (isset($ignore[$prop])) {
+        unset($ret[$i]);
+      }
+    }
+    return $ret;
+  }
+
+  /**
+   * Get the server this index lies on.
+   *
+   * @throws SearchApiException
+   *   If $this->server is set, but no server with that machine name exists.
+   *
    * @return SearchApiServer
    *   The server associated with this index, or NULL if this index currently
    *   doesn't lie on a server.
@@ -271,6 +742,10 @@
   public function server() {
     if (!isset($this->server_object)) {
       $this->server_object = $this->server ? search_api_server_load($this->server) : FALSE;
+      if ($this->server && !$this->server_object) {
+        throw new SearchApiException(t('Unknown server !server specified for index !name.',
+            array('!server' => $this->server, '!name' => $this->machine_name)));
+      }
     }
     return $this->server_object ? $this->server_object : NULL;
   }
@@ -425,10 +900,11 @@
    *   The called object.
    */
   protected function prepareProcessors() {
-    if (empty($this->options['processors']) || !empty($this->processors)) {
+    if (empty($this->options['processors']) || isset($this->processors)) {
       return $this;
     }
 
+    $this->processors = array();
     $processor_settings = $this->options['processors'];
     $infos = search_api_get_processors();
 
@@ -532,18 +1008,19 @@
    *   index.
    */
   public function getFulltextFields($only_indexed = TRUE) {
-    if ($this->fulltext_fields === NULL) {
-      $this->fulltext_fields = array();
+    $i = $only_indexed ? 1 : 0;
+    if (!isset($this->fulltext_fields[$i])) {
+      $this->fulltext_fields[$i] = array();
       if (empty($this->options['fields'])) {
         return array();
       }
       foreach ($this->options['fields'] as $key => $field) {
         if (search_api_is_text_type($field['type']) && (!$only_indexed || $field['indexed'])) {
-          $this->fulltext_fields[] = $key;
+          $this->fulltext_fields[$i][] = $key;
         }
       }
     }
-    return $this->fulltext_fields;
+    return $this->fulltext_fields[$i];
   }
 
 }
Index: includes/service.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/includes/Attic/service.inc,v
retrieving revision 1.1.2.33
diff -u -r1.1.2.33 service.inc
--- includes/service.inc	15 Oct 2010 18:57:39 -0000	1.1.2.33
+++ includes/service.inc	26 Oct 2010 21:09:20 -0000
@@ -151,8 +151,8 @@
    * If the index wasn't added to the server, the method call should be ignored.
    *
    * @param $index
-   *   Either an object representing the index to remove, or its id (if the
-   *   index was completely deleted).
+   *   Either an object representing the index to remove, or its machine name
+   *   (if the index was completely deleted).
    */
   public function removeIndex($index);
 
@@ -249,6 +249,8 @@
   protected $server;
 
   /**
+   * Direct reference to the server's $options property.
+   *
    * @var array
    */
   protected $options = array();
@@ -311,7 +313,7 @@
     if (!empty($this->options)) {
       $values += $this->options;
     }
-    $this->options = $values + $this->options;
+    $this->options = $values;
   }
 
   /**
@@ -409,11 +411,10 @@
    * Notifies this server that it is about to be deleted from the database and
    * should therefore clean up, if appropriate.
    *
-   *
    * By default, deletes all indexes from this server.
    */
   public function preDelete() {
-    $indexes = search_api_list_indexes(array('server' => $this->server->id));
+    $indexes = entity_load('search_api_index', FALSE, array('server' => $this->server->machine_name));
     foreach ($indexes as $index) {
       $this->removeIndex($index);
     }
@@ -458,8 +459,8 @@
    * By default, removes all items from that index.
    *
    * @param $index
-   *   Either an object representing the index to remove, or its id (if the
-   *   index was completely deleted).
+   *   Either an object representing the index to remove, or its machine name
+   *   (if the index was completely deleted).
    */
   public function removeIndex($index) {
     $this->deleteItems('all', $index);
Index: tests/search_api_test.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/tests/Attic/search_api_test.module,v
retrieving revision 1.1.2.7
diff -u -r1.1.2.7 search_api_test.module
--- tests/search_api_test.module	15 Oct 2010 18:57:39 -0000	1.1.2.7
+++ tests/search_api_test.module	26 Oct 2010 21:09:20 -0000
@@ -233,7 +233,7 @@
 
   public function indexItems(SearchApiIndex $index, array $items) {
     // Refuse to index items with IDs that are multiples of 7 unless all items are indexed.
-    $count = db_query('SELECT COUNT(*) FROM {search_api_item} WHERE index_id = :index_id AND changed <> 0', array(':index_id' => $index->id))->fetchField();
+    $count = db_query('SELECT COUNT(*) FROM {search_api_item} WHERE index_id = :index_id AND changed <> 0', array(':index_id' => $index->machine_name))->fetchField();
     if ($count == count($items)) {
       return $this->index($index, array_keys($items));
     }
@@ -248,17 +248,17 @@
 
   protected function index(SearchApiIndex $index, array $ids) {
     $this->options += array('indexes' => array());
-    $this->options['indexes'] += array($index->id => array());
-    $this->options['indexes'][$index->id] += drupal_map_assoc($ids);
-    sort($this->options['indexes'][$index->id]);
-    search_api_server_edit($this->id, array('options' => $this->options));
+    $this->options['indexes'] += array($index->machine_name => array());
+    $this->options['indexes'][$index->machine_name] += drupal_map_assoc($ids);
+    sort($this->options['indexes'][$index->machine_name]);
+    $this->server->save();
     return $ids;
   }
 
   public function deleteItems($ids = 'all', SearchApiIndex $index = NULL) {
     if ($ids == 'all') {
       if ($index) {
-        $this->options['indexes'][$index->id] = array();
+        $this->options['indexes'][$index->machine_name] = array();
       }
       else {
         $this->options['indexes'] = array();
@@ -266,16 +266,16 @@
     }
     else {
       foreach ($ids as $id) {
-        unset($this->options['indexes'][$index->id][$id]);
+        unset($this->options['indexes'][$index->machine_name][$id]);
       }
     }
-    search_api_server_edit($this->id, array('options' => $this->options));
+    $this->server->save();
   }
 
   public function search(SearchApiQueryInterface $query) {
     $options = $query->getOptions();
     $ret = array();
-    $index_id = $query->getIndex()->id;
+    $index_id = $query->getIndex()->machine_name;
     if (empty($this->options['indexes'][$index_id])) {
       return array(
         'result count' => 0,
