Merge pull request #16908 from glennjacobs/improve-json-nested-search

Handle Postgres nested JSON searches
This commit is contained in:
Dan Harrin
2025-07-10 10:53:32 +01:00
committed by GitHub
2 changed files with 34 additions and 13 deletions
+14 -9
View File
@@ -166,20 +166,25 @@ if (! function_exists('Filament\Support\generate_search_column_expression')) {
$column = match ($driverName) {
'pgsql' => (
str($column)->contains('->')
? str(
? (
// Handle `table.field` part with double quotes
str($column)
->beforeLast('->')
->before('->')
->explode('.')
->map(fn (string $part) => str($part)->wrap('"'))
->map(fn (string $part): string => (string) str($part)->wrap('"'))
->implode('.')
)
->append('->>')
->append("'")
->append(str($column)->afterLast('->'))
->append("'")
) . collect(str($column)->after('->')->explode('->')) // Handle JSON path parts
->map(function ($segment, $index) use ($column): string {
$totalParts = substr_count($column, '->');
return ($index === ($totalParts - 1))
? "->>'{$segment}'"
: "->'{$segment}'";
})
->implode('')
: str($column)
->explode('.')
->map(fn (string $part) => str($part)->wrap('"'))
->map(fn (string $part): string => (string) str($part)->wrap('"'))
->implode('.')
) . '::text',
default => $column,
+20 -4
View File
@@ -44,7 +44,7 @@ it('will prepare data attributes', function () {
]);
});
it('will generate json search column expression for mysql', function () {
it('will generate a JSON search column expression for MySQL', function () {
$column = 'data->name';
$isSearchForcedCaseInsensitive = true;
@@ -62,7 +62,7 @@ it('will generate json search column expression for mysql', function () {
->toBe("lower(json_extract(`data`, '$.\"name\"'))");
});
it('will generate json search column expression for pgsql', function () {
it('will generate a JSON search column expression for Postgres', function () {
$column = 'data->name';
$isSearchForcedCaseInsensitive = true;
@@ -78,7 +78,8 @@ it('will generate json search column expression for pgsql', function () {
->toBe("lower(\"data\"->>'name'::text)");
});
it('will generate column expression for pgsql with colons in the name', function (string $column, string $text) {
it('will generate a nested JSON search column expression for Postgres', function () {
$column = 'data->name->value->en';
$isSearchForcedCaseInsensitive = true;
$databaseConnection = Mockery::mock(Connection::class);
@@ -90,7 +91,22 @@ it('will generate column expression for pgsql with colons in the name', function
$expression = generate_search_column_expression($column, $isSearchForcedCaseInsensitive, $databaseConnection);
expect($expression->getValue($grammar))
->toBe($text);
->toBe("lower(\"data\"->'name'->'value'->>'en'::text)");
});
it('will generate a column expression for Postgres with colons in the table name', function (string $column, string $expectedExpression) {
$isSearchForcedCaseInsensitive = true;
$databaseConnection = Mockery::mock(Connection::class);
$databaseConnection->shouldReceive('getDriverName')->andReturn('pgsql');
$databaseConnection->shouldReceive('getConfig')->with('search_collation')->andReturn(null);
$grammar = new PostgresGrammar($databaseConnection);
$actualExpression = generate_search_column_expression($column, $isSearchForcedCaseInsensitive, $databaseConnection);
expect($actualExpression->getValue($grammar))
->toBe($expectedExpression);
})
->with([
['blog:posts.title', 'lower("blog:posts"."title"::text)'],