Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bd218ac

Browse files
committed
Set typemap on cursor, only root._id is mapped
1 parent 22f40a0 commit bd218ac

File tree

3 files changed

+53
-35
lines changed

3 files changed

+53
-35
lines changed

‎src/Scout/ScoutEngine.php‎

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ final class ScoutEngine extends Engine
6666
],
6767
];
6868

69+
private const TYPEMAP = ['root' => 'object', 'document' => 'bson', 'array' => 'bson'];
70+
6971
public function __construct(
7072
private Database $database,
7173
private bool $softDelete,
@@ -96,28 +98,42 @@ public function update($models)
9698

9799
$bulk = [];
98100
foreach ($models as $model) {
101+
assert($model instanceof Model && method_exists($model, 'toSearchableArray'), new LogicException(sprintf('Model "%s" must use "%s" trait', $model::class, Searchable::class)));
102+
99103
$searchableData = $model->toSearchableArray();
100104
$searchableData = self::serialize($searchableData);
101105

102-
if ($searchableData) {
103-
unset($searchableData['_id']);
104-
105-
$searchableData = array_merge($searchableData, $model->scoutMetadata());
106-
if (isset($searchableData['__soft_deleted'])) {
107-
$searchableData['__soft_deleted'] = (bool) $searchableData['__soft_deleted'];
108-
}
109-
106+
// Skip/remove the model if it doesn't provide any searchable data
107+
if (! $searchableData) {
110108
$bulk[] = [
111-
'updateOne' => [
109+
'deleteOne' => [
112110
['_id' => $model->getScoutKey()],
113-
[
114-
'$setOnInsert' => ['_id' => $model->getScoutKey()],
115-
'$set' => $searchableData,
116-
],
117-
['upsert' => true],
118111
],
119112
];
113+
114+
continue;
120115
}
116+
117+
unset($searchableData['_id']);
118+
119+
$searchableData = array_merge($searchableData, $model->scoutMetadata());
120+
121+
/** Convert the __soft_deleted set by {@see Searchable::pushSoftDeleteMetadata()}
122+
* into a boolean for efficient storage and indexing. */
123+
if (isset($searchableData['__soft_deleted'])) {
124+
$searchableData['__soft_deleted'] = (bool) $searchableData['__soft_deleted'];
125+
}
126+
127+
$bulk[] = [
128+
'updateOne' => [
129+
['_id' => $model->getScoutKey()],
130+
[
131+
'$setOnInsert' => ['_id' => $model->getScoutKey()],
132+
'$set' => $searchableData,
133+
],
134+
['upsert' => true],
135+
],
136+
];
121137
}
122138

123139
$this->getIndexableCollection($models)->bulkWrite($bulk);
@@ -133,7 +149,7 @@ public function update($models)
133149
#[Override]
134150
public function delete($models): void
135151
{
136-
assert($models instanceof Collection, new TypeError(sprintf('Argument #1 ($models) must be of type %s, %s given', Collection::class, get_debug_type($models))));
152+
assert($models instanceof EloquentCollection, new TypeError(sprintf('Argument #1 ($models) must be of type %s, %s given', Collection::class, get_debug_type($models))));
137153

138154
if ($models->isEmpty()) {
139155
return;
@@ -149,7 +165,7 @@ public function delete($models): void
149165
*
150166
* @see Engine::search()
151167
*
152-
* @return mixed
168+
* @return array
153169
*/
154170
#[Override]
155171
public function search(Builder $builder)
@@ -158,14 +174,14 @@ public function search(Builder $builder)
158174
}
159175

160176
/**
161-
* Perform the given search on the engine.
177+
* Perform the given search on the engine with pagination.
162178
*
163179
* @see Engine::paginate()
164180
*
165181
* @param int $perPage
166182
* @param int $page
167183
*
168-
* @return mixed
184+
* @return array
169185
*/
170186
#[Override]
171187
public function paginate(Builder $builder, $perPage, $page)
@@ -182,20 +198,21 @@ public function paginate(Builder $builder, $perPage, $page)
182198
/**
183199
* Perform the given search on the engine.
184200
*/
185-
protected function performSearch(Builder $builder, ?int $offset = null): array
201+
private function performSearch(Builder $builder, ?int $offset = null): array
186202
{
187203
$collection = $this->getSearchableCollection($builder->model);
188204

189205
if ($builder->callback) {
190-
$result = call_user_func(
206+
$cursor = call_user_func(
191207
$builder->callback,
192208
$collection,
193209
$builder->query,
194210
$offset,
195211
);
196-
assert($result instanceof CursorInterface, new LogicException(sprintf('The search builder closure must return a MongoDB cursor, %s returned', get_debug_type($result))));
212+
assert($cursor instanceof CursorInterface, new LogicException(sprintf('The search builder closure must return a MongoDB cursor, %s returned', get_debug_type($cursor))));
213+
$cursor->setTypeMap(self::TYPEMAP);
197214

198-
return $result->toArray();
215+
return $cursor->toArray();
199216
}
200217

201218
$compound = [
@@ -270,11 +287,10 @@ protected function performSearch(Builder $builder, ?int $offset = null): array
270287
$pipeline[] = ['$limit' => $builder->limit];
271288
}
272289

273-
$options = [
274-
'typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array'],
275-
];
290+
$cursor = $collection->aggregate($pipeline);
291+
$cursor->setTypeMap(self::TYPEMAP);
276292

277-
return $collection->aggregate($pipeline, $options)->toArray();
293+
return $cursor->toArray();
278294
}
279295

280296
/**
@@ -539,6 +555,6 @@ private function wait(Closure $callback): void
539555
sleep(1);
540556
}
541557

542-
throw new MongoDBRuntimeException('Atlas search index operation time out');
558+
throw new MongoDBRuntimeException(sprintf('Atlas search index operation time out after %s seconds', self::WAIT_TIMEOUT_SEC));
543559
}
544560
}

‎tests/AtlasSearchTest.php‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414

1515
use function array_map;
1616
use function assert;
17-
1817
use function mt_getrandmax;
1918
use function rand;
2019
use function range;
21-
use function srand;
2220
use function sort;
21+
use function srand;
2322
use function usleep;
2423
use function usort;
2524

‎tests/Scout/ScoutEngineTest.php‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
/** Unit tests that do not require an Atlas Search cluster */
2828
class ScoutEngineTest extends TestCase
2929
{
30-
private const EXPECTED_SEARCH_OPTIONS = ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']];
30+
private const EXPECTED_TYPEMAP = ['root' => 'object', 'document' => 'bson', 'array' => 'bson'];
3131

3232
/** @param callable(): Builder $builder */
3333
#[DataProvider('provideSearchPipelines')]
@@ -40,13 +40,16 @@ public function testSearch(Closure $builder, array $expectedPipeline): void
4040
->with('collection_searchable')
4141
->andReturn($collection);
4242
$cursor = m::mock(CursorInterface::class);
43+
$cursor->shouldReceive('setTypeMap')->once()->with(self::EXPECTED_TYPEMAP);
4344
$cursor->shouldReceive('toArray')->once()->with()->andReturn($data);
4445

46+
$collection->shouldReceive('getCollectionName')
47+
->zeroOrMoreTimes()
48+
->andReturn('collection_searchable');
4549
$collection->shouldReceive('aggregate')
4650
->once()
47-
->withArgs(function ($pipeline, $options) use ($expectedPipeline) {
51+
->withArgs(function ($pipeline) use ($expectedPipeline) {
4852
self::assertEquals($expectedPipeline, $pipeline);
49-
self::assertEquals(self::EXPECTED_SEARCH_OPTIONS, $options);
5053

5154
return true;
5255
})
@@ -295,10 +298,11 @@ function () {
295298
fn () => new Builder(new SearchableModel(), 'query', callback: function (...$args) {
296299
$this->assertCount(3, $args);
297300
$this->assertInstanceOf(Collection::class, $args[0]);
301+
$this->assertSame('collection_searchable', $args[0]->getCollectionName());
298302
$this->assertSame('query', $args[1]);
299303
$this->assertNull($args[2]);
300304

301-
return $args[0]->aggregate(['pipeline'], self::EXPECTED_SEARCH_OPTIONS);
305+
return $args[0]->aggregate(['pipeline']);
302306
}),
303307
['pipeline'],
304308
];
@@ -383,11 +387,10 @@ public function testPaginate()
383387
],
384388
], $args[0]);
385389

386-
$this->assertSame(self::EXPECTED_SEARCH_OPTIONS, $args[1]);
387-
388390
return true;
389391
})
390392
->andReturn($cursor);
393+
$cursor->shouldReceive('setTypeMap')->once()->with(self::EXPECTED_TYPEMAP);
391394
$cursor->shouldReceive('toArray')
392395
->once()
393396
->with()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /