search_api_meilisearch-1.0.x-dev/tests/src/Kernel/Backend/QueryFilterTest.php
tests/src/Kernel/Backend/QueryFilterTest.php
<?php
namespace Drupal\Tests\search_api_meilisearch\Kernel\Backend;
use Drupal\Core\Logger\LoggerChannel;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\search_api\SearchApiException;
use PHPUnit\Framework\Attributes\Group;
/**
* Provides tests for filtered searching via search api index.
*
* @group search_api_meilisearch
*/
#[Group('search_api_meilisearch')]
class QueryFilterTest extends BackendKernelTestBase {
protected const TEST_INDEX = 'kernel_backend_filter_test_index';
/**
* Tests search with various query conditions.
*/
public function testSearchConditions(): void {
$this->prepareTestIndex(self::TEST_INDEX);
$this->populateIndex();
// Search without any conditions.
$results = $this->index->query()->range()->execute();
$this->assertCount(5, $results->getResultItems());
// Search with a condition that yields no results.
$query = $this->index->query()->range();
$query->addCondition('name', 'Test entity');
$result = $query->execute();
$this->assertEquals(0, $result->getResultCount());
// Search with a single condition.
$query = $this->index->query()->range();
$query->addCondition('name', 'Test entity 1');
$result = $query->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with a negated single condition.
$query = $this->index->query()->range();
$query->addCondition('name', 'Test entity 1', '<>');
$result = $query->execute();
$this->assertEquals(4, $result->getResultCount());
// Search with multiple conditions.
$query = $this->index->query()->range();
$conditionGroup1 = $query->createConditionGroup('OR');
$conditionGroup1->addCondition('name', 'Test entity');
$conditionGroup1->addCondition('name', 'Test entity 1');
$conditionGroup2 = $query->createConditionGroup();
$conditionGroup2->addCondition('name', 'Test entity 2');
$baseConditionGroup = $query->createConditionGroup('OR');
$baseConditionGroup
->addConditionGroup($conditionGroup1)
->addConditionGroup($conditionGroup2);
$query = $this->index->query()->range();
$query->addConditionGroup($baseConditionGroup);
$result = $query->execute();
$this->assertEquals(2, $result->getResultCount());
// Search with an integer field condition.
$query = $this->index->query()->range();
$query->addCondition('weight', 100, '<');
$result = $query->execute();
$this->assertEquals(2, $result->getResultCount());
// Search with an IN operator.
$query = $this->index->query()->range();
$query->addCondition(
'name',
['Test entity 1', 'Test entity', 'Test entity 4'],
'IN',
);
$result = $query->execute();
$this->assertEquals(2, $result->getResultCount());
// Search with a NOT IN operator.
$query = $this->index->query()->range();
$query->addCondition(
'name',
['Test entity 1', 'Test entity 4'],
'NOT IN',
);
$result = $query->execute();
$this->assertEquals(3, $result->getResultCount());
// Search with a BETWEEN operator.
$query = $this->index->query()->range();
$query->addCondition(
'weight',
[90, '103'],
'BETWEEN',
);
$result = $query->execute();
$this->assertEquals(3, $result->getResultCount());
// Search with a NOT BETWEEN operator.
$query = $this->index->query()->range();
$query->addCondition(
'weight',
[90, '103'],
'NOT BETWEEN',
);
$result = $query->execute();
$this->assertEquals(2, $result->getResultCount());
// Search with non-alphanumeric filter value.
EntityTest::create([
'name' => 'Test:entity',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', 'test:entity')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with an empty field condition.
EntityTest::create([
'name' => 'Another Test Entity',
'body' => NULL,
'weight' => 100,
])->save();
$this->index->indexItems();
$query = $this->index->query()->range();
$query->addCondition('body', NULL);
$result = $query->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with empty field excluded.
$query = $this->index->query()->range();
$query->addCondition('body', NULL, '<>');
$result = $query->execute();
$this->assertEquals(6, $result->getResultCount());
}
/**
* Tests filtering with special characters such as quotes, backslashes etc.
*/
public function testFiltersWithSpecialCharacters(): void {
$this->prepareTestIndex(self::TEST_INDEX);
// Search with filter value containing single quote, double quote and
// backslash.
EntityTest::create([
'name' => 'he":llo\\\'',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', 'he":llo\\\'')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with filter value wrapped in double quotes.
EntityTest::create([
'name' => '"test\'s entity"',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', '"test\'s entity"')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with filter value wrapped in single quotes.
EntityTest::create([
'name' => '\'test"s entity\'',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', '\'test"s entity\'')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with filter value wrapped in double quotes at beginning and
// single quotes at the end.
EntityTest::create([
'name' => '"test:entity\'',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', '"test:entity\'')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with two backslashes in filter.
EntityTest::create([
'name' => '"test:entity\\\\"',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index->query()->range()
->addCondition('name', '"test:entity\\\\"')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search filter containing only two backslashes.
EntityTest::create([
'name' => '\\\\',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index->query()->range()
->addCondition('name', '\\\\')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with one backslash at the end.
EntityTest::create([
'name' => 'test:entity\\',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', 'test:entity\\')
->execute();
$this->assertEquals(1, $result->getResultCount());
// Search with three backslashes at the end.
EntityTest::create([
'name' => 'test:entity\\\\\\',
'body' => 'This is the body of entity',
'weight' => 10,
])->save();
$this->index->indexItems();
$result = $this->index
->query()
->range()
->addCondition('name', 'test:entity\\\\\\')
->execute();
$this->assertEquals(1, $result->getResultCount());
}
/**
* Tests search throws exception with non-existing field filter.
*/
public function testFilteredSearchWithNonExistingField(): void {
$mockLogger = $this->createMock(LoggerChannel::class);
$mockLogger->expects($this->once())->method('error');
$this->container->set('logger.channel.search_api_meilisearch', $mockLogger);
$this->prepareTestIndex(self::TEST_INDEX);
$this->populateIndex();
$query = $this->index->query()->range();
$query->addCondition('non_existing_field', 'test');
$this->expectException(SearchApiException::class);
$query->execute();
}
/**
* Populates the index with entities.
*/
protected function populateIndex(): void {
foreach ([100, 99, 102, 80, 104] as $key => $weight) {
EntityTest::create([
'name' => 'Test entity ' . $key,
'body' => 'This is the body of entity' . $key,
'weight' => $weight,
])->save();
}
$this->index->indexItems();
}
/**
* {@inheritdoc}
*/
protected function getTestIndexes(): array {
return [
self::TEST_INDEX,
];
}
}
