From ce0f13bc4b3c000cdb2edab7ae7ed0c3b648b436 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 12 Nov 2024 10:46:35 +0000 Subject: [PATCH] Support whereJsonOverlaps in query builders --- src/Query/Builder.php | 42 +++++++++++ src/Query/EloquentQueryBuilder.php | 24 ++++++ src/Query/IteratorBuilder.php | 36 +++++++++ src/Stache/Query/Builder.php | 30 ++++++++ tests/Data/Assets/AssetQueryBuilderTest.php | 72 ++++++++++++++++++ tests/Data/Entries/EntryQueryBuilderTest.php | 70 ++++++++++++++++++ .../Data/Taxonomies/TermQueryBuilderTest.php | 74 +++++++++++++++++++ tests/Forms/SubmissionQueryBuilderTest.php | 74 +++++++++++++++++++ 8 files changed, 422 insertions(+) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index e9bd131737..a9d1901e98 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -314,6 +314,48 @@ public function orWhereJsonLength($column, $operator, $value = null) return $this->whereJsonLength($column, $operator, $value, 'or'); } + public function whereJsonOverlaps($column, $values, $boolean = 'and') + { + if (! is_array($values)) { + $values = [$values]; + } + + $this->wheres[] = [ + 'type' => 'JsonOverlaps', + 'column' => $column, + 'values' => $values, + 'boolean' => $boolean, + ]; + + return $this; + } + + public function orWhereJsonOverlaps($column, $values) + { + return $this->whereJsonOverlaps($column, $values, 'or'); + } + + public function whereJsonDoesntOverlap($column, $values, $boolean = 'and') + { + if (! is_array($values)) { + $values = [$values]; + } + + $this->wheres[] = [ + 'type' => 'JsonDoesntOverlap', + 'column' => $column, + 'values' => $values, + 'boolean' => $boolean, + ]; + + return $this; + } + + public function orWhereJsonDoesntOverlap($column, $values) + { + return $this->whereJsonDoesntOverlap($column, $values, 'or'); + } + public function whereNull($column, $boolean = 'and', $not = false) { $this->wheres[] = [ diff --git a/src/Query/EloquentQueryBuilder.php b/src/Query/EloquentQueryBuilder.php index 7d1ab1f75f..5c07720013 100644 --- a/src/Query/EloquentQueryBuilder.php +++ b/src/Query/EloquentQueryBuilder.php @@ -204,6 +204,30 @@ public function orWhereJsonLength($column, $operator, $value = null) return $this->whereJsonLength($column, $operator, $value, 'or'); } + public function whereJsonOverlaps($column, $values, $boolean = 'and') + { + $this->builder->whereJsonOverlaps($this->column($column), $values, $boolean); + + return $this; + } + + public function orWhereJsonOverlaps($column, $values) + { + return $this->whereJsonOverlaps($column, $values, 'or'); + } + + public function whereJsonDoesntOverlap($column, $values, $boolean = 'and') + { + $this->builder->whereJsonDoesntOverlap($this->column($column), $values, $boolean); + + return $this; + } + + public function orWhereJsonDoesntOverlap($column, $values) + { + return $this->whereJsonDoesntOverlap($column, $values, 'or'); + } + public function whereNull($column, $boolean = 'and', $not = false) { $this->builder->whereNull($this->column($column), $boolean, $not); diff --git a/src/Query/IteratorBuilder.php b/src/Query/IteratorBuilder.php index a917ba7b3d..3025b3a55e 100644 --- a/src/Query/IteratorBuilder.php +++ b/src/Query/IteratorBuilder.php @@ -2,6 +2,8 @@ namespace Statamic\Query; +use Statamic\Support\Arr; + abstract class IteratorBuilder extends Builder { protected $randomize = false; @@ -212,6 +214,40 @@ protected function filterWhereJsonLength($entries, $where) }); } + protected function filterWhereJsonOverlaps($entries, $where) + { + return $entries->filter(function ($entry) use ($where) { + $value = $this->getFilterItemValue($entry, $where['column']); + + if (is_null($value) || is_null($where['values'])) { + return false; + } + + if (! is_array($value) && ! is_array($where['values'])) { + return $value === $where['values']; + } + + return ! empty(array_intersect(Arr::wrap($value), $where['values'])); + }); + } + + protected function filterWhereJsonDoesntOverlap($entries, $where) + { + return $entries->filter(function ($entry) use ($where) { + $value = $this->getFilterItemValue($entry, $where['column']); + + if (is_null($value) || is_null($where['values'])) { + return true; + } + + if (! is_array($value) && ! is_array($where['values'])) { + return $value !== $where['values']; + } + + return empty(array_intersect(Arr::wrap($value), $where['values'])); + }); + } + protected function filterWhereDate($entries, $where) { $method = $this->operatorToCarbonMethod($where['operator']); diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index 3444923648..e4e1eabe06 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -329,6 +329,36 @@ protected function filterWhereJsonLength($values, $where) }); } + protected function filterWhereJsonOverlaps($values, $where) + { + return $values->filter(function ($value) use ($where) { + if (is_null($value) || is_null($where['values'])) { + return false; + } + + if (! is_array($value) && ! is_array($where['values'])) { + return $value === $where['values']; + } + + return ! empty(array_intersect(Arr::wrap($value), $where['values'])); + }); + } + + protected function filterWhereJsonDoesntOverlap($values, $where) + { + return $values->filter(function ($value) use ($where) { + if (is_null($value) || is_null($where['values'])) { + return true; + } + + if (! is_array($value) && ! is_array($where['values'])) { + return $value !== $where['values']; + } + + return empty(array_intersect(Arr::wrap($value), $where['values'])); + }); + } + protected function filterWhereColumn($values, $where) { $whereColumnKeys = $this->getWhereColumnKeyValuesByIndex($where['value']); diff --git a/tests/Data/Assets/AssetQueryBuilderTest.php b/tests/Data/Assets/AssetQueryBuilderTest.php index f3cdc8297e..7513bc9489 100644 --- a/tests/Data/Assets/AssetQueryBuilderTest.php +++ b/tests/Data/Assets/AssetQueryBuilderTest.php @@ -407,6 +407,78 @@ public function assets_are_found_using_or_where_json_doesnt_contain() $this->assertEquals(['a', 'c', 'b', 'd'], $assets->map->filename()->all()); } + #[Test] + public function assets_are_found_using_where_json_overlaps() + { + Asset::find('test::a.jpg')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Asset::find('test::b.txt')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Asset::find('test::c.txt')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Asset::find('test::d.jpg')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Asset::find('test::e.jpg')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $assets = $this->container->queryAssets()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1', 'taxonomy-5'])->get(); + + $this->assertCount(3, $assets); + $this->assertEquals(['a', 'c', 'e'], $assets->map->filename()->all()); + + $assets = $this->container->queryAssets()->whereJsonOverlaps('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(2, $assets); + $this->assertEquals(['a', 'c'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_are_found_using_where_json_doesnt_overlap() + { + Asset::find('test::a.jpg')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Asset::find('test::b.txt')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Asset::find('test::c.txt')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Asset::find('test::d.jpg')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Asset::find('test::e.jpg')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + Asset::find('test::f.jpg')->data(['test_taxonomy' => ['taxonomy-1']])->save(); + + $assets = $this->container->queryAssets()->whereJsonDoesntOverlap('test_taxonomy', ['taxonomy-1'])->get(); + + $this->assertCount(3, $assets); + $this->assertEquals(['b', 'd', 'e'], $assets->map->filename()->all()); + + $assets = $this->container->queryAssets()->whereJsonDoesntOverlap('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(3, $assets); + $this->assertEquals(['b', 'd', 'e'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_are_found_using_or_where_json_overlaps() + { + Asset::find('test::a.jpg')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Asset::find('test::b.txt')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Asset::find('test::c.txt')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Asset::find('test::d.jpg')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Asset::find('test::e.jpg')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $assets = $this->container->queryAssets()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonOverlaps('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(3, $assets); + $this->assertEquals(['a', 'c', 'e'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_are_found_using_or_where_json_doesnt_overlap() + { + Asset::find('test::a.jpg')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Asset::find('test::b.txt')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Asset::find('test::c.txt')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Asset::find('test::d.jpg')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Asset::find('test::e.jpg')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + Asset::find('test::f.jpg')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $assets = $this->container->queryAssets()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonDoesntOverlap('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(4, $assets); + $this->assertEquals(['a', 'c', 'b', 'd'], $assets->map->filename()->all()); + } + #[Test] public function assets_are_found_using_where_json_length() { diff --git a/tests/Data/Entries/EntryQueryBuilderTest.php b/tests/Data/Entries/EntryQueryBuilderTest.php index 75c6706dbe..ed8a301e59 100644 --- a/tests/Data/Entries/EntryQueryBuilderTest.php +++ b/tests/Data/Entries/EntryQueryBuilderTest.php @@ -475,6 +475,76 @@ public function entries_are_found_using_where_json_length() $this->assertEquals(['Post 2', 'Post 5', 'Post 4'], $entries->map->title->all()); } + #[Test] + public function entries_are_found_using_where_json_overlaps() + { + EntryFactory::id('1')->slug('post-1')->collection('posts')->data(['title' => 'Post 1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->create(); + EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'test_taxonomy' => ['taxonomy-3']])->create(); + EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->create(); + EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->create(); + EntryFactory::id('5')->slug('post-5')->collection('posts')->data(['title' => 'Post 5', 'test_taxonomy' => ['taxonomy-5']])->create(); + + $entries = Entry::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1', 'taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['Post 1', 'Post 3', 'Post 5'], $entries->map->title->all()); + + $entries = Entry::query()->whereJsonOverlaps('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(2, $entries); + $this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all()); + } + + #[Test] + public function entries_are_found_using_where_json_doesnt_overlap() + { + EntryFactory::id('1')->slug('post-1')->collection('posts')->data(['title' => 'Post 1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->create(); + EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'test_taxonomy' => ['taxonomy-3']])->create(); + EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->create(); + EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->create(); + EntryFactory::id('5')->slug('post-5')->collection('posts')->data(['title' => 'Post 5', 'test_taxonomy' => ['taxonomy-5']])->create(); + + $entries = Entry::query()->whereJsonDoesntOverlap('test_taxonomy', ['taxonomy-1'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['Post 2', 'Post 4', 'Post 5'], $entries->map->title->all()); + + $entries = Entry::query()->whereJsonDoesntOverlap('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['Post 2', 'Post 4', 'Post 5'], $entries->map->title->all()); + } + + #[Test] + public function entries_are_found_using_or_where_json_overlaps() + { + EntryFactory::id('1')->slug('post-1')->collection('posts')->data(['title' => 'Post 1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->create(); + EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'test_taxonomy' => ['taxonomy-3']])->create(); + EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->create(); + EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->create(); + EntryFactory::id('5')->slug('post-5')->collection('posts')->data(['title' => 'Post 5', 'test_taxonomy' => ['taxonomy-5']])->create(); + + $entries = Entry::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonOverlaps('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['Post 1', 'Post 3', 'Post 5'], $entries->map->title->all()); + } + + #[Test] + public function entries_are_found_using_or_where_json_doesnt_overlap() + { + EntryFactory::id('1')->slug('post-1')->collection('posts')->data(['title' => 'Post 1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->create(); + EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'test_taxonomy' => ['taxonomy-3']])->create(); + EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->create(); + EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->create(); + EntryFactory::id('5')->slug('post-5')->collection('posts')->data(['title' => 'Post 5', 'test_taxonomy' => ['taxonomy-5']])->create(); + + $entries = Entry::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonDoesntOverlap('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(4, $entries); + $this->assertEquals(['Post 1', 'Post 3', 'Post 2', 'Post 4'], $entries->map->title->all()); + } + #[Test] public function entries_are_found_using_array_of_wheres() { diff --git a/tests/Data/Taxonomies/TermQueryBuilderTest.php b/tests/Data/Taxonomies/TermQueryBuilderTest.php index b7136ede31..e1de5f2def 100644 --- a/tests/Data/Taxonomies/TermQueryBuilderTest.php +++ b/tests/Data/Taxonomies/TermQueryBuilderTest.php @@ -573,6 +573,80 @@ public function terms_are_found_using_or_where_json_doesnt_contain() $this->assertEquals(['1', '3', '2', '4'], $entries->map->slug()->all()); } + #[Test] + public function terms_are_found_using_where_json_overlaps() + { + Taxonomy::make('tags')->save(); + Term::make('1')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Term::make('2')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Term::make('3')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Term::make('4')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Term::make('5')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = Term::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1', 'taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['1', '3', '5'], $entries->map->slug()->all()); + + $entries = Term::query()->whereJsonOverlaps('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(2, $entries); + $this->assertEquals(['1', '3'], $entries->map->slug()->all()); + } + + #[Test] + public function terms_are_found_using_where_json_doesnt_overlap() + { + Taxonomy::make('tags')->save(); + Term::make('1')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Term::make('2')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Term::make('3')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Term::make('4')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Term::make('5')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = Term::query()->whereJsonDoesntOverlap('test_taxonomy', ['taxonomy-1'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['2', '4', '5'], $entries->map->slug()->all()); + + $entries = Term::query()->whereJsonDoesntOverlap('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['2', '4', '5'], $entries->map->slug()->all()); + } + + #[Test] + public function terms_are_found_using_or_where_json_overlaps() + { + Taxonomy::make('tags')->save(); + Term::make('1')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Term::make('2')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Term::make('3')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Term::make('4')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Term::make('5')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = Term::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonOverlaps('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['1', '3', '5'], $entries->map->slug()->all()); + } + + #[Test] + public function terms_are_found_using_or_where_json_doesnt_overlap() + { + Taxonomy::make('tags')->save(); + Term::make('1')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + Term::make('2')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3']])->save(); + Term::make('3')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + Term::make('4')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + Term::make('5')->taxonomy('tags')->data(['test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = Term::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonDoesntOverlap('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(4, $entries); + $this->assertEquals(['1', '3', '2', '4'], $entries->map->slug()->all()); + } + #[Test] public function terms_are_found_using_where_json_length() { diff --git a/tests/Forms/SubmissionQueryBuilderTest.php b/tests/Forms/SubmissionQueryBuilderTest.php index 51ab55718d..d9edc6d37b 100644 --- a/tests/Forms/SubmissionQueryBuilderTest.php +++ b/tests/Forms/SubmissionQueryBuilderTest.php @@ -357,6 +357,80 @@ public function submissions_are_found_using_or_where_json_doesnt_contain() $this->assertEquals(['1', '3', '2', '4'], $entries->map->get('id')->all()); } + #[Test] + public function submissions_are_found_using_where_json_overlaps() + { + $form = tap(Form::make('test'))->save(); + FormSubmission::make()->form($form)->data(['id' => '1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + FormSubmission::make()->form($form)->data(['id' => '2', 'test_taxonomy' => ['taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + FormSubmission::make()->form($form)->data(['id' => '5', 'test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = FormSubmission::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1', 'taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['1', '3', '5'], $entries->map->get('id')->all()); + + $entries = FormSubmission::query()->whereJsonOverlaps('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(2, $entries); + $this->assertEquals(['1', '3'], $entries->map->get('id')->all()); + } + + #[Test] + public function submissions_are_found_using_where_json_doesnt_overlap() + { + $form = tap(Form::make('test'))->save(); + FormSubmission::make()->form($form)->data(['id' => '1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + FormSubmission::make()->form($form)->data(['id' => '2', 'test_taxonomy' => ['taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + FormSubmission::make()->form($form)->data(['id' => '5', 'test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = FormSubmission::query()->whereJsonDoesntOverlap('test_taxonomy', ['taxonomy-1'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['2', '4', '5'], $entries->map->get('id')->all()); + + $entries = FormSubmission::query()->whereJsonDoesntOverlap('test_taxonomy', 'taxonomy-1')->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['2', '4', '5'], $entries->map->get('id')->all()); + } + + #[Test] + public function submissions_are_found_using_or_where_json_overlaps() + { + $form = tap(Form::make('test'))->save(); + FormSubmission::make()->form($form)->data(['id' => '1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + FormSubmission::make()->form($form)->data(['id' => '2', 'test_taxonomy' => ['taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + FormSubmission::make()->form($form)->data(['id' => '5', 'test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = FormSubmission::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonOverlaps('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(3, $entries); + $this->assertEquals(['1', '3', '5'], $entries->map->get('id')->all()); + } + + #[Test] + public function submissions_are_found_using_or_where_json_doesnt_overlap() + { + $form = tap(Form::make('test'))->save(); + FormSubmission::make()->form($form)->data(['id' => '1', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-2']])->save(); + FormSubmission::make()->form($form)->data(['id' => '2', 'test_taxonomy' => ['taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '3', 'test_taxonomy' => ['taxonomy-1', 'taxonomy-3']])->save(); + FormSubmission::make()->form($form)->data(['id' => '4', 'test_taxonomy' => ['taxonomy-3', 'taxonomy-4']])->save(); + FormSubmission::make()->form($form)->data(['id' => '5', 'test_taxonomy' => ['taxonomy-5']])->save(); + + $entries = FormSubmission::query()->whereJsonOverlaps('test_taxonomy', ['taxonomy-1'])->orWhereJsonDoesntOverlap('test_taxonomy', ['taxonomy-5'])->get(); + + $this->assertCount(4, $entries); + $this->assertEquals(['1', '3', '2', '4'], $entries->map->get('id')->all()); + } + #[Test] public function submissions_are_found_using_where_json_length() {