From 9dae53c7d47fa25d355e717d7ca5c209865bd79f Mon Sep 17 00:00:00 2001
From: Katherine Bailey <katherine@katbailey.net>
Date: Mon, 26 Sep 2011 20:55:07 -0700
Subject: [PATCH] Removing db module so it does not conflict with new separate db module

---
 contrib/search_api_db/README.txt            |   29 -
 contrib/search_api_db/search_api_db.info    |    9 -
 contrib/search_api_db/search_api_db.install |   84 --
 contrib/search_api_db/search_api_db.module  |   16 -
 contrib/search_api_db/search_api_db.test    |  250 ------
 contrib/search_api_db/service.inc           | 1185 ---------------------------
 6 files changed, 0 insertions(+), 1573 deletions(-)
 delete mode 100644 contrib/search_api_db/README.txt
 delete mode 100644 contrib/search_api_db/search_api_db.info
 delete mode 100644 contrib/search_api_db/search_api_db.install
 delete mode 100644 contrib/search_api_db/search_api_db.module
 delete mode 100644 contrib/search_api_db/search_api_db.test
 delete mode 100644 contrib/search_api_db/service.inc

diff --git contrib/search_api_db/README.txt contrib/search_api_db/README.txt
deleted file mode 100644
index 21c7143..0000000
--- contrib/search_api_db/README.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-
-Database search
----------------
-
-This module provides a database based implementation of the Search API. The
-database and target to use for storing and accessing the indexes can be selected
-when creating a new server.
-
-All Search API datatypes are supported by using appropriate SQL datatypes for
-their respective columns (with "String"/"URI", and "Integer"/"Duration" being
-equivalent).
-
-The "direct" parse mode for queries will result in a simple splitting of the
-query string into keys. Additionally, search keys containing whitespace will be
-split, as searching for phrases is currently not supported.
-
-Due to SQL limitations, fulltext searches are always case-insensitive.
-
-Supported optional features
----------------------------
-
-Regarding third-party features, this module supports the "search_api_facets"
-feature, introduced by the module of the same name. This lets you create
-facetted searches for any index lying on a database server.
-
-If you feel some service option is missing, or have other ideas for improving
-this implementation, please file a feature request in the project's issue queue,
-at [http://drupal.org/project/issues/search_api], using the "Database search"
-component.
diff --git contrib/search_api_db/search_api_db.info contrib/search_api_db/search_api_db.info
deleted file mode 100644
index e9820b8..0000000
--- contrib/search_api_db/search_api_db.info
+++ /dev/null
@@ -1,9 +0,0 @@
-
-name = Database search
-description = "Offers an implementation of the Search API that uses database tables for indexing content."
-dependencies[] = search_api
-core = 7.x
-package = Search
-
-files[] = search_api_db.test
-files[] = service.inc
\ No newline at end of file
diff --git contrib/search_api_db/search_api_db.install contrib/search_api_db/search_api_db.install
deleted file mode 100644
index 16ffc4b..0000000
--- contrib/search_api_db/search_api_db.install
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-/**
- * Implements hook_requirements().
- */
-function search_api_db_requirements($phase) {
-  $t = get_t();
-  $ret['search_api_db']['title'] = $t('Database search');
-  $ret['search_api_db']['value'] = $t('deprecated');
-  $ret['search_api_db']['description'] = $t('This module was moved out of the core Search API project. ' .
-      'To keep using it, please download the separate module from <a href="@url">its project page</a>. ' .
-      'To get rid of this error message delete the old module under @path after downloading the new one.' .
-      'The legacy module used now will soon be removed.',
-      array('@url' => url('http://drupal.org/project/search_api_db'), '@path' => DRUPAL_ROOT . drupal_get_path('module', 'search_api_db')));
-  $ret['search_api_db']['severity'] = REQUIREMENT_ERROR;
-
-  return $ret;
-}
-
-/**
- * Implements hook_uninstall().
- */
-function search_api_db_uninstall() {
-  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_dependencies().
- */
-function search_api_db_update_dependencies() {
-  // This update should run after primary IDs have been changed to machine names in the framework.
-  $dependencies['search_api_db'][7101] = array(
-    'search_api' => 7102,
-  );
-  return $dependencies;
-}
-
-/**
- * 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();
-  }
-}
diff --git contrib/search_api_db/search_api_db.module contrib/search_api_db/search_api_db.module
deleted file mode 100644
index e6c76c0..0000000
--- contrib/search_api_db/search_api_db.module
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-/**
- * Implements hook_search_api_service_info().
- */
-function search_api_db_search_api_service_info() {
-  $services['search_api_db_service'] = array(
-    'name' => t('Database service'),
-    'description' => t('<p>Index items using multiple database tables, for simple searches.</p>' .
-        '<ul>' . '<li>All field types are supported and indexed in a special way, with URI/String and Integer/Duration being equivalent.</li>' .
-        '<li>The "direct" parse mode results in the keys being normally split around white-space, only preprocessing might differ.</li>' .
-        '<li>Currently, phrase queries are not supported. Additionally, due to MySQL limitations, all searches will be case-insensitive.</li>' . '</ul>'),
-    'class' => 'SearchApiDbService',
-  );
-  return $services;
-}
diff --git contrib/search_api_db/search_api_db.test contrib/search_api_db/search_api_db.test
deleted file mode 100644
index b8dc0f4..0000000
--- contrib/search_api_db/search_api_db.test
+++ /dev/null
@@ -1,250 +0,0 @@
-<?php
-
-/**
- * Class for testing index and search capabilities using the Database search
- * module.
- */
-class SearchApiDbTest extends DrupalWebTestCase {
-
-  protected $server_id;
-  protected $index_id;
-
-  protected function assertText($text, $message = '', $group = 'Other') {
-    return parent::assertText($text, $message ? $message : $text, $group);
-  }
-
-  protected function drupalGet($path, array $options = array(), array $headers = array()) {
-    $ret = parent::drupalGet($path, $options, $headers);
-    $this->assertResponse(200, t('HTTP code 200 returned.'));
-    return $ret;
-  }
-
-  protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) {
-    $ret = parent::drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post);
-    $this->assertResponse(200, t('HTTP code 200 returned.'));
-    return $ret;
-  }
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Test "Database search" module',
-      'description' => 'Tests indexing and searching with the "Database search" module.',
-      'group' => 'Search API',
-    );
-  }
-
-  public function setUp() {
-    parent::setUp('entity', 'search_api', 'search_api_db', 'search_api_test');
-  }
-
-  public function testFramework() {
-    $this->drupalLogin($this->drupalCreateUser(array('administer search_api')));
-    $this->insertItems();
-    $this->createServer();
-    $this->createIndex();
-    $this->searchNoResults();
-    $this->indexItems();
-    $this->searchSuccess1();
-    $this->editServer();
-    $this->searchSuccess2();
-    $this->clearIndex();
-    $this->searchNoResults();
-  }
-
-  protected function insertItems() {
-    $this->drupalGet('search_api_test/insert');
-    $count = db_query('SELECT COUNT(*) FROM {search_api_test}')->fetchField();
-    $this->insertItem(array(
-      'id' => 1,
-      'title' => 'foo bar baz',
-      'body' => 'test test',
-      'type' => 'item',
-    ));
-    $this->insertItem(array(
-      'id' => 2,
-      'title' => 'foo test',
-      'body' => 'bar test',
-      'type' => 'item',
-    ));
-    $this->insertItem(array(
-      'id' => 3,
-      'title' => 'bar',
-      'body' => 'test foobar',
-      'type' => 'item',
-    ));
-    $this->insertItem(array(
-      'id' => 4,
-      'title' => 'foo baz',
-      'body' => 'test test test',
-      'type' => 'article',
-    ));
-    $this->insertItem(array(
-      'id' => 5,
-      'title' => 'bar baz',
-      'body' => 'foo',
-      'type' => 'article',
-    ));
-    $count = db_query('SELECT COUNT(*) FROM {search_api_test}')->fetchField() - $count;
-    $this->assertEqual($count, 5, t('@count items inserted.', array('@count' => $count)));
-  }
-
-  protected function insertItem($values) {
-    $this->drupalPost(NULL, $values, t('Save'));
-  }
-
-  protected function createServer() {
-    $this->server_id = 'database_search_server';
-    $values = array(
-      'name' => 'Database search server',
-      'machine_name' => $this->server_id,
-      'enabled' => 1,
-      'description' => 'A server used for testing.',
-      'class' => 'search_api_db_service',
-    );
-    $this->drupalPost('admin/config/search/search_api/add_server', $values, t('Create server'));
-
-    $values2 = array(
-      'options[form][min_chars]' => 3,
-    );
-    $this->drupalPost(NULL, $values2, t('Create server'));
-
-    $this->assertText(t('The server was successfully created.'));
-    $found = strpos($this->getUrl(), '/admin/config/search/search_api/server/' . $this->server_id) !== FALSE;
-    $this->assertTrue($found, t('Correct redirect.'));
-  }
-
-  protected function createIndex() {
-    $this->index_id = $id = 'test_index';
-    $values = array(
-      'name' => 'Test index',
-      'machine_name' => 'test_index',
-      'item_type' => 'search_api_test',
-      'enabled' => 1,
-      'description' => 'An index used for testing.',
-      'server' => $this->server_id,
-      'options[cron_limit]' => 5,
-    );
-    $this->drupalPost('admin/config/search/search_api/add_index', $values, t('Create index'));
-
-    $this->assertText(t('The index was successfully created. Please set up its indexed fields now.'), t('The index was successfully created.'));
-    $found = strpos($this->getUrl(), 'admin/config/search/search_api/index/' . $id) !== FALSE;
-    $this->assertTrue($found, t('Correct redirect.'));
-
-    $values = array(
-      'fields[id][type]' => 'integer',
-      'fields[id][boost]' => '1.0',
-      'fields[id][indexed]' => 1,
-      'fields[title][type]' => 'text',
-      'fields[title][boost]' => '5.0',
-      'fields[title][indexed]' => 1,
-      'fields[body][type]' => 'text',
-      'fields[body][boost]' => '1.0',
-      'fields[body][indexed]' => 1,
-      'fields[type][type]' => 'string',
-      'fields[type][boost]' => '1.0',
-      'fields[type][indexed]' => 1,
-    );
-    $this->drupalPost(NULL, $values, t('Save changes'));
-    $this->assertText(t('The indexed fields were successfully changed. The index was cleared and will have to be re-indexed with the new settings.'), t('Field settings saved.'));
-
-    $this->drupalPost(NULL, array(), t('Save configuration'));
-    $this->assertText(t("The search index' workflow was successfully edited. All content was scheduled for re-indexing so the new settings can take effect."), t('Workflow successfully edited.'));
-
-    $this->drupalGet("admin/config/search/search_api/index/$id/status");
-    $this->assertText(t('The index is currently enabled.'), t('"Enabled" status displayed.'));
-    $this->assertText(t('All items still need to be indexed (@total total).', array('@total' => 5)), t('!field displayed.', array('!field' => t('Correct index status'))));
-  }
-
-  protected function searchNoResults() {
-    $this->drupalGet('search_api_test/query/' . $this->index_id . '/text');
-    $this->assertText('result count = 0', t('No search results returned without indexing.'));
-    $this->assertText('results = ()', t('No search results returned without indexing.'));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-  }
-
-  protected function indexItems() {
-    $this->drupalPost("admin/config/search/search_api/index/{$this->index_id}/status", array(), t('Index now'));
-    $this->assertText(t('Successfully indexed @count items.', array('@count' => 5)));
-    $this->assertNoText(t("Some items couldn't be indexed. Check the logs for details."), t("Index errors warning isn't displayed."));
-    $this->assertNoText(t("Couldn't index items. Check the logs for details."), t("Index error isn't displayed."));
-    $this->assertText(t('All items have been indexed (@total / @total).', array('@total' => 5)), t('!field displayed.', array('!field' => t('Correct index status'))));
-  }
-
-  protected function searchSuccess1() {
-    $base ='search_api_test/query/' . $this->index_id;
-    $this->drupalGet($base . '/test/1/2');
-    $this->assertText('result count = 4', t('Search for »!keys« returned correct result.', array('!keys' => 'test')));
-    $this->assertText('results = (2, 3)', t('Search for »!keys« returned correct result.', array('!keys' => 'test')));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '/"test foo"/0/10//search_api_relevance,DESC');
-    $this->assertText('result count = 3', t('Search for »!keys« returned correct result.', array('!keys' => '"test foo"')));
-    $this->assertText('results = (2, 4, 1)', t('Search for »!keys« returned correct result.', array('!keys' => '"test foo"')));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '/foo/0/10///type=item');
-    $this->assertText('result count = 2', t('Search for »!keys« returned correct result.', array('!keys' => 'foo')));
-    $this->assertText('results = (1, 2)', t('Search for »!keys« returned correct result.', array('!keys' => 'foo')));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '/|COMPLEX|');
-    $this->assertText('result count = 1', t('Complex search returned correct result.'));
-    $this->assertText('results = (4)', t('Complex search returned correct result.'));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-  }
-
-  protected function editServer() {
-    $values = array(
-      'options[form][min_chars]' => 4,
-    );
-    $this->drupalPost("admin/config/search/search_api/server/{$this->server_id}/edit", $values, t('Save settings'));
-
-    $this->clearIndex();
-    $this->indexItems();
-  }
-
-  protected function searchSuccess2() {
-    $base ='search_api_test/query/' . $this->index_id;
-    $this->drupalGet($base . '/test/1/2');
-    $this->assertText('result count = 4', t('Search for »!keys« returned correct result.', array('!keys' => 'test')));
-    $this->assertText('results = (2, 3)', t('Search for »!keys« returned correct result.', array('!keys' => 'test')));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '//0/10//search_api_relevance,DESC/body=test foobar');
-    $this->assertText('result count = 1', t('Search with multi-term fulltext filter returned correct result.', array('!keys' => 'test foobar')));
-    $this->assertText('results = (3)', t('Search with multi-term fulltext filter returned correct result.', array('!keys' => 'test foobar')));
-    $this->assertText('ignored = ()', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '/"test foo"/0/10//search_api_relevance,DESC');
-    $this->assertText('result count = 4', t('Search for »!keys« returned correct result.', array('!keys' => '"test foo"')));
-    $this->assertText('results = (2, 4, 1, 3)', t('Search for »!keys« returned correct result.', array('!keys' => '"test foo"')));
-    $this->assertText('ignored = (foo)', t('Short key was ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-
-    $this->drupalGet($base . '/foo/0/10///type=item');
-    $this->assertText('result count = 3', t('Search for »!keys« returned correct result.', array('!keys' => 'foo')));
-    $this->assertText('results = (1, 2, 3)', t('Search for »!keys« returned correct result.', array('!keys' => 'foo')));
-    $this->assertText('ignored = (foo)', t('Short key was ignored.'));
-    $this->assertText('warnings = ("' . t('No valid search keys were present in the query.') . '")', t('Warning displayed.'));
-
-    $this->drupalGet($base . '/|COMPLEX|');
-    $this->assertText('result count = 1', t('Complex search returned correct result.'));
-    $this->assertText('results = (3)', t('Complex search returned correct result.'));
-    $this->assertText('ignored = (baz, bar)', t('No keys were ignored.'));
-    $this->assertText('warnings = ()', t('No warnings were displayed.'));
-  }
-
-  protected function clearIndex() {
-    $this->drupalPost("admin/config/search/search_api/index/{$this->index_id}/status", array(), t('Clear index'));
-    $this->assertText(t('The index was successfully cleared.'));
-    $this->assertText(t('All items still need to be indexed (@total total).', array('@total' => 5)), t('!field displayed.', array('!field' => t('Correct index status'))));
-  }
-
-}
diff --git contrib/search_api_db/service.inc contrib/search_api_db/service.inc
deleted file mode 100644
index fe37668..0000000
--- contrib/search_api_db/service.inc
+++ /dev/null
@@ -1,1185 +0,0 @@
-<?php
-
-/**
- * Search service class using the database for storing index information.
- */
-class SearchApiDbService extends SearchApiAbstractService {
-
-  protected $previous_db;
-  protected $query_options = array();
-  protected $ignored = array();
-  protected $warnings = array();
-
-  public function configurationForm(array $form, array &$form_state) {
-    if (empty($this->options)) {
-      global $databases;
-      foreach ($databases as $key => $targets) {
-        foreach ($targets as $target => $info) {
-          $options[$key]["$key:$target"] = "$key > $target";
-        }
-      }
-      if (count($options) > 1 || count(reset($options)) > 1) {
-        $form['database'] = array(
-          '#type' => 'select',
-          '#title' => t('Database'),
-          '#description' => t('Select the database key and target to use for storing indexing information in. ' .
-              'Cannot be changed after creation.'),
-          '#options' => $options,
-          '#default_value' => 'default:default',
-          '#required' => TRUE,
-        );
-      }
-      else {
-        $form['database'] = array(
-          '#type' => 'value',
-          '#value' => "$key:$target",
-        );
-      }
-      $form['min_chars'] = array(
-        '#type' => 'select',
-        '#title' => t('Minimum word length'),
-        '#description' => t('The minimum number of characters a word must consist of to be indexed.'),
-        '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6)),
-        '#default_value' => 1,
-      );
-    }
-    else {
-      $form = array(
-        'database' => array(
-          '#type' => 'value',
-          '#title' => t('Database'), // Slight hack for the "View server" page.
-          '#value' => $this->options['database'],
-        ),
-        'database_text' => array(
-          '#type' => 'item',
-          '#title' => t('Database'),
-          '#markup' => check_plain(str_replace(':', ' > ', $this->options['database'])),
-        ),
-        'min_chars' => array(
-          '#type' => 'select',
-          '#title' => t('Minimum word length'),
-          '#description' => t('The minimum number of characters a word must consist of to be indexed.'),
-          '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6)),
-          '#default_value' => $this->options['min_chars'],
-        ),
-      );
-    }
-
-    return $form;
-  }
-
-  public function supportsFeature($feature) {
-    return $feature == 'search_api_facets';
-  }
-
-  public function postUpdate() {
-    return $this->server->options != $this->server->original->options;
-  }
-
-  public function preDelete() {
-    if (empty($this->options['indexes'])) {
-      return;
-    }
-    foreach ($this->options['indexes'] as $index) {
-      foreach ($index as $field) {
-        db_drop_table($field['table']);
-      }
-    }
-  }
-
-  public function addIndex(SearchApiIndex $index) {
-    $this->options += array('indexes' => array());
-    $indexes = &$this->options['indexes'];
-    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->machine_name] = array();
-      $this->server->save();
-      return $this;
-    }
-
-    $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($index, $field, $table);
-      $indexes[$index->machine_name][$name]['table'] = $table;
-      $indexes[$index->machine_name][$name]['type']  = $field['type'];
-      $indexes[$index->machine_name][$name]['boost'] = $field['boost'];
-    }
-
-    $this->server->save();
-  }
-
-  /**
-   * 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) {
-    // A DB prefix might further reduce the maximum length of the table name.
-    $maxlen = 63;
-    list($key, $target) = explode(':', $this->options['database'], 2);
-    if ($db_prefix = Database::getConnection($target, $key)->tablePrefix()) {
-      $maxlen -= drupal_strlen($db_prefix);
-    }
-
-    $base = $table = drupal_substr($prefix . drupal_strtolower(preg_replace('/[^a-z0-9]/i', '_', $name)), 0, $maxlen);
-    $i = 0;
-    while (db_table_exists($table)) {
-      $suffix = '_' . ++$i;
-      $table = drupal_substr($base, 0, $maxlen - drupal_strlen($suffix)) . $suffix;
-    }
-    return $table;
-  }
-
-  /**
-   * Helper method for creating the table for a field.
-   */
-  protected function createFieldTable(SearchApiIndex $index, $field, $name) {
-    $table = array(
-      'name' => $name,
-      'module' => 'search_api_db',
-      'fields' => array(
-        'item_id' => array(
-          'description' => 'The primary identifier of the item.',
-          'not null' => TRUE,
-        ),
-      ),
-    );
-    // The type of the item_id field depends on the ID field's type.
-    $id_field = $index->datasource()->getIdFieldInfo();
-    $table['fields']['item_id'] += $this->sqlType($id_field['type'] == 'text' ? 'string' : $id_field['type']);
-    if (isset($table['fields']['item_id']['length'])) {
-      // A length of 255 is overkill for IDs. 50 should be more than enough.
-      $table['fields']['item_id']['length'] = 50;
-    }
-
-    $type = search_api_extract_inner_type($field['type']);
-    if ($type == 'text') {
-      $table['fields']['word'] = array(
-        'description' => 'The text of the indexed token.',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => TRUE,
-      );
-      $table['fields']['score'] = array(
-        'description' => 'The score associated with this token.',
-        'type' => 'float',
-        'not null' => TRUE,
-      );
-      $table['primary key'] = array('item_id', 'word');
-      $table['indexes']['word'] = array(array('word', 10));
-    }
-    else {
-      $table['fields']['value'] = $this->sqlType($type);
-      $table['fields']['value'] += array('description' => "The field's value for this item.");
-      if ($type != $field['type']) {
-        // This is a list type.
-        $table['fields']['value']['not null'] = TRUE;
-        $table['primary key'] = array('item_id', 'value');
-      }
-      else {
-        $table['primary key'] = array('item_id');
-      }
-      $table['indexes']['value'] = $table['fields']['value'] == 'varchar' ? array(array('value', 10)) : array('value');
-    }
-
-    $set = $this->setDb();
-    db_create_table($name, $table);
-    if ($set) {
-      $this->resetDb();
-    }
-  }
-
-  protected function sqlType($type) {
-    $type = search_api_extract_inner_type($type);
-    switch ($type) {
-      case 'string':
-      case 'uri':
-        return array('type' => 'varchar', 'length' => 255);
-      case 'integer':
-      case 'duration':
-      case 'date': // 'datetime' sucks. This way, we just convert every input into a timestamp.
-        return array('type' => 'int');
-      case 'decimal':
-        return array('type' => 'float');
-      case 'boolean':
-        return array('type' => 'int', 'size' => 'tiny');
-
-      default:
-        throw new SearchApiException(t('Unknown field type !type. Database search module might be out of sync with Search API.', array('!type' => $type)));
-    }
-  }
-
-  public function fieldsUpdated(SearchApiIndex $index) {
-    $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]);
-      }
-    }
-
-    $reindex = FALSE;
-    $cleared = FALSE;
-    $set = $this->setDb();
-    foreach ($fields as $name => $field) {
-      if (!isset($new_fields[$name])) {
-        db_drop_table($field['table']);
-        unset($fields[$name]);
-        continue;
-      }
-      $old_type = $field['type'];
-      $new_type = $new_fields[$name]['type'];
-      $fields[$name]['type'] = $new_type;
-      $fields[$name]['boost'] = $new_fields[$name]['boost'];
-      $old_inner_type = search_api_extract_inner_type($old_type);
-      $new_inner_type = search_api_extract_inner_type($new_type);
-      if ($old_type != $new_type) {
-        if ($old_inner_type == 'text' || $new_inner_type == 'text'
-            || search_api_list_nesting_level($old_type) != search_api_list_nesting_level($new_type)) {
-          // A change in fulltext or list status necessitates completely
-          // clearing the index.
-          $reindex = TRUE;
-          if (!$cleared) {
-            $cleared = TRUE;
-            $this->deleteItems('all', $index);
-          }
-          db_drop_table($field['table']);
-          $this->createFieldTable($index, $new_fields[$name], $field['table']);
-        }
-        elseif ($this->sqlType($old_inner_type) != $this->sqlType($new_inner_type)) {
-          // There is a change in SQL type. We don't have to clear the index, since types can be converted.
-          db_change_field($field['table'], 'value', 'value', $this->sqlType($new_type) + array('description' => "The field's value for this item."));
-          $reindex = TRUE;
-        }
-        elseif ($old_inner_type == 'date' || $new_inner_type == 'date') {
-          // Even though the SQL type stays the same, we have to reindex since conversion rules change.
-          $reindex = TRUE;
-        }
-      }
-      elseif (!$reindex && $new_inner_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))
-          ->execute();
-      }
-      unset($new_fields[$name]);
-    }
-    $prefix = 'search_api_db_' . $index->machine_name. '_';
-    // These are new fields that were previously not indexed.
-    foreach ($new_fields as $name => $field) {
-      $reindex = TRUE;
-      $table = $this->findFreeTable($prefix, $name);
-      $this->createFieldTable($index, $field, $table);
-      $fields[$name]['table'] = $table;
-      $fields[$name]['type']  = $field['type'];
-      $fields[$name]['boost'] = $field['boost'];
-    }
-    if ($set) {
-      $this->resetDb();
-    }
-
-    $this->server->save();
-    return $reindex;
-  }
-
-  public function removeIndex($index) {
-    $id = is_object($index) ? $index->machine_name : $index;
-    if (!isset($this->options['indexes'][$id])) {
-      return;
-    }
-    $set = $this->setDb();
-    foreach ($this->options['indexes'][$id] as $field) {
-      db_drop_table($field['table']);
-    }
-    if ($set) {
-      $this->resetDb();
-    }
-    unset($this->options['indexes'][$id]);
-    $this->server->save();
-  }
-
-  public function indexItems(SearchApiIndex $index, array $items) {
-    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();
-    foreach ($items as $id => $item) {
-      try {
-        if ($this->indexItem($index, $id, $item)) {
-          $indexed[] = $id;
-        }
-      }
-      catch (Exception $e) {
-        // We just log the error, hoping we can index the other items.
-        watchdog('search api', $e->getMessage(), NULL, WATCHDOG_WARNING);
-      }
-    }
-    if ($set) {
-      $this->resetDb();
-    }
-    return $indexed;
-  }
-
-  protected function indexItem(SearchApiIndex $index, $id, array $item) {
-    $fields = $this->options['indexes'][$index->machine_name];
-    $txn = db_transaction('search_api_indexing', $this->query_options);
-    try {
-      foreach ($item as $name => $field) {
-        $table = $fields[$name]['table'];
-        $boost = $fields[$name]['boost'];
-        db_delete($table, $this->query_options)
-            ->condition('item_id', $id)
-            ->execute();
-        $type = $field['type'];
-        $value = $this->convert($field['value'], $type, $field['original_type'], $index);
-
-        if (search_api_is_text_type($type, array('text', 'tokens'))) {
-          $words = array();
-          foreach ($value as $token) {
-            // Taken from core search to reflect less importance of words later
-            // in the text.
-            // Focus is a decaying value in terms of the amount of unique words
-            // up to this point. From 100 words and more, it decays, to e.g. 0.5
-            // at 500 words and 0.3 at 1000 words.
-            $focus = min(1, .01 + 3.5 / (2 + count($words) * .015));
-
-            $value = &$token['value'];
-            if (is_numeric($value)) {
-              $value = ltrim($value, '-0');
-            }
-            elseif (drupal_strlen($value) < $this->options['min_chars']) {
-              continue;
-            }
-            $value = drupal_strtolower($value);
-            $token['score'] *= $focus;
-            if (!isset($words[$value])) {
-              $words[$value] = $token;
-            }
-            else {
-              $words[$value]['score'] += $token['score'];
-            }
-          }
-          if ($words) {
-            $query = db_insert($table, $this->query_options)
-              ->fields(array('item_id', 'word', 'score'));
-            foreach ($words as $word) {
-              $query->values(array(
-                'item_id' => $id,
-                'word'    => $word['value'],
-                'score'   => $word['score'] * $boost,
-              ));
-            }
-            $query->execute();
-          }
-        }
-        elseif (search_api_is_list_type($type)) {
-          $values = array();
-          if (is_array($value)) {
-            foreach ($value as $v) {
-              if ($v !== NULL) {
-                $values[$v] = TRUE;
-              }
-            }
-            $values = array_keys($values);
-          }
-          else {
-            $values[] = $value;
-          }
-          if ($values) {
-            $insert = db_insert($table, $this->query_options)
-              ->fields(array('item_id', 'value'));
-            foreach ($values as $v) {
-              $insert->values(array(
-                'item_id' => $id,
-                'value'   => $v,
-              ));
-            }
-            $insert->execute();
-          }
-        }
-        else {
-          db_insert($table, $this->query_options)
-            ->fields(array(
-              'item_id' => $id,
-              'value'   => $value,
-            ))
-            ->execute();
-        }
-      }
-    }
-    catch (Exception $e) {
-      $txn->rollback();
-      throw $e;
-    }
-    return TRUE;
-  }
-
-  protected function convert($value, $type, $original_type, SearchApiIndex $index) {
-    if (search_api_is_list_type($type)) {
-      $type = substr($type, 5, -1);
-      $original_type = search_api_extract_inner_type($original_type);
-      $ret = array();
-      if (is_array($value)) {
-        foreach ($value as $v) {
-          $v = $this->convert($v, $type, $original_type, $index);
-          $ret = array_merge($ret, is_array($v) ? $v : array($v));
-        }
-      }
-      return $ret;
-    }
-    switch ($type) {
-      case 'text':
-        $ret = array();
-        foreach (preg_split('/[^\p{L}\p{N}]+/u', $value, -1, PREG_SPLIT_NO_EMPTY) as $v) {
-          if ($v) {
-            $ret[] = array(
-              'value' => $v,
-              'score' => 1.0,
-            );
-          }
-        }
-        $value = $ret;
-        // FALL-THROUGH!
-      case 'tokens':
-        while (TRUE) {
-          foreach ($value as $i => $v) {
-            // Check for over-long tokens.
-            $score = $v['score'];
-            $v = $v['value'];
-            if (drupal_strlen($v) > 50) {
-              $words = preg_split('/[^\p{L}\p{N}]+/u', $v, -1, PREG_SPLIT_NO_EMPTY);
-              if (count($words) > 1 && max(array_map('drupal_strlen', $words)) <= 50) {
-                // Overlong token is due to bad tokenizing.
-                // Check for "Tokenizer" preprocessor on index.
-                if (empty($index->options['processors']['search_api_tokenizer']['status'])) {
-                  watchdog('search_api_db', 'An overlong word (more than 50 characters) was encountered while indexing, due to bad tokenizing. ' .
-                      'It is recommended to enable the "Tokenizer" preprocessor for indexes using database servers. ' .
-                      'Otherwise, the service class has to use its own, fixed tokenizing.', array(), WATCHDOG_WARNING);
-                }
-                else {
-                  watchdog('search_api_db', 'An overlong word (more than 50 characters) was encountered while indexing, due to bad tokenizing. ' .
-                      'Please check your settings for the "Tokenizer" preprocessor to ensure that data is tokenized correctly.',
-                      array(), WATCHDOG_WARNING);
-                }
-              }
-
-              $tokens = array();
-              foreach ($words as $word) {
-                if (drupal_strlen($word) > 50) {
-                  watchdog('search_api_db', 'An overlong word (more than 50 characters) was encountered while indexing: %word.<br />' .
-                      'Database search servers currently cannot index such words correctly – the word was therefore trimmed to the allowed length.',
-                      array('%word' => $word), WATCHDOG_WARNING);
-                  $word = drupal_substr($word, 0, 50);
-                }
-                $tokens[] = array(
-                  'value' => $word,
-                  'score' => $score,
-                );
-              }
-              array_splice($value, $i, 1, $tokens);
-              continue 2;
-            }
-          }
-          break;
-        }
-        return $value;
-
-      case 'string':
-      case 'uri':
-        // For non-dates, PHP can handle this well enough
-        if ($original_type == 'date') {
-          return date('%c', $value);
-        }
-        if (drupal_strlen($value) > 255) {
-          throw new SearchApiException(t('A string value longer than 255 characters was encountered. ' .
-              "Such values currently aren't supported by the database backend."));
-        }
-        return $value;
-
-      case 'integer':
-      case 'duration':
-      case 'decimal':
-        return 0 + $value;
-
-      case 'boolean':
-        return $value ? 1 : 0;
-
-      case 'date':
-        if (is_numeric($value) || !$value) {
-          return 0 + $value;
-        }
-        return strtotime($value);
-
-      default:
-        throw new SearchApiException(t('Unknown field type !type. Database search module might be out of sync with Search API.', array('!type' => $type)));
-    }
-  }
-
-  public function deleteItems($ids = 'all', SearchApiIndex $index = NULL) {
-    if (!$index) {
-      if (empty($this->options['indexes'])) {
-        return;
-      }
-      $set = $this->setDb();
-      foreach ($this->options['indexes'] as $index) {
-        foreach ($index as $fields) {
-          foreach ($fields as $field) {
-            db_truncate($field['table'], $this->query_options)->execute();
-          }
-        }
-      }
-      if ($set) {
-        $this->resetDb();
-      }
-      return;
-    }
-
-    if (empty($this->options['indexes'][$index->machine_name])) {
-      return;
-    }
-    $set = $this->setDb();
-    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')
-          ->execute();
-      }
-      else {
-        db_truncate($field['table'], $this->query_options)->execute();
-      }
-    }
-    if ($set) {
-      $this->resetDb();
-    }
-  }
-
-  public function search(SearchApiQueryInterface $query) {
-    $time_method_called = microtime(TRUE);
-    $set = $this->setDb();
-    $index = $query->getIndex();
-    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->machine_name];
-
-    $keys = &$query->getKeys();
-    $keys_set = (boolean) $keys;
-    $keys = $this->prepareKeys($keys);
-    if ($keys && !(is_array($keys) && count($keys) == 1)) {
-      $fulltext_fields = $query->getFields();
-      if ($fulltext_fields) {
-        $_fulltext_fields = $fulltext_fields;
-        $fulltext_fields = array();
-        foreach ($_fulltext_fields as $name) {
-          if (!isset($fields[$name])) {
-            throw new SearchApiException(t('Unknown field !field specified as search target.', array('!field' => $name)));
-          }
-          if (!search_api_is_text_type($fields[$name]['type'])) {
-            throw new SearchApiException(t('Cannot perform fulltext search on field !field of type !type.', array('!field' => $name, '!type' => $fields[$name]['type'])));
-          }
-          $fulltext_fields[$name] = $fields[$name];
-        }
-
-        $db_query = $this->createKeysQuery($keys, $fulltext_fields, $fields);
-        if (is_array($keys) && !empty($keys['#negation'])) {
-          $db_query->addExpression(':score', 'score', array(':score' => 1));
-        }
-      }
-      else {
-        $msg = t('Search keys are given but no fulltext fields are defined.');
-        watchdog('search api', $msg, NULL, WATCHDOG_WARNING);
-        $this->warnings[$msg] = 1;
-      }
-    }
-    elseif ($keys_set) {
-      $msg = t('No valid search keys were present in the query.');
-      $this->warnings[$msg] = 1;
-    }
-
-    $filter = $query->getFilter();
-    if ($filter->getFilters()) {
-      if (!isset($db_query)) {
-        $db_query = db_select($fields['search_api_language']['table'], 't', $this->query_options);
-        $db_query->addField('t', 'item_id', 'item_id');
-        $db_query->addExpression(':score', 'score', array(':score' => 1));
-      }
-      $condition = $this->createFilterCondition($filter, $fields, $db_query);
-      if ($condition) {
-        $db_query->condition($condition);
-      }
-    }
-
-    if (!isset($db_query)) {
-      $db_query = db_select($fields['search_api_language']['table'], 't', $this->query_options);
-      $db_query->addField('t', 'item_id', 'item_id');
-      $db_query->addExpression(':score', 'score', array(':score' => 1));
-    }
-
-    $time_processing_done = microtime(TRUE);
-    $results = array();
-    $count_query = $db_query->countQuery();
-    $results['result count'] = $count_query->execute()->fetchField();
-
-    if ($results['result count']) {
-      if ($query->getOption('search_api_facets')) {
-        $results['search_api_facets'] = $this->getFacets($query, clone $db_query);
-      }
-
-      $query_options = $query->getOptions();
-      if (isset($query_options['offset']) || isset($query_options['limit'])) {
-        $offset = isset($query_options['offset']) ? $query_options['offset'] : 0;
-        $limit = isset($query_options['limit']) ? $query_options['limit'] : 1000000;
-        $db_query->range($offset, $limit);
-      }
-
-      $sort = $query->getSort();
-      if ($sort) {
-        foreach ($sort as $field_name => $order) {
-          if ($order != 'ASC' && $order != 'DESC') {
-            $msg = t('Unknown sort order !order. Assuming "ASC".', array('!order' => $order));
-            $this->warnings[$msg] = $msg;
-            $order = 'ASC';
-          }
-          if ($field_name == 'search_api_relevance') {
-            $db_query->orderBy('score', $order);
-            continue;
-          }
-          if ($field_name == 'search_api_id') {
-            $db_query->orderBy('item_id', $order);
-            continue;
-          }
-          if (!isset($fields[$field_name])) {
-            throw new SearchApiException(t('Trying to sort on unknown field !field.', array('!field' => $field_name)));
-          }
-          $field = $fields[$field_name];
-          if (search_api_is_list_type($field['type'])) {
-            throw new SearchApiException(t('Cannot sort on field !field of a list type.', array('!field' => $field_name)));
-          }
-          if (search_api_is_text_type($field['type'])) {
-            throw new SearchApiException(t('Cannot sort on fulltext field !field.', array('!field' => $field_name)));
-          }
-          $alias = $this->getTableAlias($field, $db_query);
-          $db_query->orderBy($alias . '.value', $order);
-        }
-      }
-      else {
-        $db_query->orderBy('score', 'DESC');
-      }
-
-      $result = $db_query->execute();
-      $time_queries_done = microtime(TRUE);
-
-      foreach ($result as $row) {
-        $results['results'][$row->item_id] = array(
-          'id' => $row->item_id,
-          'score' => $row->score,
-        );
-      }
-    }
-    else {
-      $time_queries_done = microtime(TRUE);
-      $results['results'] = array();
-    }
-
-    $results['warnings'] = array_keys($this->warnings);
-    $results['ignored'] = array_keys($this->ignored);
-
-    if ($set) {
-      $this->resetDb();
-    }
-
-    $time_end = microtime(TRUE);
-    $results['performance'] = array(
-      'complete' => $time_end - $time_method_called,
-      'preprocessing' => $time_processing_done - $time_method_called,
-      'execution' => $time_queries_done - $time_processing_done,
-      'postprocessing' => $time_end - $time_queries_done,
-    );
-
-    return $results;
-  }
-
-  /**
-   * Helper method for removing unnecessary nested expressions from keys.
-   */
-  protected function prepareKeys($keys) {
-    if (is_scalar($keys)) {
-      $keys = $this->splitKeys($keys);
-      return is_array($keys) ? $this->eliminateDuplicates($keys) : $keys;
-    }
-    elseif (!$keys) {
-      return NULL;
-    }
-    $keys = $this->eliminateDuplicates($this->splitKeys($keys));
-    $conj = $keys['#conjunction'];
-    $neg = !empty($keys['#negation']);
-    foreach ($keys as $i => &$nested) {
-      if (is_array($nested)) {
-        $nested = $this->prepareKeys($nested);
-        if ($neg == !empty($nested['#negation'])) {
-          if ($nested['#conjunction'] == $conj) {
-            unset($nested['#conjunction'], $nested['#negation']);
-            foreach ($nested as $renested) {
-              $keys[] = $renested;
-            }
-            unset($keys[$i]);
-          }
-        }
-      }
-    }
-    $keys = array_filter($keys);
-    if (($count = count($keys)) <= 2) {
-      if ($count < 2 || isset($keys['#negation'])) {
-        $keys = NULL;
-      }
-      else {
-        unset($keys['#conjunction']);
-        $keys = array_shift($keys);
-      }
-    }
-    return $keys;
-  }
-
-  /**
-   * Helper method for splitting keys.
-   */
-  protected function splitKeys($keys) {
-    if (is_scalar($keys)) {
-      $proc = drupal_strtolower(trim($keys));
-      if (is_numeric($proc)) {
-        return ltrim($proc, '-0');
-      }
-      elseif (drupal_strlen($proc) < $this->options['min_chars']) {
-        $this->ignored[$keys] = 1;
-        return NULL;
-      }
-      $words = preg_split('/[^\p{L}\p{N}]+/u', $proc, -1, PREG_SPLIT_NO_EMPTY);
-      if (count($words) > 1) {
-        $proc = $this->splitKeys($words);
-        $proc['#conjunction'] = 'AND';
-      }
-      return $proc;
-    }
-    foreach ($keys as $i => $key) {
-      if (element_child($i)) {
-        $keys[$i] = $this->splitKeys($key);
-      }
-    }
-    return array_filter($keys);
-  }
-
-  /**
-   * Helper method for eliminating duplicates from the search keys.
-   */
-  protected function eliminateDuplicates($keys, &$words = array()) {
-    foreach ($keys as $i => $word) {
-      if (!element_child($i)) {
-        continue;
-      }
-      if (is_scalar($word)) {
-        if (isset($words[$word])) {
-          unset($keys[$i]);
-        }
-        else {
-          $words[$word] = TRUE;
-        }
-      }
-      else {
-        $keys[$i] = $this->eliminateDuplicates($word, $words);
-      }
-    }
-    return $keys;
-  }
-
-  /**
-   * Helper method for creating a SELECT query for given search keys.
-   *
-   * @return SelectQueryInterface
-   *   A SELECT query returning item_id and score (or only item_id, if
-   *   $keys['#negation'] is set).
-   */
-  protected function createKeysQuery($keys, array $fields, array $all_fields) {
-    if (!is_array($keys)) {
-      $keys = array(
-        '#conjunction' => 'AND',
-        $keys,
-      );
-    }
-
-    $or = db_or();
-    $neg = !empty($keys['#negation']);
-    $conj = $keys['#conjunction'];
-    $words = array();
-    $nested = array();
-    $negated = array();
-    $db_query = NULL;
-    $mul_words = FALSE;
-    $not_nested = FALSE;
-
-    foreach ($keys as $i => $key) {
-      if (!element_child($i)) {
-        continue;
-      }
-      if (is_scalar($key)) {
-        $words[] = $key;
-      }
-      elseif (empty($key['#negation'])) {
-        if ($neg) {
-          // If this query is negated, we also only need item_ids from
-          // subqueries.
-          $key['#negation'] = TRUE;
-        }
-        $nested[] = $key;
-      }
-      else {
-        $negated[] = $key;
-      }
-    }
-    $subs = count($words) + count($nested);
-    $not_nested = ($subs <= 1 && count($fields) == 1) || ($neg && $conj == 'OR' && !$negated);
-
-    if ($words) {
-      if (count($words) > 1) {
-        $mul_words = TRUE;
-        foreach ($words as $word) {
-          $or->condition('word', $word);
-        }
-      }
-      else {
-        $word = array_shift($words);
-      }
-      foreach ($fields as $name => $field) {
-        $table = $field['table'];
-        $query = db_select($table, 't', $this->query_options);
-        if ($neg) {
-          $query->fields('t', array('item_id'));
-        }
-        elseif ($not_nested) {
-          $query->fields('t', array('item_id', 'score'));
-        }
-        else {
-          $query->fields('t');
-        }
-        if ($mul_words) {
-          $query->condition($or);
-        }
-        else {
-          $query->condition('word', $word);
-        }
-        if (!isset($db_query)) {
-          $db_query = $query;
-        }
-        elseif ($not_nested) {
-          $db_query->union($query, 'UNION');
-        }
-        else {
-          $db_query->union($query, 'UNION ALL');
-        }
-      }
-    }
-
-    if ($nested) {
-      $word = '';
-      foreach ($nested as $k) {
-        $query = $this->createKeysQuery($k, $fields, $all_fields);
-        if (!$neg) {
-          $word .= ' ';
-          $var = ':word' . strlen($word);
-          $query->addExpression($var, 'word', array($var => $word));
-        }
-        if (!isset($db_query)) {
-          $db_query = $query;
-        }
-        elseif ($not_nested) {
-          $db_query->union($query, 'UNION');
-        }
-        else {
-          $db_query->union($query, 'UNION ALL');
-        }
-      }
-    }
-
-    if (isset($db_query) && !$not_nested) {
-      $db_query = db_select($db_query, 't', $this->query_options);
-      $db_query->addField('t', 'item_id', 'item_id');
-      if (!$neg) {
-        $db_query->addExpression('SUM(t.score)', 'score');
-        $db_query->groupBy('t.item_id');
-      }
-      if ($conj == 'AND' && $subs > 1) {
-        $var = ':subs' . ((int) $subs);
-        if (!$db_query->getGroupBy()) {
-          $db_query->groupBy('t.item_id');
-        }
-        if ($mul_words) {
-          $db_query->having('COUNT(DISTINCT t.word) >= ' . $var, array($var => $subs));
-        }
-        else {
-          $db_query->having('COUNT(DISTINCT t.word) >= ' . $var, array($var => $subs));
-        }
-      }
-    }
-
-    if ($negated) {
-      if (!isset($db_query) || $conj == 'OR') {
-        if (isset($all_fields['search_api_language'])) {
-          // We use this table because all items should be contained exactly once.
-          $table = $all_fields['search_api_language']['table'];
-        }
-        else {
-          $distinct = TRUE;
-          foreach ($all_fields as $field) {
-            $table = $field['table'];
-            if (!search_api_is_list_type($field['type']) && !search_api_is_text_type($field['type'])) {
-              unset($distinct);
-              break;
-            }
-          }
-        }
-        if (isset($db_query)) {
-          // We are in a rather bizarre case where the keys are something like "a OR (NOT b)".
-          $old_query = $db_query;
-        }
-        $db_query = db_select($table, 't', $this->query_options);
-        $db_query->addField('t', 'item_id', 'item_id');
-        if (!$neg) {
-          $db_query->addExpression(':score', 'score', array(':score' => 1));
-        }
-        if (isset($distinct)) {
-          $db_query->distinct();
-        }
-      }
-
-      if ($conj == 'AND') {
-        foreach ($negated as $k) {
-          $db_query->condition('t.item_id', $this->createKeysQuery($k, $fields, $all_fields), 'NOT IN');
-        }
-      }
-      else {
-        $or = db_or();
-        foreach ($negated as $k) {
-          $or->condition('t.item_id', $this->createKeysQuery($k, $fields, $all_fields), 'NOT IN');
-        }
-        if (isset($old_query)) {
-          $or->condition('t.item_id', $old_query, 'NOT IN');
-        }
-        $db_query->condition($or);
-      }
-    }
-
-    return $db_query;
-  }
-
-  /**
-   * Helper method for finding any needed table for a filter query.
-   */
-  protected function findTable(array $filters, array $fields) {
-    foreach ($filters as $filter) {
-      if (is_array($filter)) {
-        return $fields[$filter[0]]['table'];
-      }
-    }
-    foreach ($filters as $filter) {
-      if (is_object($filter)) {
-        $ret = $this->findTable($filter->getFilters(), $fields);
-        if ($ret) {
-          return $ret;
-        }
-      }
-    }
-  }
-
-  /**
-   * Helper method for creating a condition for filtering search results.
-   *
-   * @return QueryConditionInterface
-   */
-  protected function createFilterCondition(SearchApiQueryFilterInterface $filter, array $fields, SelectQueryInterface $db_query) {
-    $cond = db_condition($filter->getConjunction());
-    $empty = TRUE;
-    foreach ($filter->getFilters() as $f) {
-      if (is_object($f)) {
-        $c = $this->createFilterCondition($f, $fields, $db_query);
-        if ($c) {
-          $empty = FALSE;
-          $cond->condition($c);
-        }
-      }
-      else {
-        $empty = FALSE;
-        if (!isset($fields[$f[0]])) {
-          throw new SearchApiException(t('Unknown field in filter clause: !field.', array('!field' => $f[0])));
-        }
-        if ($f[1] === NULL) {
-          $query = db_select($fields[$f[0]]['table'], 't')
-            ->fields('t', array('item_id'));
-          $cond->condition('t.item_id', $query, $f[2] == '<>' || $f[2] == '!=' ? 'IN' : 'NOT IN');
-          continue;
-        }
-        if (search_api_is_text_type($fields[$f[0]]['type'])) {
-          // #negation is set here purely because we don't want any score.
-          $keys = array('#conjunction' => 'AND', '#negation' => TRUE, $f[1]);
-          $keys = $this->prepareKeys($keys);
-          $query = $this->createKeysQuery($keys, array($fields[$f[0]]), $fields);
-          $cond->condition('t.item_id', $query, $f[2] == '<>' || $f[2] == '!=' ? 'NOT IN' : 'IN');
-        }
-        else {
-          $alias = $this->getTableAlias($fields[$f[0]], $db_query, search_api_is_list_type($fields[$f[0]]['type']));
-          $cond->condition($alias . '.value', $f[1], $f[2]);
-        }
-      }
-    }
-    return $empty ? NULL : $cond;
-  }
-
-  /**
-   * Helper method for adding a field's table to a database query.
-   *
-   * @param array $field
-   *   The field information array. The "table" key should contain the table
-   *   name to which a join should be made.
-   * @param SelectQueryInterface $db_query
-   *   The database query used.
-   * @param $newjoin
-   *   If TRUE, a join is done even if the table was already joined to in the
-   *   query.
-   */
-  protected function getTableAlias(array $field, SelectQueryInterface $db_query, $newjoin = FALSE) {
-    if(!$newjoin) {
-      foreach ($db_query->getTables() as $alias => $info) {
-        $table = $info['table'];
-        if (is_scalar($table) && $table == $field['table']) {
-          return $alias;
-        }
-      }
-    }
-    return $db_query->join($field['table'], 't', 't.item_id = %alias.item_id');
-  }
-
-  /**
-   * Helper method for getting the facet values for a query.
-   */
-  protected function getFacets(SearchApiQueryInterface $query, SelectQueryInterface $db_query) {
-    // We only need the id field, not the score.
-    $fields = &$db_query->getFields();
-    unset($fields['score']);
-    if (count($fields) != 1 || !isset($fields['item_id'])) {
-      $this->warnings[] = t('Error while adding facets: only "item_id" field should be used, used are: @fields.',
-          array('@fields' => implode(', ', array_keys($fields))));
-      return array();
-    }
-    $expressions = &$db_query->getExpressions();
-    $expressions = array();
-    $group_by = &$db_query->getGroupBy();
-    $group_by = array();
-    $db_query->distinct();
-    if (!$db_query->preExecute()) {
-      return array();
-    }
-    $args = $db_query->getArguments();
-    $table = db_query_temporary((string) $db_query, $args, $this->query_options);
-
-    $fields = $this->options['indexes'][$query->getIndex()->machine_name];
-    $ret = array();
-    foreach ($query->getOption('search_api_facets') as $key => $facet) {
-      if (empty($fields[$facet['field']])) {
-        $this->warnings[] = t('Unknown facet field @field.', array('@field' => $facet['field']));
-        continue;
-      }
-      $field = $fields[$facet['field']];
-
-      $select = db_select($table, 't');
-      $alias = $this->getTableAlias($field, $select, TRUE);
-      $select->addField($alias, search_api_is_text_type($field['type']) ? 'word' : 'value', 'value');
-      $select->addExpression('COUNT(DISTINCT t.item_id)', 'num');
-      $select->groupBy('value');
-      $select->orderBy('num', 'DESC');
-
-      $limit = $facet['limit'];
-      if ($limit) {
-        $select->range(0, $limit);
-      }
-      if ($facet['min_count'] > 1) {
-        $select->having('num >= :count', array(':count' => $facet['min_count']));
-      }
-      if ($facet['missing']) {
-        $inner_query = db_select($field['table'], 't1')
-          ->fields('t1', array('item_id'));
-        $missing_count = db_select($table, 't');
-        $missing_count->addExpression('COUNT(item_id)');
-        $missing_count->condition('item_id', $inner_query, 'NOT IN');
-        $missing_count = $missing_count->execute()->fetchField();
-        $missing_count = $missing_count >= $facet['min_count'] ? $missing_count : 0;
-      }
-      else {
-        $missing_count = 0;
-      }
-
-      $terms = array();
-      foreach ($select->execute() as $row) {
-        if ($missing_count && $missing_count > $row->num) {
-          $terms[] = array(
-            'count'  => $missing_count,
-            'filter' => '!',
-          );
-          $missing_count = 0;
-          if ($limit && count($terms) == $limit) {
-            break;
-          }
-        }
-        $terms[] = array(
-          'count' => $row->num,
-          'filter' => '"' . $row->value . '"',
-        );
-      }
-      if ($missing_count && (!$limit || count($terms) < $limit)) {
-        $terms[] = array(
-          'count'  => $missing_count,
-          'filter' => '!',
-        );
-      }
-      $ret[$key] = $terms;
-    }
-    return $ret;
-  }
-
-  /**
-   * Helper method for setting the database to the one selected by the user.
-   */
-  protected function setDb() {
-    if (!isset($this->previous_db)) {
-      list($key, $target) = explode(':', $this->options['database'], 2);
-      $this->previous_db = db_set_active($key);
-      if (!isset($this->query_options)) {
-        $this->query_options = array('target' => $target);
-      }
-      return TRUE;
-    }
-    return FALSE;
-  }
-
-  /**
-   * Helper method for resetting the original database.
-   */
-  protected function resetDb() {
-    if (isset($this->previous_db)) {
-      db_set_active($this->previous_db);
-      $this->previous_db = NULL;
-      return TRUE;
-    }
-    return FALSE;
-  }
-
-}
-- 
1.7.4.1

