You are here

search_api_db.test in Search API Database Search 7

File

search_api_db.test
View source
<?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 Database Search',
    );
  }
  public function setUp() {
    parent::setUp('entity', 'search_api', 'search_api_db', 'search_api_test');
  }
  public function testFramework() {
    if (Database::getConnection()
      ->databaseType() == 'mysql') {
      try {
        db_query("SET SESSION sql_mode = 'ANSI,ONLY_FULL_GROUP_BY'");
      } catch (Exception $e) {

        // It was worth a try, but if it fails just go on.
      }
    }
    $this
      ->drupalLogin($this
      ->drupalCreateUser(array(
      'administer search_api',
    )));
    $this
      ->insertItems();
    $this
      ->createServer();
    $this
      ->createIndex();
    $this
      ->searchNoResults();
    $this
      ->indexItems();
    $this
      ->searchSuccess1();
    $this
      ->checkFacets();
    $this
      ->regressionTests();
    $this
      ->editServerPartial();
    $this
      ->searchSuccessPartial();
    $this
      ->editServer();
    $this
      ->searchSuccess2();
    $this
      ->clearIndex();
    $this
      ->searchNoResults();
    $this
      ->regressionTests2();
    $this
      ->uninstallModule();
  }
  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 foobaz',
      'body' => 'test test',
      'type' => 'item',
      'keywords' => 'orange',
    ));
    $this
      ->insertItem(array(
      'id' => 2,
      'title' => 'foo test foobuz',
      'body' => 'bar test',
      'type' => 'item',
      'keywords' => 'orange,apple,grape',
    ));
    $this
      ->insertItem(array(
      'id' => 3,
      'title' => 'bar',
      'body' => 'test foobar',
    ));
    $this
      ->insertItem(array(
      'id' => 4,
      'title' => 'foo baz',
      'body' => 'test test test',
      'type' => 'article',
      'keywords' => 'apple,strawberry,grape',
    ));
    $this
      ->insertItem(array(
      'id' => 5,
      'title' => 'bar baz',
      'body' => 'foo',
      'type' => 'article',
      'keywords' => 'orange,strawberry,grape,banana,orange,Orange',
    ));
    $count = db_query('SELECT COUNT(*) FROM {search_api_test}')
      ->fetchField() - $count;
    $this
      ->assertEqual($count, 5, "{$count} items inserted.");
  }
  protected function insertItem($values) {
    $this
      ->drupalPost(NULL, $values, t('Save'));
  }
  protected function createServer() {
    $this->server_id = 'database_search_server';
    global $databases;
    $database = 'default:default';

    // Make sure to pick an available connection and to not rely on any
    // defaults.
    foreach ($databases as $key => $targets) {
      foreach ($targets as $target => $info) {
        $database = "{$key}:{$target}";
        break;
      }
    }
    $values = array(
      'name' => 'Database search server',
      'machine_name' => $this->server_id,
      'enabled' => 1,
      'description' => 'A server used for testing.',
      'class' => 'search_api_db_service',
      'options' => array(
        'min_chars' => 3,
        'database' => $database,
        'partial_matches' => FALSE,
      ),
    );
    $success = (bool) entity_create('search_api_server', $values)
      ->save();
    $this
      ->assertTrue($success, 'The server was successfully created.');
  }
  protected function createIndex() {
    $this->index_id = 'test_index';
    $values = array(
      'name' => 'Test index',
      'machine_name' => $this->index_id,
      'item_type' => 'search_api_test',
      'enabled' => 1,
      'description' => 'An index used for testing.',
      'server' => $this->server_id,
      'options' => array(
        'cron_limit' => -1,
        'index_directly' => TRUE,
        'fields' => array(
          'id' => array(
            'type' => 'integer',
          ),
          'title' => array(
            'type' => 'text',
            'boost' => '5.0',
          ),
          'body' => array(
            'type' => 'text',
          ),
          'type' => array(
            'type' => 'string',
          ),
          'keywords' => array(
            'type' => 'list<string>',
          ),
          'search_api_language' => array(
            'type' => 'string',
          ),
        ),
      ),
    );
    $index = entity_create('search_api_index', $values);
    $success = (bool) $index
      ->save();
    $this
      ->assertTrue($success, 'The index was successfully created.');
    $status = search_api_index_status($index);
    $this
      ->assertEqual($status['total'], 5, 'Correct item count.');
    $this
      ->assertEqual($status['indexed'], 0, 'All items still need to be indexed.');
  }
  protected function buildSearch($keys = NULL, array $filters = array(), array $fields = array()) {
    $query = search_api_query($this->index_id);
    if ($keys) {
      $query
        ->keys($keys);
      if ($fields) {
        $query
          ->fields($fields);
      }
    }
    foreach ($filters as $filter) {
      list($field, $value) = explode(',', $filter, 2);
      $query
        ->condition($field, $value);
    }
    $query
      ->range(0, 10);
    return $query;
  }
  protected function searchNoResults() {
    $results = $this
      ->buildSearch('test')
      ->execute();
    $this
      ->assertEqual($results['result count'], 0, 'No search results returned without indexing.');
    $this
      ->assertEqual(array_keys($results['results']), array(), 'No search results returned without indexing.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
  }
  protected function indexItems() {
    search_api_index_items(search_api_index_load($this->index_id));
  }
  protected function searchSuccess1() {
    $results = $this
      ->buildSearch('test')
      ->range(1, 2)
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search for »test« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      4,
      1,
    ), 'Search for »test« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('test foo')
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'Search for »test foo« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
      4,
      1,
    ), 'Search for »test foo« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('foo', array(
      'type,item',
    ))
      ->sort('id', 'ASC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Search for »foo« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
    ), 'Search for »foo« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'AND',
      'test',
      array(
        '#conjunction' => 'OR',
        'baz',
        'foobar',
      ),
      array(
        '#conjunction' => 'OR',
        '#negation' => TRUE,
        'bar',
        'fooblob',
      ),
    );
    $results = $this
      ->buildSearch($keys)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Complex search 1 returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      4,
    ), 'Complex search 1 returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch()
      ->sort('id');
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('title', 'bar');
    $filter
      ->condition('body', 'bar');
    $query
      ->filter($filter);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search with multi-field fulltext filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      3,
      5,
    ), 'Search with multi-field fulltext filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
  }
  protected function checkFacets() {
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR', array(
      'facet:type',
    ));
    $filter
      ->condition('type', 'article');
    $query
      ->filter($filter);
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 1,
      'missing' => TRUE,
      'operator' => 'or',
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'OR facets query returned correct number of results.');
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
      array(
        'count' => 1,
        'filter' => '!',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct OR facets were returned');
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR', array(
      'facet:type',
    ));
    $filter
      ->condition('type', 'article');
    $query
      ->filter($filter);
    $filter = $query
      ->createFilter('AND');
    $filter
      ->condition('type', NULL, '<>');
    $query
      ->filter($filter);
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 1,
      'missing' => TRUE,
      'operator' => 'or',
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'OR facets query returned correct number of results.');
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct OR facets were returned');
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR', array(
      'facet:type',
    ));
    $filter
      ->condition('type', 'article');
    $query
      ->filter($filter);
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 2,
      'missing' => TRUE,
      'operator' => 'or',
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct OR facets were returned with min_count');
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR', array(
      'facet:type',
    ));
    $filter
      ->condition('type', 'article');
    $query
      ->filter($filter);
    $filter = $query
      ->createFilter('AND');
    $filter
      ->condition('type', NULL, '<>');
    $query
      ->filter($filter);
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 2,
      'missing' => TRUE,
      'operator' => 'or',
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct OR facets were returned with min_count');
  }
  protected function editServer() {
    $server = search_api_server_load($this->server_id, TRUE);
    $server->options['min_chars'] = 4;
    $server->options['partial_matches'] = FALSE;
    $success = (bool) $server
      ->save();
    $this
      ->assertTrue($success, 'The server was successfully edited.');
    $this
      ->clearIndex();
    $this
      ->indexItems();

    // Reset the internal cache so the new values will be available.
    search_api_index_load($this->index_id, TRUE);
  }
  protected function searchSuccess2() {
    $results = $this
      ->buildSearch('test')
      ->range(1, 2)
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search for »test« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      4,
      1,
    ), 'Search for »test« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch()
      ->sort('id');
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('title', 'test');
    $filter
      ->condition('body', 'test');
    $query
      ->filter($filter);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search with multi-field fulltext filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      3,
      4,
    ), 'Search with multi-field fulltext filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch(NULL, array(
      'body,test foobar',
    ))
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Search with multi-term fulltext filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'Search with multi-term fulltext filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('test foo')
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search for »test foo« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
      4,
      1,
      3,
    ), 'Search for »test foo« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(
      'foo',
    ), 'Short key was ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('foo', array(
      'type,item',
    ))
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Search for »foo« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
    ), 'Search for »foo« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(
      'foo',
    ), 'Short key was ignored.');
    $this
      ->assertEqual($results['warnings'], array(
      t('No valid search keys were present in the query.'),
    ), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'AND',
      'test',
      array(
        '#conjunction' => 'OR',
        'baz',
        'foobar',
      ),
      array(
        '#conjunction' => 'OR',
        '#negation' => TRUE,
        'bar',
        'fooblob',
      ),
    );
    $results = $this
      ->buildSearch($keys)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Complex search 1 returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'Complex search 1 returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(
      'baz',
      'bar',
    ), 'Correct keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'AND',
      'test',
      array(
        '#conjunction' => 'OR',
        'baz',
        'foobar',
      ),
      array(
        '#conjunction' => 'OR',
        '#negation' => TRUE,
        'bar',
        'fooblob',
      ),
    );
    $results = $this
      ->buildSearch($keys)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Complex search 2 returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'Complex search 2 returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(
      'baz',
      'bar',
    ), 'Correct keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch(NULL, array(
      'keywords,orange',
    ))
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'Filter query 1 on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      5,
    ), 'Filter query 1 on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'Warning displayed.');
    $results = $this
      ->buildSearch()
      ->condition('keywords', 'orange', '<>')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Negated filter on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      4,
    ), 'Negated filter on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'Warning displayed.');
    $filters = array(
      'keywords,orange',
      'keywords,apple',
    );
    $results = $this
      ->buildSearch(NULL, $filters)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Filter query 2 on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
    ), 'Filter query 2 on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch()
      ->condition('keywords', NULL)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Query with NULL filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'Query with NULL filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch()
      ->condition('keywords', NULL, '<>')
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Query with NOT NULL filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'Query with NOT NULL filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
  }

  /**
   * Edits the server to enable partial matches.
   *
   * @param bool $enable
   *   (optional) Whether partial matching should be enabled or disabled.
   */
  protected function editServerPartial($enable = TRUE) {
    $server = search_api_server_load($this->server_id, TRUE);
    $server->options['partial_matches'] = $enable;
    $success = (bool) $server
      ->save();
    $this
      ->assertTrue($success, 'The server was successfully edited.');

    // Reset the internal cache so the index won't use the old server.
    search_api_index_load($this->index_id, TRUE);
  }

  /**
   * Tests whether partial searches work.
   */
  protected function searchSuccessPartial() {
    $results = $this
      ->buildSearch('foobaz')
      ->range(0, 1)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Partial search for »foobaz« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
    ), 'Partial search for »foobaz« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('foo')
      ->sort('search_api_relevance', 'DESC')
      ->sort('id', 'ASC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 5, 'Partial search for »foo« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      3,
      5,
    ), 'Partial search for »foo« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch('foo', array(
      'type,item',
    ))
      ->sort('id', 'DESC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Partial search for »foo« of type »item« returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
      1,
    ), 'Partial search for »foo« of type »item« returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch()
      ->sort('id');
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('title', 'test');
    $filter
      ->condition('body', 'test');
    $query
      ->filter($filter);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Partial search with multi-field fulltext filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      3,
      4,
    ), 'Partial search with multi-field fulltext filter returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
  }

  /**
   * Executes regression tests for issues that were already fixed.
   */
  protected function regressionTests() {

    // Regression tests for #2007872.
    $results = $this
      ->buildSearch('test')
      ->sort('id', 'ASC')
      ->sort('type', 'ASC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Sorting on field with NULLs returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      3,
      4,
    ), 'Sorting on field with NULLs returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('id', 3);
    $filter
      ->condition('type', 'article');
    $query
      ->filter($filter);
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'OR filter on field with NULLs returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      4,
      5,
    ), 'OR filter on field with NULLs returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #1863672.
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('keywords', 'orange');
    $filter
      ->condition('keywords', 'apple');
    $query
      ->filter($filter);
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'OR filter on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'OR filter on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch();
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('keywords', 'orange');
    $filter
      ->condition('keywords', 'strawberry');
    $query
      ->filter($filter);
    $filter = $query
      ->createFilter('OR');
    $filter
      ->condition('keywords', 'apple');
    $filter
      ->condition('keywords', 'grape');
    $query
      ->filter($filter);
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'Multiple OR filters on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
      4,
      5,
    ), 'Multiple OR filters on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch();
    $filter1 = $query
      ->createFilter('OR');
    $filter = $query
      ->createFilter('AND');
    $filter
      ->condition('keywords', 'orange');
    $filter
      ->condition('keywords', 'apple');
    $filter1
      ->filter($filter);
    $filter = $query
      ->createFilter('AND');
    $filter
      ->condition('keywords', 'strawberry');
    $filter
      ->condition('keywords', 'grape');
    $filter1
      ->filter($filter);
    $query
      ->filter($filter1);
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'Complex nested filters on multi-valued field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      2,
      4,
      5,
    ), 'Complex nested filters on multi-valued field returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #2040543.
    $query = $this
      ->buildSearch();
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 1,
      'missing' => TRUE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
      array(
        'count' => 1,
        'filter' => '!',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct facets were returned');
    $query = $this
      ->buildSearch();
    $facets['type']['missing'] = FALSE;
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct facets were returned');

    // Regression tests for #2111753.
    $keys = array(
      '#conjunction' => 'OR',
      'foo',
      'test',
    );
    $query = $this
      ->buildSearch($keys, array(), array(
      'title',
    ));
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 3, 'OR keywords returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
    ), 'OR keywords returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch($keys, array(), array(
      'title',
      'body',
    ));
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 5, 'Multi-field OR keywords returned correct number of results.');
    $this
      ->assertTrue(empty($results['results']), 'Multi-field OR keywords returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'OR',
      'foo',
      'test',
      array(
        '#conjunction' => 'AND',
        'bar',
        'baz',
      ),
    );
    $query = $this
      ->buildSearch($keys, array(), array(
      'title',
    ));
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Nested OR keywords returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'Nested OR keywords returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'OR',
      array(
        '#conjunction' => 'AND',
        'foo',
        'test',
      ),
      array(
        '#conjunction' => 'AND',
        'bar',
        'baz',
      ),
    );
    $query = $this
      ->buildSearch($keys, array(), array(
      'title',
      'body',
    ));
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Nested multi-field OR keywords returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'Nested multi-field OR keywords returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #2127001.
    $keys = array(
      '#conjunction' => 'AND',
      '#negation' => TRUE,
      'foo',
      'bar',
    );
    $results = $this
      ->buildSearch($keys)
      ->sort('search_api_id', 'ASC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Negated AND fulltext search returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      4,
    ), 'Negated AND fulltext search returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'OR',
      '#negation' => TRUE,
      'foo',
      'baz',
    );
    $results = $this
      ->buildSearch($keys)
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Negated OR fulltext search returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'Negated OR fulltext search returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $keys = array(
      '#conjunction' => 'AND',
      'test',
      array(
        '#conjunction' => 'AND',
        '#negation' => TRUE,
        'foo',
        'bar',
      ),
    );
    $results = $this
      ->buildSearch($keys)
      ->sort('search_api_id', 'ASC')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Nested NOT AND fulltext search returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      4,
    ), 'Nested NOT AND fulltext search returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #2136409
    $query = $this
      ->buildSearch();
    $query
      ->condition('type', NULL);
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'NULL filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
    ), 'NULL filter returned correct result.');
    $query = $this
      ->buildSearch();
    $query
      ->condition('type', NULL, '<>');
    $query
      ->sort('id', 'ASC');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'NOT NULL filter returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'NOT NULL filter returned correct result.');

    // Regression tests for #1658964.
    $query = $this
      ->buildSearch();
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 0,
      'missing' => TRUE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->condition('type', 'article');
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"article"',
      ),
      array(
        'count' => 0,
        'filter' => '!',
      ),
      array(
        'count' => 0,
        'filter' => '"item"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct facets were returned');

    // Regression tests for #1403916.
    $query = $this
      ->buildSearch('test foo');
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 1,
      'missing' => TRUE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
      array(
        'count' => 1,
        'filter' => '"article"',
      ),
    );
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct facets were returned');

    // Regression tests for #2305107.
    $results = $this
      ->buildSearch('test')
      ->execute();
    $expected = array(
      2 => 6,
      4 => 3,
      1 => 2,
      3 => 1,
    );
    $scores = array();
    foreach ($results['results'] as $item_id => $result) {
      $scores[$item_id] = $result['score'];
    }
    $this
      ->assertIdentical($scores, $expected, 'Correct scores were computed.');
    $this
      ->editServerPartial();
    $results = $this
      ->buildSearch('test')
      ->execute();
    $this
      ->editServerPartial(FALSE);
    $scores = array();
    foreach ($results['results'] as $item_id => $result) {
      $scores[$item_id] = $result['score'];
    }
    $this
      ->assertIdentical($scores, $expected, 'Correct scores were computed with partial matching.');
    $results = $this
      ->buildSearch('test baz')
      ->execute();
    $expected = array(
      4 => 8,
      1 => 7,
    );
    $scores = array();
    foreach ($results['results'] as $item_id => $result) {
      $scores[$item_id] = $result['score'];
    }
    $this
      ->assertIdentical($scores, $expected, 'Correct scores were computed for two keywords.');
    $this
      ->editServerPartial();
    $results = $this
      ->buildSearch('test baz')
      ->execute();
    $expected = array(
      1 => 12,
      4 => 8,
    );
    $scores = array();
    foreach ($results['results'] as $item_id => $result) {
      $scores[$item_id] = $result['score'];
    }
    $this
      ->assertIdentical($scores, $expected, 'Correct scores were computed for two keywords with partial matching.');
    $results = $this
      ->buildSearch('nonexistent baz')
      ->execute();
    $this
      ->assertEqual($results['result count'], 0, 'No incorrect results returned with partial matching.');
    $query = $this
      ->buildSearch('test');
    $facets['type'] = array(
      'field' => 'type',
      'limit' => 0,
      'min_count' => 1,
      'missing' => TRUE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $this
      ->editServerPartial(FALSE);
    $expected = array(
      array(
        'count' => 2,
        'filter' => '"item"',
      ),
      array(
        'count' => 1,
        'filter' => '!',
      ),
      array(
        'count' => 1,
        'filter' => '"article"',
      ),
    );
    usort($results['search_api_facets']['type'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['type'], $expected, 'Correct facets were returned with partial matching.');

    // Regression tests for #2469547.
    $query = $this
      ->buildSearch();
    $query
      ->condition('id', 5, '<>');
    $facets['body'] = array(
      'field' => 'body',
      'limit' => 0,
      'min_count' => 1,
      'missing' => FALSE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 4,
        'filter' => '"test"',
      ),
      array(
        'count' => 1,
        'filter' => '"bar"',
      ),
      array(
        'count' => 1,
        'filter' => '"foobar"',
      ),
    );
    usort($results['search_api_facets']['body'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['body'], $expected, 'Correct facets were returned for a fulltext field.');

    // Regression tests for #2511860.
    $query = $this
      ->buildSearch();
    $query
      ->condition('body', 'ab xy');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 5, 'Fulltext filters on short words do not change the result.');
    $query = $this
      ->buildSearch();
    $query
      ->condition('body', 'ab ab');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 5, 'Fulltext filters on duplicate short words do not change the result.');

    // Regression test for #2632426.
    $query = $this
      ->buildSearch();
    $query
      ->condition('type', 'unknown_type');
    $query
      ->setOption('skip result count', TRUE);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], FALSE, 'Search for unknown type returned correct result count.');
    $this
      ->assertEqual($results['results'], array(), 'Search for unknown type returned an empty result set.');

    // Regression tests for #2566329.
    $query = $this
      ->buildSearch();
    $query
      ->condition('id', 5, '<>');
    $facets['body'] = array(
      'field' => 'body',
      'limit' => 0,
      'min_count' => 0,
      'missing' => FALSE,
    );
    $query
      ->setOption('search_api_facets', $facets);
    $query
      ->range(0, 0);
    $results = $query
      ->execute();
    $expected = array(
      array(
        'count' => 4,
        'filter' => '"test"',
      ),
      array(
        'count' => 1,
        'filter' => '"bar"',
      ),
      array(
        'count' => 1,
        'filter' => '"foobar"',
      ),
      array(
        'count' => 0,
        'filter' => '"foo"',
      ),
    );
    usort($results['search_api_facets']['body'], array(
      $this,
      'facetCompare',
    ));
    $this
      ->assertEqual($results['search_api_facets']['body'], $expected, 'Correct facets were returned for a fulltext field with minimum count 0.');
  }

  /**
   * Compares two facets for ordering.
   *
   * Used as a callback for usort() in checkFacets() and regressionTests().
   */
  public function facetCompare($a, $b) {
    if ($a['count'] != $b['count']) {
      return $b['count'] - $a['count'];
    }
    return strcasecmp($a['filter'], $b['filter']);
  }
  protected function clearIndex() {
    $success = search_api_index_load($this->index_id)
      ->clear();
    $this
      ->assertTrue($success, 'The index was successfully cleared.');
  }

  /**
   * Executes regression tests which are unpractical to run in between.
   */
  protected function regressionTests2() {

    // Regression test for #1916474.
    $index = search_api_index_load($this->index_id, TRUE);
    $index->options['fields']['prices']['type'] = 'list<decimal>';
    $success = $index
      ->save();
    $this
      ->assertTrue($success, 'The index field settings were successfully changed.');

    // Reset the internal cache so the new values will be available.
    search_api_server_load($this->server_id, TRUE);
    search_api_index_load($this->index_id, TRUE);
    $this
      ->indexItems();
    $this
      ->drupalGet('search_api_test/insert');
    $mb_string = 'äöüßáŧæøðđŋħĸµäöüßáŧæøðđŋħĸµ';
    $this
      ->insertItem(array(
      'id' => 6,
      'body' => $mb_string,
      'prices' => '3.5,3.25,3.75,3.5',
    ));
    $query = $this
      ->buildSearch(NULL, array(
      'prices,3.25',
    ));
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Filter on decimal field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      6,
    ), 'Filter on decimal field returned correct result.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch(NULL, array(
      'prices,3.5',
    ));
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Filter on decimal field returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      6,
    ), 'Filter on decimal field returned correct result.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression test for #2616804.
    // The word has 28 Unicode characters but 56 bytes. Verify that it was still
    // indexed correctly.
    $query = $this
      ->buildSearch($mb_string);
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 1, 'Search for word with 28 multi-byte characters returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      6,
    ), 'Search for word with 28 multi-byte characters returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $query = $this
      ->buildSearch($mb_string . 'ä');
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 0, 'Search for unknown word with 29 multi-byte characters returned no results.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #2745655.
    $results = $this
      ->buildSearch()
      ->condition('title', NULL)
      ->execute();

    // "Minimum chars" is 3 at this point, so all items with no longer words in
    // their titles will be returned, too.
    $this
      ->assertEqual($results['result count'], 4, 'Search for items without title returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      4,
      5,
      6,
    ), 'Search for items without title returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch()
      ->condition('title', NULL, '<>')
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Search for items with title returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
    ), 'Search for items with title returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');

    // Regression tests for #2795245.
    // Make sure changing a field's type from something else to "text" works
    // correctly.
    $index->options['fields']['type']['type'] = 'text';
    $index
      ->save();
    search_api_index_items($index);
    $results = $this
      ->buildSearch()
      ->condition('type', NULL)
      ->execute();
    $this
      ->assertEqual($results['result count'], 2, 'Search for items without type returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      3,
      6,
    ), 'Search for items without type returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
    $results = $this
      ->buildSearch()
      ->condition('type', NULL, '<>')
      ->execute();
    $this
      ->assertEqual($results['result count'], 4, 'Search for items with type returned correct number of results.');
    $this
      ->assertEqual(array_keys($results['results']), array(
      1,
      2,
      4,
      5,
    ), 'Search for items with type returned correct result.');
    $this
      ->assertEqual($results['ignored'], array(), 'No keys were ignored.');
    $this
      ->assertEqual($results['warnings'], array(), 'No warnings were displayed.');
  }

  /**
   * Tests whether removing the configuration again works as it should.
   */
  protected function uninstallModule() {

    // See whether clearing the server works.
    // Regression test for #2156151.
    $server = search_api_server_load($this->server_id, TRUE);
    $server
      ->deleteItems();
    $query = $this
      ->buildSearch();
    $results = $query
      ->execute();
    $this
      ->assertEqual($results['result count'], 0, 'Clearing the server worked correctly.');
    $table = 'search_api_db_' . $this->index_id;
    $this
      ->assertTrue(db_table_exists($table), 'The index tables were left in place.');

    // Remove first the index and then the server.
    $index = search_api_index_load($this->index_id, TRUE);
    $index
      ->update(array(
      'server' => NULL,
    ));
    $server = search_api_server_load($this->server_id, TRUE);
    $this
      ->assertEqual($server->options['indexes'], array(), 'The index was successfully removed from the server.');
    $this
      ->assertFalse(db_table_exists($table), 'The index tables were deleted.');
    $server
      ->delete();

    // Uninstall the module.
    module_disable(array(
      'search_api_db',
    ), FALSE);
    $this
      ->assertFalse(module_exists('search_api_db'), 'The Database Search module was successfully disabled.');
    drupal_uninstall_modules(array(
      'search_api_db',
    ), FALSE);
    $prefix = Database::getConnection()
      ->prefixTables('{search_api_db_}') . '%';
    $this
      ->assertEqual(db_find_tables($prefix), array(), 'The Database Search module was successfully uninstalled.');
  }

}

Classes

Namesort descending Description
SearchApiDbTest Class for testing index and search capabilities using the Database search module.