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', ), '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'; $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.'); } }