Skip to content

Commit f354c61

Browse files
committed
2 parents 3936a1a + 534b1ae commit f354c61

File tree

8 files changed

+299
-55
lines changed

8 files changed

+299
-55
lines changed

.github/workflows/tests.yml

+2-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@ jobs:
1313
strategy:
1414
fail-fast: true
1515
matrix:
16-
php: [7.2, 7.3, 7.4, 8.0]
17-
laravel: [^6.0, ^7.0, ^8.0]
18-
exclude:
19-
- php: 7.2
20-
laravel: ^8.0
16+
php: [7.3, 7.4, 8.0]
17+
laravel: [^8.0]
2118

2219
name: P${{ matrix.php }} - L${{ matrix.laravel }}
2320

composer.json

+44-36
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,46 @@
11
{
2-
"name": "sti3bas/laravel-scout-array-driver",
3-
"description": "Array driver for Laravel Scout",
4-
"keywords": ["array", "testing", "driver", "scout", "laravel"],
5-
"license": "MIT",
6-
"authors": [{
7-
"name": "Laurynas Geigalas",
8-
"email": "[email protected]"
9-
}],
10-
"require": {
11-
"php": "^7.2|^8.0",
12-
"laravel/scout": "8.*",
13-
"illuminate/database": "^6.0|^7.0|^8.0",
14-
"illuminate/support": "^6.0|^7.0|^8.0"
15-
},
16-
"require-dev": {
17-
"phpunit/phpunit": "^8.0",
18-
"mockery/mockery": "~1.0"
19-
},
20-
"autoload": {
21-
"psr-4": {
22-
"Sti3bas\\ScoutArray\\": "src/"
23-
}
24-
},
25-
"autoload-dev": {
26-
"psr-4": {
27-
"Sti3bas\\ScoutArray\\Tests\\": "tests/"
28-
}
29-
},
30-
"extra": {
31-
"laravel": {
32-
"providers": [
33-
"Sti3bas\\ScoutArray\\ScoutArrayEngineServiceProvider"
34-
]
35-
}
36-
},
37-
"prefer-stable": true
2+
"name": "sti3bas/laravel-scout-array-driver",
3+
"description": "Array driver for Laravel Scout",
4+
"keywords": [
5+
"array",
6+
"testing",
7+
"driver",
8+
"scout",
9+
"laravel"
10+
],
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "Laurynas Geigalas",
15+
"email": "[email protected]"
16+
}
17+
],
18+
"require": {
19+
"php": "^7.3|^8.0",
20+
"laravel/scout": "^9.0",
21+
"illuminate/database": "^8.0",
22+
"illuminate/support": "^8.0"
23+
},
24+
"require-dev": {
25+
"phpunit/phpunit": "^9.3",
26+
"mockery/mockery": "~1.0"
27+
},
28+
"autoload": {
29+
"psr-4": {
30+
"Sti3bas\\ScoutArray\\": "src/"
31+
}
32+
},
33+
"autoload-dev": {
34+
"psr-4": {
35+
"Sti3bas\\ScoutArray\\Tests\\": "tests/"
36+
}
37+
},
38+
"extra": {
39+
"laravel": {
40+
"providers": [
41+
"Sti3bas\\ScoutArray\\ScoutArrayEngineServiceProvider"
42+
]
43+
}
44+
},
45+
"prefer-stable": true
3846
}

src/ArrayStore.php

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ public function forget(string $index, string $key): void
3131
$this->setData('current', $index, $this->getRecordsExcept('current', $key, $index));
3232
}
3333

34+
public function createIndex(string $index): void
35+
{
36+
$this->setData('current', $index, []);
37+
}
38+
39+
public function deleteIndex(string $index): void
40+
{
41+
$this->storage = Arr::except($this->storage, 'current.' . $index);
42+
}
43+
44+
public function indexExists(string $index): bool
45+
{
46+
return Arr::exists($this->storage['current'], $index);
47+
}
48+
3449
public function flush(string $index): void
3550
{
3651
$this->setData('current', $index, []);

src/Engines/ArrayEngine.php

+55-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
use Illuminate\Database\Eloquent\SoftDeletes;
66
use Illuminate\Support\Collection;
7+
use Illuminate\Support\LazyCollection;
78
use Laravel\Scout\Builder;
89
use Laravel\Scout\Engines\Engine;
910
use RecursiveArrayIterator;
1011
use RecursiveIteratorIterator;
12+
use Sti3bas\ScoutArray\ArrayStore;
1113

1214
class ArrayEngine extends Engine
1315
{
@@ -126,7 +128,7 @@ protected function performSearch(Builder $builder, array $options = [])
126128
return !$builder->query || stripos($value, $builder->query) !== false;
127129
}));
128130
}, true);
129-
131+
130132
$matches = Collection::make($matches);
131133

