Skip to content

Commit

Permalink
Support whereJsonOverlaps,orWhereJsonOverlaps and `whereJsonDoesn…
Browse files Browse the repository at this point in the history
…tOverlap` to `\Hyperf\Database\Query\Builder` (#6782)
  • Loading branch information
zds-s authored May 23, 2024
1 parent 92f613c commit 0a62a12
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,46 @@ public function orWhereJsonDoesntContain($column, $value)
return $this->whereJsonDoesntContain($column, $value, 'or');
}

/**
* Add a "where JSON overlaps" clause to the query.
*/
public function whereJsonOverlaps(string $column, mixed $value, string $boolean = 'and', bool $not = false): static
{
$type = 'JsonOverlaps';

$this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not');

if (! $value instanceof Expression) {
$this->addBinding($this->grammar->prepareBindingForJsonContains($value));
}

return $this;
}

/**
* Add an "or where JSON overlaps" clause to the query.
*/
public function orWhereJsonOverlaps(string $column, mixed $value): static
{
return $this->whereJsonOverlaps($column, $value, 'or');
}

/**
* Add a "where JSON not overlap" clause to the query.
*/
public function whereJsonDoesntOverlap(string $column, mixed $value, string $boolean = 'and'): static
{
return $this->whereJsonOverlaps($column, $value, $boolean, true);
}

/**
* Add an "or where JSON not overlap" clause to the query.
*/
public function orWhereJsonDoesntOverlap(string $column, mixed $value): static
{
return $this->whereJsonDoesntOverlap($column, $value, 'or');
}

/**
* Add a "where JSON length" clause to the query.
*
Expand Down
21 changes: 21 additions & 0 deletions src/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,27 @@ public function compileJoinLateral(JoinLateralClause $join, string $expression):
throw new RuntimeException('This database engine does not support lateral joins.');
}

/**
* Compile a "where JSON overlaps" clause.
*/
protected function whereJsonOverlaps(Builder $query, array $where): string
{
$not = $where['not'] ? 'not ' : '';

return $not . $this->compileJsonOverlaps(
$where['column'],
$this->parameter($where['value'])
);
}

/**
* Compile a "JSON overlaps" statement into SQL.
*/
protected function compileJsonOverlaps(string $column, string $value): string
{
throw new RuntimeException('This database engine does not support JSON overlaps operations.');
}

/**
* Compile the components necessary for a select clause.
*/
Expand Down
10 changes: 10 additions & 0 deletions src/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ protected function compileJsonContains($column, $value): string
return 'json_contains(' . $field . ', ' . $value . $path . ')';
}

/**
* Compile a "JSON overlaps" statement into SQL.
*/
protected function compileJsonOverlaps(string $column, string $value): string
{
[$field, $path] = $this->wrapJsonFieldAndPath($column);

return 'json_overlaps(' . $field . ', ' . $value . $path . ')';
}

/**
* Compile a "JSON length" statement into SQL.
*
Expand Down
31 changes: 31 additions & 0 deletions tests/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,37 @@ public function testOrWhereAny()
$this->assertEquals(['%Taylor%', '%Otwell%', '%Otwell%'], $builder->getBindings());
}

public function testWhereJsonOverlapsMySql(): void
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereJsonOverlaps('options', ['en', 'fr']);
$this->assertSame('select * from `users` where json_overlaps(`options`, ?)', $builder->toSql());
$this->assertEquals(['["en","fr"]'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereJsonOverlaps('users.options->languages', ['en', 'fr']);
$this->assertSame('select * from `users` where json_overlaps(`users`.`options`, ?, \'$."languages"\')', $builder->toSql());
$this->assertEquals(['["en","fr"]'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonOverlaps('options->languages', new Raw("'[\"en\", \"fr\"]'"));
$this->assertSame('select * from `users` where `id` = ? or json_overlaps(`options`, \'["en", "fr"]\', \'$."languages"\')', $builder->toSql());
$this->assertEquals([1], $builder->getBindings());
}

public function testWhereJsonDoesntOverlapMySql(): void
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereJsonDoesntOverlap('options->languages', ['en', 'fr']);
$this->assertSame('select * from `users` where not json_overlaps(`options`, ?, \'$."languages"\')', $builder->toSql());
$this->assertEquals(['["en","fr"]'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonDoesntOverlap('options->languages', new Raw("'[\"en\", \"fr\"]'"));
$this->assertSame('select * from `users` where `id` = ? or not json_overlaps(`options`, \'["en", "fr"]\', \'$."languages"\')', $builder->toSql());
$this->assertEquals([1], $builder->getBindings());
}

public function testJoinLateral()
{
$builder = $this->getMySqlBuilder();
Expand Down

0 comments on commit 0a62a12

Please sign in to comment.