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 6cb3838

Browse files
authored
PHPORM-273 Add schema helpers to create search and vector indexes (#3230)
1 parent 7890518 commit 6cb3838

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed

‎phpcs.xml.dist‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@
5353
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
5454
<exclude-pattern>tests/Ticket/*.php</exclude-pattern>
5555
</rule>
56+
57+
<rule ref="SlevomatCodingStandard.Commenting.DocCommentSpacing.IncorrectAnnotationsGroup">
58+
<exclude-pattern>src/Schema/Blueprint.php</exclude-pattern>
59+
</rule>
5660
</ruleset>

‎src/Schema/Blueprint.php‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,42 @@ public function sparse_and_unique($columns = null, $options = [])
303303
return $this;
304304
}
305305

306+
/**
307+
* Create an Atlas Search Index.
308+
*
309+
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/#std-label-search-index-definition-create
310+
*
311+
* @phpstan-param array{
312+
* analyzer?: string,
313+
* analyzers?: list<array>,
314+
* searchAnalyzer?: string,
315+
* mappings: array{dynamic: true} | array{dynamic?: bool, fields: array<string, array>},
316+
* storedSource?: bool|array,
317+
* synonyms?: list<array>,
318+
* ...
319+
* } $definition
320+
*/
321+
public function searchIndex(array $definition, string $name = 'default'): static
322+
{
323+
$this->collection->createSearchIndex($definition, ['name' => $name, 'type' => 'search']);
324+
325+
return $this;
326+
}
327+
328+
/**
329+
* Create an Atlas Vector Search Index.
330+
*
331+
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/#std-label-vector-search-index-definition-create
332+
*
333+
* @phpstan-param array{fields: array<string, array{type: string, ...}>} $definition
334+
*/
335+
public function vectorSearchIndex(array $definition, string $name = 'default'): static
336+
{
337+
$this->collection->createSearchIndex($definition, ['name' => $name, 'type' => 'vectorSearch']);
338+
339+
return $this;
340+
}
341+
306342
/**
307343
* Allow fluent columns.
308344
*

‎src/Schema/Builder.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ public function getIndexes($table)
253253
try {
254254
$indexes = $collection->listSearchIndexes(['typeMap' => ['root' => 'array', 'array' => 'array', 'document' => 'array']]);
255255
foreach ($indexes as $index) {
256+
// Status 'DOES_NOT_EXIST' means the index has been dropped but is still in the process of being removed
257+
if ($index['status'] === 'DOES_NOT_EXIST') {
258+
continue;
259+
}
260+
256261
$indexList[] = [
257262
'name' => $index['name'],
258263
'columns' => match ($index['type']) {

‎tests/SchemaTest.php‎

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@
88
use Illuminate\Support\Facades\Schema;
99
use MongoDB\BSON\Binary;
1010
use MongoDB\BSON\UTCDateTime;
11+
use MongoDB\Collection;
12+
use MongoDB\Database;
1113
use MongoDB\Laravel\Schema\Blueprint;
1214

15+
use function assert;
1316
use function collect;
1417
use function count;
1518

1619
class SchemaTest extends TestCase
1720
{
1821
public function tearDown(): void
1922
{
20-
Schema::drop('newcollection');
21-
Schema::drop('newcollection_two');
23+
$database = $this->getConnection('mongodb')->getMongoDB();
24+
assert($database instanceof Database);
25+
$database->dropCollection('newcollection');
26+
$database->dropCollection('newcollection_two');
2227
}
2328

2429
public function testCreate(): void
@@ -474,6 +479,7 @@ public function testGetColumns()
474479
$this->assertSame([], $columns);
475480
}
476481

482+
/** @see AtlasSearchTest::testGetIndexes() */
477483
public function testGetIndexes()
478484
{
479485
Schema::create('newcollection', function (Blueprint $collection) {
@@ -523,9 +529,54 @@ public function testGetIndexes()
523529
$this->assertSame([], $indexes);
524530
}
525531

532+
public function testSearchIndex(): void
533+
{
534+
$this->skipIfSearchIndexManagementIsNotSupported();
535+
536+
Schema::create('newcollection', function (Blueprint $collection) {
537+
$collection->searchIndex([
538+
'mappings' => [
539+
'dynamic' => false,
540+
'fields' => [
541+
'foo' => ['type' => 'string', 'analyzer' => 'lucene.whitespace'],
542+
],
543+
],
544+
]);
545+
});
546+
547+
$index = $this->getSearchIndex('newcollection', 'default');
548+
self::assertNotNull($index);
549+
550+
self::assertSame('default', $index['name']);
551+
self::assertSame('search', $index['type']);
552+
self::assertFalse($index['latestDefinition']['mappings']['dynamic']);
553+
self::assertSame('lucene.whitespace', $index['latestDefinition']['mappings']['fields']['foo']['analyzer']);
554+
}
555+
556+
public function testVectorSearchIndex()
557+
{
558+
$this->skipIfSearchIndexManagementIsNotSupported();
559+
560+
Schema::create('newcollection', function (Blueprint $collection) {
561+
$collection->vectorSearchIndex([
562+
'fields' => [
563+
['type' => 'vector', 'path' => 'foo', 'numDimensions' => 128, 'similarity' => 'euclidean', 'quantization' => 'none'],
564+
],
565+
], 'vector');
566+
});
567+
568+
$index = $this->getSearchIndex('newcollection', 'vector');
569+
self::assertNotNull($index);
570+
571+
self::assertSame('vector', $index['name']);
572+
self::assertSame('vectorSearch', $index['type']);
573+
self::assertSame('vector', $index['latestDefinition']['fields'][0]['type']);
574+
}
575+
526576
protected function getIndex(string $collection, string $name)
527577
{
528578
$collection = DB::getCollection($collection);
579+
assert($collection instanceof Collection);
529580

530581
foreach ($collection->listIndexes() as $index) {
531582
if (isset($index['key'][$name])) {
@@ -535,4 +586,16 @@ protected function getIndex(string $collection, string $name)
535586

536587
return false;
537588
}
589+
590+
protected function getSearchIndex(string $collection, string $name): ?array
591+
{
592+
$collection = DB::getCollection($collection);
593+
assert($collection instanceof Collection);
594+
595+
foreach ($collection->listSearchIndexes(['name' => $name, 'typeMap' => ['root' => 'array', 'array' => 'array', 'document' => 'array']]) as $index) {
596+
return $index;
597+
}
598+
599+
return null;
600+
}
538601
}

‎tests/TestCase.php‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
namespace MongoDB\Laravel\Tests;
66

77
use Illuminate\Foundation\Application;
8+
use MongoDB\Driver\Exception\ServerException;
89
use MongoDB\Laravel\MongoDBServiceProvider;
10+
use MongoDB\Laravel\Schema\Builder;
911
use MongoDB\Laravel\Tests\Models\User;
1012
use MongoDB\Laravel\Validation\ValidationServiceProvider;
1113
use Orchestra\Testbench\TestCase as OrchestraTestCase;
@@ -64,4 +66,17 @@ protected function getEnvironmentSetUp($app): void
6466
$app['config']->set('queue.failed.database', 'mongodb2');
6567
$app['config']->set('queue.failed.driver', 'mongodb');
6668
}
69+
70+
public function skipIfSearchIndexManagementIsNotSupported(): void
71+
{
72+
try {
73+
$this->getConnection('mongodb')->getCollection('test')->listSearchIndexes(['name' => 'just_for_testing']);
74+
} catch (ServerException $e) {
75+
if (Builder::isAtlasSearchNotSupportedException($e)) {
76+
self::markTestSkipped('Search index management is not supported on this server');
77+
}
78+
79+
throw $e;
80+
}
81+
}
6782
}

0 commit comments

Comments
(0)

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