Skip to content

Commit

Permalink
fix(typesense): properly format boolean filters in Typesense (#874)
Browse files Browse the repository at this point in the history
* fix(typesense): properly format boolean filters

- Add a new function `parseFilterValue` to check for its type and
properly format values to typesense syntax
- Add unit tests for filtering

* style: fix linting errors

* fix: revert back to fallback for array filter values in where

* fix(test): fix test to align with a673914 changes
  • Loading branch information
tharropoulos authored Oct 29, 2024
1 parent c71a051 commit b8ce0fe
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 3 deletions.
25 changes: 22 additions & 3 deletions src/Engines/TypesenseEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,12 @@ public function buildSearchParameters(Builder $builder, int $page, int|null $per
protected function filters(Builder $builder): string
{
$whereFilter = collect($builder->wheres)
->map(fn ($value, $key) => $this->parseWhereFilter($value, $key))
->map(fn ($value, $key) => $this->parseWhereFilter($this->parseFilterValue($value), $key))
->values()
->implode(' && ');

$whereInFilter = collect($builder->whereIns)
->map(fn ($value, $key) => $this->parseWhereInFilter($value, $key))
->map(fn ($value, $key) => $this->parseWhereInFilter($this->parseFilterValue($value), $key))
->values()
->implode(' && ');

Expand All @@ -369,6 +369,25 @@ protected function filters(Builder $builder): string
).$whereInFilter;
}

/**
* Parse the given filter value.
*
* @param array|string|bool|int|float $value
* @return array|bool|float|int|string
*/
protected function parseFilterValue(array|string|bool|int|float $value)
{
if (is_array($value)) {
return array_map([$this, 'parseFilterValue'], $value);
}

if (gettype($value) == 'boolean') {
return $value ? 'true' : 'false';
}

return $value;
}

/**
* Create a "where" filter string.
*
Expand All @@ -392,7 +411,7 @@ protected function parseWhereFilter(array|string $value, string $key): string
*/
protected function parseWhereInFilter(array $value, string $key): string
{
return sprintf('%s:=%s', $key, '['.implode(', ', $value).']');
return sprintf('%s:=[%s]', $key, implode(', ', $value));
}

/**
Expand Down
62 changes: 62 additions & 0 deletions tests/Unit/TypesenseEngineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,68 @@ protected function tearDown(): void
m::close();
}

/**
* Call protected/private method of a class.
*
* @param object &$object Instantiated object that we will run method on.
* @param string $methodName Method name to call
* @param array $parameters Array of parameters to pass into method.
* @return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = [])
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);

return $method->invokeArgs($object, $parameters);
}

public function test_filters_method()
{
$builder = m::mock(Builder::class);
$builder->wheres = [
'status' => 'active',
'age' => 25,
];
$builder->whereIns = [
'category' => ['electronics', 'books'],
];

$result = $this->invokeMethod($this->engine, 'filters', [$builder]);

$expected = 'status:=active && age:=25 && category:=[electronics, books]';
$this->assertEquals($expected, $result);
}

public function test_parse_filter_value_method()
{
$this->assertEquals('true', $this->invokeMethod($this->engine, 'parseFilterValue', [true]));
$this->assertEquals('false', $this->invokeMethod($this->engine, 'parseFilterValue', [false]));
$this->assertEquals('25', $this->invokeMethod($this->engine, 'parseFilterValue', [25]));
$this->assertEquals('3.14', $this->invokeMethod($this->engine, 'parseFilterValue', [3.14]));
$this->assertEquals('test', $this->invokeMethod($this->engine, 'parseFilterValue', ['test']));
$this->assertEquals('test "quoted"', $this->invokeMethod($this->engine, 'parseFilterValue', ['test "quoted"']));
$this->assertEquals('`special value`', $this->invokeMethod($this->engine, 'parseFilterValue', ['`special value`']));

$nestedArray = ['a', ['b', 'c'], 'd'];
$expectedNested = ['a', ['b', 'c'], 'd'];
$this->assertEquals($expectedNested, $this->invokeMethod($this->engine, 'parseFilterValue', [$nestedArray]));
}

public function test_parse_where_filter_method()
{
$this->assertEquals('status:=active', $this->invokeMethod($this->engine, 'parseWhereFilter', ['active', 'status']));
$this->assertEquals('age:=25', $this->invokeMethod($this->engine, 'parseWhereFilter', ['25', 'age']));
$this->assertEquals('tags:tag1tag2tag3', $this->invokeMethod($this->engine, 'parseWhereFilter', [['tag1', 'tag2', 'tag3'], 'tags']));
}

public function test_parse_where_in_filter_method()
{
$this->assertEquals('category:=[electronics, books]', $this->invokeMethod($this->engine, 'parseWhereInFilter', [['electronics', 'books'], 'category']));
$this->assertEquals('id:=[1, 2, 3]', $this->invokeMethod($this->engine, 'parseWhereInFilter', [[1, 2, 3], 'id']));
}

public function test_update_method(): void
{
// Mock models and their methods
Expand Down

0 comments on commit b8ce0fe

Please sign in to comment.