Skip to content

Commit 9b45a42

Browse files
Add "whereNotIn" support to Meilisearch, Database and Collection engines. (#760)
1 parent 01790b8 commit 9b45a42

File tree

5 files changed

+84
-9
lines changed

5 files changed

+84
-9
lines changed

src/Builder.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ class Builder
6262
*/
6363
public $whereIns = [];
6464

65+
/**
66+
* The "where not in" constraints added to the query.
67+
*
68+
* @var array
69+
*/
70+
public $whereNotIns = [];
71+
6572
/**
6673
* The "limit" that should be applied to the search.
6774
*
@@ -144,6 +151,20 @@ public function whereIn($field, array $values)
144151
return $this;
145152
}
146153

154+
/**
155+
* Add a "where not in" constraint to the search query.
156+
*
157+
* @param string $field
158+
* @param array $values
159+
* @return $this
160+
*/
161+
public function whereNotIn($field, array $values)
162+
{
163+
$this->whereNotIns[$field] = $values;
164+
165+
return $this;
166+
}
167+
147168
/**
148169
* Include soft deleted records in the results.
149170
*

src/Engines/CollectionEngine.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ protected function searchModels(Builder $builder)
100100
$query->whereIn($key, $values);
101101
}
102102
})
103+
->when(! $builder->callback && count($builder->whereNotIns) > 0, function ($query) use ($builder) {
104+
foreach ($builder->whereNotIns as $key => $values) {
105+
$query->whereNotIn($key, $values);
106+
}
107+
})
103108
->when($builder->orders, function ($query) use ($builder) {
104109
foreach ($builder->orders as $order) {
105110
$query->orderBy($order['column'], $order['direction']);

src/Engines/DatabaseEngine.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ protected function addAdditionalConstraints(Builder $builder, $query)
249249
foreach ($builder->whereIns as $key => $values) {
250250
$query->whereIn($key, $values);
251251
}
252+
})->when(! $builder->callback && count($builder->whereNotIns) > 0, function ($query) use ($builder) {
253+
foreach ($builder->whereNotIns as $key => $values) {
254+
$query->whereNotIn($key, $values);
255+
}
252256
})->when(! is_null($builder->queryCallback), function ($query) use ($builder) {
253257
call_user_func($builder->queryCallback, $query);
254258
});

src/Engines/MeilisearchEngine.php

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,25 @@ protected function filters(Builder $builder)
186186
: sprintf('%s="%s"', $key, $value);
187187
});
188188

189-
foreach ($builder->whereIns as $key => $values) {
190-
$filters->push(sprintf('%s IN [%s]', $key, collect($values)->map(function ($value) {
191-
if (is_bool($value)) {
192-
return sprintf('%s', $value ? 'true' : 'false');
189+
$whereInOperators = [
190+
'whereIns' => 'IN',
191+
'whereNotIns' => 'NOT IN'
192+
];
193+
194+
foreach ($whereInOperators as $property => $operator) {
195+
if (property_exists($builder, $property)) {
196+
foreach ($builder->{$property} as $key => $values) {
197+
$filters->push(sprintf('%s %s [%s]', $key, $operator, collect($values)->map(function ($value) {
198+
if (is_bool($value)) {
199+
return sprintf('%s', $value ? 'true' : 'false');
200+
}
201+
202+
return filter_var($value, FILTER_VALIDATE_INT) !== false
203+
? sprintf('%s', $value)
204+
: sprintf('"%s"', $value);
205+
})->values()->implode(', ')));
193206
}
194-
195-
return filter_var($value, FILTER_VALIDATE_INT) !== false
196-
? sprintf('%s', $value)
197-
: sprintf('"%s"', $value);
198-
})->values()->implode(', ')));
207+
}
199208
}
200209

201210
return $filters->values()->implode(' AND ');

tests/Unit/MeilisearchEngineTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,25 @@ public function test_where_in_conditions_are_applied()
541541
$engine->search($builder);
542542
}
543543

544+
public function test_where_not_in_conditions_are_applied()
545+
{
546+
$builder = new Builder(new SearchableModel(), '');
547+
$builder->where('foo', 'bar');
548+
$builder->where('bar', 'baz');
549+
$builder->whereIn('qux', [1, 2]);
550+
$builder->whereIn('quux', [1, 2]);
551+
$builder->whereNotIn('eaea', [3]);
552+
$client = m::mock(Client::class);
553+
$client->shouldReceive('index')->once()->andReturn($index = m::mock(Indexes::class));
554+
$index->shouldReceive('rawSearch')->once()->with($builder->query, array_filter([
555+
'filter' => 'foo="bar" AND bar="baz" AND qux IN [1, 2] AND quux IN [1, 2] AND eaea NOT IN [3]',
556+
'hitsPerPage' => $builder->limit,
557+
]))->andReturn([]);
558+
559+
$engine = new MeilisearchEngine($client);
560+
$engine->search($builder);
561+
}
562+
544563
public function test_where_in_conditions_are_applied_without_other_conditions()
545564
{
546565
$builder = new Builder(new SearchableModel(), '');
@@ -557,6 +576,23 @@ public function test_where_in_conditions_are_applied_without_other_conditions()
557576
$engine->search($builder);
558577
}
559578

579+
public function test_where_not_in_conditions_are_applied_without_other_conditions()
580+
{
581+
$builder = new Builder(new SearchableModel(), '');
582+
$builder->whereIn('qux', [1, 2]);
583+
$builder->whereIn('quux', [1, 2]);
584+
$builder->whereNotIn('eaea', [3]);
585+
$client = m::mock(Client::class);
586+
$client->shouldReceive('index')->once()->andReturn($index = m::mock(Indexes::class));
587+
$index->shouldReceive('rawSearch')->once()->with($builder->query, array_filter([
588+
'filter' => 'qux IN [1, 2] AND quux IN [1, 2] AND eaea NOT IN [3]',
589+
'hitsPerPage' => $builder->limit,
590+
]))->andReturn([]);
591+
592+
$engine = new MeilisearchEngine($client);
593+
$engine->search($builder);
594+
}
595+
560596
public function test_empty_where_in_conditions_are_applied_correctly()
561597
{
562598
$builder = new Builder(new SearchableModel(), '');

0 commit comments

Comments
 (0)