|
20 | 20 | use MongoDB\Database;
|
21 | 21 | use MongoDB\Driver\Cursor;
|
22 | 22 | use MongoDB\Driver\CursorInterface;
|
23 |
| -use MongoDB\Driver\Exception\ServerException; |
24 | 23 | use MongoDB\Exception\Exception;
|
25 | 24 | use MongoDB\Exception\RuntimeException;
|
| 25 | +use MongoDB\Laravel\Connection; |
26 | 26 | use Traversable;
|
27 | 27 | use TypeError;
|
28 | 28 |
|
@@ -186,12 +186,20 @@ protected function performSearch(Builder $builder, ?int $offset = null): array
|
186 | 186 | }
|
187 | 187 |
|
188 | 188 | $compound = [
|
189 |
| - 'must' => [ |
| 189 | + 'should' => [ |
190 | 190 | [
|
191 | 191 | 'text' => [
|
192 | 192 | 'query' => $builder->query,
|
193 | 193 | 'path' => ['wildcard' => '*'],
|
194 | 194 | 'fuzzy' => ['maxEdits' => 2],
|
| 195 | + 'score' => ['boost' => ['value' => 5]], |
| 196 | + ], |
| 197 | + ], |
| 198 | + [ |
| 199 | + 'wildcard' => [ |
| 200 | + 'query' => $builder->query . '*', |
| 201 | + 'path' => ['wildcard' => '*'], |
| 202 | + 'allowAnalyzedField' => true, |
195 | 203 | ],
|
196 | 204 | ],
|
197 | 205 | ],
|
@@ -362,40 +370,41 @@ public function lazyMap(Builder $builder, $results, $model): LazyCollection
|
362 | 370 | * Accepted options:
|
363 | 371 | * - wait: bool, default true. Wait for the index to be created.
|
364 | 372 | *
|
365 |
| - * @param string $name Collection name |
| 373 | + * @param string> $name Collection name |
366 | 374 | * @param array{wait?:bool} $options
|
367 | 375 | */
|
368 | 376 | public function createIndex($name, array $options = []): void
|
369 | 377 | {
|
370 | 378 | assert(is_string($name), new TypeError(sprintf('Argument #1 ($name) must be of type string, %s given', get_debug_type($name))));
|
371 | 379 |
|
372 |
| - $this->performSearchIndexOperation(function () use ($name, $options) { |
373 |
| - $this->mongodb->createCollection($name); |
374 |
| - $collection = $this->mongodb->selectCollection($name); |
375 |
| - /** @todo accept configuration for the mapping */ |
376 |
| - $collection->createSearchIndex( |
377 |
| - $options['definition'] ?? self::DEFAULT_DEFINITION, |
378 |
| - ['name' => self::INDEX_NAME], |
379 |
| - ); |
380 |
| - |
381 |
| - if ($options['wait'] ?? true) { |
382 |
| - $this->wait(function () use ($collection) { |
383 |
| - $indexes = $collection->listSearchIndexes([ |
384 |
| - 'name' => self::INDEX_NAME, |
385 |
| - 'typeMap' => ['root' => 'bson'], |
386 |
| - ]); |
387 |
| - |
388 |
| - // Many indexes with the same name may exist on Atlas local |
389 |
| - foreach ($indexes as $index) { |
390 |
| - if ($index->name === self::INDEX_NAME && $index->status === 'READY') { |
391 |
| - return true; |
392 |
| - } |
| 380 | + // Ensure the collection exists before creating the search index |
| 381 | + $this->mongodb->createCollection($name); |
| 382 | + $collection = $this->mongodb->selectCollection($name); |
| 383 | + |
| 384 | + $collection = $this->mongodb->selectCollection($name); |
| 385 | + /** @todo accept configuration for the mapping */ |
| 386 | + $collection->createSearchIndex( |
| 387 | + $options['definition'] ?? self::DEFAULT_DEFINITION, |
| 388 | + ['name' => self::INDEX_NAME], |
| 389 | + ); |
| 390 | + |
| 391 | + if ($options['wait'] ?? true) { |
| 392 | + $this->wait(function () use ($collection) { |
| 393 | + $indexes = $collection->listSearchIndexes([ |
| 394 | + 'name' => self::INDEX_NAME, |
| 395 | + 'typeMap' => ['root' => 'bson'], |
| 396 | + ]); |
| 397 | + |
| 398 | + // Many indexes with the same name may exist on Atlas local |
| 399 | + foreach ($indexes as $index) { |
| 400 | + if ($index->name === self::INDEX_NAME && $index->status === 'READY') { |
| 401 | + return true; |
393 | 402 | }
|
| 403 | + } |
394 | 404 |
|
395 |
| - return false; |
396 |
| - }); |
397 |
| - } |
398 |
| - }); |
| 405 | + return false; |
| 406 | + }); |
| 407 | + } |
399 | 408 | }
|
400 | 409 |
|
401 | 410 | /**
|
@@ -443,8 +452,17 @@ private function getIndexableCollection(Model|EloquentCollection $model): MongoD
|
443 | 452 | $model = $model->first();
|
444 | 453 | }
|
445 | 454 |
|
| 455 | + assert($model instanceof Model); |
446 | 456 | assert(in_array(Searchable::class, class_uses_recursive($model)), sprintf('Model "%s" must use "%s" trait', $model::class, Searchable::class));
|
447 | 457 |
|
| 458 | + if ( |
| 459 | + $model->getConnection() instanceof Connection |
| 460 | + && $model->getConnection()->getDatabaseName() === $this->mongodb->getDatabaseName() |
| 461 | + && $model->getTable() === $model->indexableAs() |
| 462 | + ) { |
| 463 | + throw new LogicException(sprintf('The MongoDB Scout collection "%s.%s" must use a different collection from the collection name of the model "%s". Set the "scout.prefix" configuration or use a distinct MongoDB database', $this->mongodb->getDatabaseName(), $model->indexableAs(), $model::class)); |
| 464 | + } |
| 465 | + |
448 | 466 | return $this->mongodb->selectCollection($model->indexableAs());
|
449 | 467 | }
|
450 | 468 |
|
@@ -476,18 +494,6 @@ private function usesSoftDelete(Model|EloquentCollection $model): bool
|
476 | 494 | return in_array(SoftDeletes::class, class_uses_recursive($model));
|
477 | 495 | }
|
478 | 496 |
|
479 |
| - private function performSearchIndexOperation(Closure $closure): void |
480 |
| - { |
481 |
| - try { |
482 |
| - $closure(); |
483 |
| - } catch (ServerException $exception) { |
484 |
| - // @todo add error codes |
485 |
| - if (in_array($exception->getCode(), [])) { |
486 |
| - throw new \RuntimeException('Failed to perform search index operation. A MongoDB Atlas Cluster is required.', 0, $exception); |
487 |
| - } |
488 |
| - } |
489 |
| - } |
490 |
| - |
491 | 497 | private function getMapping(Model $model): array
|
492 | 498 | {
|
493 | 499 | $mapping = self::DEFAULT_DEFINITION;
|
@@ -521,9 +527,9 @@ private function wait(Closure $callback): void
|
521 | 527 | return;
|
522 | 528 | }
|
523 | 529 |
|
524 |
| - sleep(5); |
| 530 | + sleep(1); |
525 | 531 | }
|
526 | 532 |
|
527 |
| - throw new RuntimeException('Time out'); |
| 533 | + throw new RuntimeException('Atlas search index operation time out'); |
528 | 534 | }
|
529 | 535 | }
|
0 commit comments