132134
return [
@@ -177,7 +179,7 @@ public function map(Builder $builder, $results, $model)
177179
if (count($results['hits']) === 0) {
178180
return $model->newCollection();
179181
}
180-
182+
181183
$objectIds = Collection::make($results['hits'])->pluck('objectID')->values()->all();
182184
$objectIdPositions = array_flip($objectIds);
183185

@@ -189,7 +191,33 @@ public function map(Builder $builder, $results, $model)
189191
})->values();
190192
}
191193

192-
194+
/**
195+
* Map the given results to instances of the given model via a lazy collection.
196+
*
197+
* @param \Laravel\Scout\Builder $builder
198+
* @param mixed $results
199+
* @param \Illuminate\Database\Eloquent\Model $model
200+
* @return \Illuminate\Support\LazyCollection
201+
*/
202+
public function lazyMap(Builder $builder, $results, $model)
203+
{
204+
if (count($results['hits']) === 0) {
205+
return LazyCollection::make($model->newCollection());
206+
}
207+
208+
$objectIds = Collection::make($results['hits'])->pluck('objectID')->values()->all();
209+
$objectIdPositions = array_flip($objectIds);
210+
211+
return $model->queryScoutModelsByIds(
212+
$builder,
213+
$objectIds
214+
)->cursor()->filter(function ($model) use ($objectIds) {
215+
return in_array($model->getScoutKey(), $objectIds);
216+
})->sortBy(function ($model) use ($objectIdPositions) {
217+
return $objectIdPositions[$model->getScoutKey()];
218+
})->values();
219+
}
220+
193221
/**
194222
* Get the total count from a raw result returned by the engine.
195223
*
@@ -213,6 +241,30 @@ public function flush($model)
213241
$this->store->flush($model->searchableAs());
214242
}
215243

244+
/**
245+
* Create a search index.
246+
*
247+
* @param string $name
248+
* @param array $options
249+
* @return mixed
250+
*/
251+
public function createIndex($name, array $options = [])
252+
{
253+
$this->store->createIndex($name);
254+
}
255+
256+
/**
257+
* Delete a search index.
258+
*
259+
* @param string $name
260+
* @return mixed
261+
*/
262+
263+
public function deleteIndex($name)
264+
{
265+
$this->store->deleteIndex($name);
266+
}
267+
216268
/**
217269
* Determine if the given model uses soft deletes.
218270
*

src/Search.php

+20
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,26 @@ public function assertNothingSyncedTo(string $index): self
202202
return $this;
203203
}
204204

205+
public function assertIndexExists($index)
206+
{
207+
Assert::assertTrue(
208+
$this->store->indexExists($index),
209+
"Failed asserting that '{$index}' search index exists."
210+
);
211+
212+
return $this;
213+
}
214+
215+
public function assertIndexNotExists($index)
216+
{
217+
Assert::assertFalse(
218+
$this->store->indexExists($index),
219+
"Failed asserting that '{$index}' search index doesn't exist."
220+
);
221+
222+
return $this;
223+
}
224+
205225
public function fakeRecord(Model $model, array $data, bool $merge = true, string $index = null): self
206226
{
207227
$this->store->mock($index ?: $model->searchableAs(), $model->getScoutKey(), $data, $merge);

tests/ArrayStoreTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -346,4 +346,33 @@ public function it_can_replace_all_record_when_mocking_a_record()
346346

347347
$this->assertEquals(['foo' => 'mocked', 'objectID' => 'key'], $store->get('test_index', 'key'));
348348
}
349+
350+
/** @test */
351+
public function it_can_create_search_index()
352+
{
353+
$store = new ArrayStore();
354+
355+
$this->assertFalse($store->indexExists('test'));
356+
357+
$store->createIndex('test');
358+
359+
$this->assertTrue($store->indexExists('test'));
360+
}
361+
362+
/** @test */
363+
public function it_can_delete_search_index()
364+
{
365+
$store = new ArrayStore();
366+
367+
$store->createIndex('test');
368+
$store->createIndex('test2');
369+
370+
$this->assertTrue($store->indexExists('test'));
371+
$this->assertTrue($store->indexExists('test2'));
372+
373+
$store->deleteIndex('test');
374+
375+
$this->assertFalse($store->indexExists('test'));
376+
$this->assertTrue($store->indexExists('test2'));
377+
}
349378
}

