search_api_meilisearch-1.0.x-dev/tests/src/Kernel/Parser/StringExpressionFilterParserTest.php
tests/src/Kernel/Parser/StringExpressionFilterParserTest.php
<?php
namespace Drupal\Tests\search_api_meilisearch\Kernel\Parser;
use Drupal\KernelTests\KernelTestBase;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\Query\ConditionGroup;
use Drupal\search_api\Query\ConditionGroupInterface;
use Drupal\search_api_meilisearch\Parser\FilterParserInterface;
use PHPUnit\Framework\Attributes\Group;
/**
* Provides tests for string expression filter parser.
*
* @group search_api_meilisearch
*/
#[Group('search_api_meilisearch')]
class StringExpressionFilterParserTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'search_api',
'search_api_meilisearch',
];
/**
* The parser service.
*
* @var \Drupal\search_api_meilisearch\Parser\FilterParserInterface
*/
protected FilterParserInterface $parser;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->parser = $this->container->get('search_api_meilisearch.filter_parser');
}
/**
* Tests parsing string expression filters from condition groups.
*/
public function testParsingConditionGroups(): void {
// AND group without conditions.
$this->assertConditionGroupParsed(new ConditionGroup(), '');
// AND group with sub condition group without conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addConditionGroup(new ConditionGroup()),
''
);
// AND group with one condition.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addCondition('name', 'test'),
'name = "test"'
);
// AND group with one condition value with spaces.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addCondition('name', 'a test value'),
'name = "a test value"'
);
// AND group with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addCondition('name', 'test')
->addCondition('body', 'test'),
'name = "test" AND body = "test"'
);
// OR group with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup('OR'))
->addCondition('name', 'test')
->addCondition('body', 'test'),
'name = "test" OR body = "test"'
);
// AND group with AND subgroup with one condition.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addConditionGroup(
(new ConditionGroup())->addCondition('name', 'test')
),
'name = "test"'
);
// AND group with OR subgroup with one condition.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addConditionGroup(
(new ConditionGroup('OR'))->addCondition('name', 'test')
),
'name = "test"'
);
// AND group with AND subgroup with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test')
->addCondition('body', 'test')
),
'name = "test" AND body = "test"'
);
// AND group with OR subgroup with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test')
->addCondition('body', 'test')
),
'name = "test" OR body = "test"'
);
// AND group with two AND subgroups each with one condition.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addConditionGroup((new ConditionGroup())->addCondition('name', 'test'))
->addConditionGroup((new ConditionGroup())->addCondition('body', 'test')),
'name = "test" AND body = "test"'
);
// OR group with two AND subgroups each with one condition.
$this->assertConditionGroupParsed(
(new ConditionGroup('OR'))
->addConditionGroup((new ConditionGroup())->addCondition('name', 'test'))
->addConditionGroup((new ConditionGroup())->addCondition('body', 'test')),
'name = "test" OR body = "test"'
);
// AND group with two AND subgroups each with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test')
->addCondition('body', 'test')
)
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test1')
->addCondition('body', 'test1')
),
'name = "test" AND body = "test" AND name = "test1" AND body = "test1"'
);
// AND group with two OR subgroups each with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test')
->addCondition('body', 'test')
)
->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test1')
->addCondition('body', 'test1')
),
'(name = "test" OR body = "test") AND (name = "test1" OR body = "test1")'
);
// OR group with two AND subgroups each with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup('OR'))
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test')
->addCondition('body', 'test')
)
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test1')
->addCondition('body', 'test1')
),
'name = "test" AND body = "test" OR name = "test1" AND body = "test1"'
);
// AND group with one condition and two AND subgroups each with two
// conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addCondition('name', 'test_')
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test')
->addCondition('body', 'test')
)
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test1')
->addCondition('body', 'test1')
),
'name = "test_" AND name = "test" AND body = "test" AND name = "test1" AND body = "test1"'
);
// Complex deeply nested group.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addCondition('name', 'test test')
->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test')
->addConditionGroup(
(new ConditionGroup())
->addCondition('count', 5, '>=')
->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test1')
->addCondition('body', 'test1')
)
)
)
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test2')
->addCondition('body', 'test2')
->addCondition('id', [1, 3], 'IN')
),
'name = "test test" AND (name = "test" OR count >= 5 AND (name = "test1" OR body = "test1")) AND name = "test2" AND body = "test2" AND (id = 1 OR id = 3)'
);
// Deeply nested group with single condition.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addConditionGroup(
(new ConditionGroup())
->addConditionGroup(
(new ConditionGroup())
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test')
)
)
),
'name = "test"'
);
// Condition group mixed with condition and subgroup.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addCondition('name', 'test')
->addConditionGroup(
(new ConditionGroup('OR'))
->addCondition('name', 'test1')
->addCondition('name', 'test2')
),
'name = "test" AND (name = "test1" OR name = "test2")'
);
// OR group with a condition with AND subgroup with two conditions.
$this->assertConditionGroupParsed(
(new ConditionGroup('OR'))
->addCondition('name', 'test')
->addConditionGroup(
(new ConditionGroup())
->addCondition('name', 'test1')
->addCondition('name', 'test2')
),
'name = "test" OR name = "test1" AND name = "test2"'
);
// AND group with two conditions, one condition with IN operator.
$this->assertConditionGroupParsed(
(new ConditionGroup())
->addCondition('name', ['test1', 'test2', 'test3'], 'IN')
->addCondition('body', 'test body'),
'(name = "test1" OR name = "test2" OR name = "test3") AND body = "test body"'
);
}
/**
* Tests parsing filters with various operators in condition group.
*/
public function testParsingOperators(): void {
// Greater than operator.
$this->assertOperatorParsed('count', '>', '0', 'count > 0');
// Greater than equal operator.
$this->assertOperatorParsed('count', '>=', 0, 'count >= 0');
// Less than operator.
$this->assertOperatorParsed('count', '<', '0', 'count < 0');
// Less than equal operator.
$this->assertOperatorParsed('count', '<=', '0', 'count <= 0');
// Not equal operator.
$this->assertOperatorParsed('count', '<>', '0', 'count != 0');
// BETWEEN operator.
$this->assertOperatorParsed('count', 'BETWEEN', [0, 10], 'count 0 TO 10');
// NOT BETWEEN operator.
$this->assertOperatorParsed('count', 'NOT BETWEEN', [0, 10], '(count < 0 OR count > 10)');
// IN operator.
$this->assertOperatorParsed('id', 'IN', [1, 3, 4], '(id = 1 OR id = 3 OR id = 4)');
// IN operator multi word.
$this->assertOperatorParsed(
'category',
'IN',
['world today', 'politics', 'sport'],
'(category = "world today" OR category = "politics" OR category = "sport")'
);
// NOT IN operator.
$this->assertOperatorParsed('id', 'NOT IN', [1, 3, 4], '(id != 1 AND id != 3 AND id != 4)');
// Is null operator.
$this->assertOperatorParsed('body', '=', NULL, "body NOT EXISTS");
// Is not null operator.
$this->assertOperatorParsed('body', '<>', NULL, "body EXISTS");
}
/**
* Tests parsing filters with boolean field operator.
*/
public function testBooleanOperator(): void {
$index = Index::create([
'id' => 'test',
'field_settings' => [
'field_boolean' => [
'type' => 'boolean',
],
],
]);
$conditionGroup = new ConditionGroup();
$conditionGroup->addCondition('field_boolean', TRUE, '=');
$conditionGroup->addCondition('field_boolean', '1', '=');
$conditionGroup->addCondition('field_boolean', 1, '=');
$result = $this->parser->parseExpression($conditionGroup, $index);
$expected = 'field_boolean = "true" AND field_boolean = "true" AND field_boolean = "true"';
$this->assertEquals($expected, $result);
$conditionGroup = new ConditionGroup();
$conditionGroup->addCondition('field_boolean', FALSE, '=');
$conditionGroup->addCondition('field_boolean', '0', '=');
$conditionGroup->addCondition('field_boolean', 0, '=');
$result = $this->parser->parseExpression($conditionGroup, $index);
$expected = 'field_boolean = "false" AND field_boolean = "false" AND field_boolean = "false"';
$this->assertEquals($expected, $result);
}
/**
* Parses the condition group and asserts the result.
*
* @param \Drupal\search_api\Query\ConditionGroupInterface $conditionGroup
* The condition group to parse.
* @param string $expected
* The expected result.
*/
protected function assertConditionGroupParsed(ConditionGroupInterface $conditionGroup, string $expected): void {
$result = $this->parser->parseExpression($conditionGroup, Index::create(['id' => 'test']));
$this->assertEquals($expected, $result);
}
/**
* Parses operator and asserts the result.
*
* @param string $field
* The field name.
* @param string $operator
* The condition operator.
* @param string|array $value
* The field value.
* @param string $expected
* Expected filter expression.
*/
protected function assertOperatorParsed(string $field, string $operator, $value, string $expected) {
$conditionGroup = new ConditionGroup();
$conditionGroup->addCondition($field, $value, $operator);
$result = $this->parser->parseExpression($conditionGroup, Index::create(['id' => 'test']));
$this->assertEquals($expected, $result);
}
}