tests/Engines/ArrayEngineTest.php

+77-4
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
namespace Sti3bas\ScoutArray\Tests\Engines;
44

5-
use Illuminate\Support\Collection;
6-
use Illuminate\Support\Facades\Config;
7-
use Laravel\Scout\Builder;
85
use Mockery;
6+
use Laravel\Scout\Builder;
97
use PHPUnit\Framework\TestCase;
8+
use Illuminate\Support\Collection;
109
use Sti3bas\ScoutArray\ArrayStore;
10+
use Illuminate\Support\Facades\Config;
11+
use Illuminate\Support\LazyCollection;
1112
use Sti3bas\ScoutArray\Engines\ArrayEngine;
12-
use Sti3bas\ScoutArray\Tests\Fixtures\EmptySearchableModel;
1313
use Sti3bas\ScoutArray\Tests\Fixtures\SearchableModel;
14+
use Sti3bas\ScoutArray\Tests\Fixtures\EmptySearchableModel;
1415
use Sti3bas\ScoutArray\Tests\Fixtures\SoftDeletableSearchableModel;
1516

1617
class ArrayEngineTest extends TestCase
@@ -20,6 +21,15 @@ protected function setUp(): void
2021
Config::shouldReceive('get')->with('scout.after_commit', Mockery::any())->andReturn(false);
2122
}
2223

24+
protected function tearDown(): void
25+
{
26+
$this->addToAssertionCount(
27+
\Mockery::getContainer()->mockery_getExpectationCount()
28+
);
29+
30+
\Mockery::close();
31+
}
32+
2333
/** @test */
2434
public function it_can_search_for_the_records()
2535
{
@@ -346,6 +356,45 @@ public function it_returns_empty_collection_if_no_results_when_mapping()
346356
$this->assertEquals(0, count($results));
347357
}
348358

359+
/** @test */
360+
public function it_can_lazy_map_records_to_models()
361+
{
362+
$engine = new ArrayEngine(new ArrayStore());
363+
364+
$model = Mockery::mock(stdClass::class);
365+
$model->shouldReceive('queryScoutModelsByIds->cursor')->andReturn($models = LazyCollection::make([
366+
$model3 = new SearchableModel(['scoutKey' => 3]),
367+
$model1 = new SearchableModel(['scoutKey' => 1]),
368+
$model2 = new SearchableModel(['scoutKey' => 2]),
369+
]));
370+
371+
$builder = Mockery::mock(Builder::class);
372+
373+
$results = $engine->lazyMap($builder, ['nbHits' => 1, 'hits' => [
374+
['objectID' => 2],
375+
['objectID' => 1],
376+
['objectID' => 3],
377+
]], $model);
378+
379+
$this->assertEquals(3, count($results));
380+
$this->assertInstanceOf(LazyCollection::class, $results);
381+
382+
$this->assertTrue($results->all()[0]->is($model1));
383+
$this->assertTrue($results->all()[1]->is($model2));
384+
$this->assertTrue($results->all()[2]->is($model3));
385+
}
386+
387+
/** @test */
388+
public function it_returns_empty_lazy_collection_if_no_results_when_lazy_mapping()
389+
{
390+
$engine = new ArrayEngine(new ArrayStore());
391+
392+
$results = $engine->lazyMap(new Builder(new SearchableModel, ''), ['hits' => []], new SearchableModel);
393+
394+
$this->assertInstanceOf(LazyCollection::class, $results);
395+
$this->assertEquals(0, count($results));
396+
}
397+
349398
/** @test */
350399
public function it_knows_total_count()
351400
{
@@ -396,4 +445,28 @@ public function it_can_be_filtered()
396445
$this->assertCount(1, $results['hits']);
397446
$this->assertEquals(2, $results['hits'][0]['scoutKey']);
398447
}
448+
449+
/** @test */
450+
public function it_can_create_search_index()
451+
{
452+
$store = Mockery::spy(ArrayStore::class);
453+
454+
$engine = new ArrayEngine($store);
455+
456+
$engine->createIndex('test');
457+
458+
$store->shouldHaveReceived('createIndex')->with('test')->once();
459+
}
460+
461+
/** @test */
462+
public function it_can_delete_search_index()
463+
{
464+
$store = Mockery::spy(ArrayStore::class);
465+
466+
$engine = new ArrayEngine($store);
467+
468+
$engine->deleteIndex('test');
469+
470+
$store->shouldHaveReceived('deleteIndex')->with('test')->once();
471+
}
399472
}

0 commit comments

Comments
 (0)