Skip to content

Commit bf18dbd

Browse files
committed
Let the server throw errors and allow creation of index with default name
1 parent d8754a9 commit bf18dbd

File tree

8 files changed

+67
-109
lines changed

8 files changed

+67
-109
lines changed

psalm-baseline.xml

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@
2828
<code>($value is NativeType ? BSONType : $value)</code>
2929
</MixedInferredReturnType>
3030
</file>
31-
<file src="src/Collection.php">
32-
<MixedInferredReturnType>
33-
<code>Traversable</code>
34-
</MixedInferredReturnType>
35-
<UndefinedClass>
36-
<code>Traversable</code>
37-
</UndefinedClass>
38-
</file>
3931
<file src="src/Command/ListCollections.php">
4032
<MixedAssignment>
4133
<code>$cmd[$option]</code>
@@ -176,14 +168,6 @@
176168
<code><![CDATA[$this->index['name']]]></code>
177169
</MixedReturnStatement>
178170
</file>
179-
<file src="src/Model/SearchIndexInput.php">
180-
<MixedReturnStatement>
181-
<code><![CDATA[$this->index['name']]]></code>
182-
</MixedReturnStatement>
183-
<PossiblyInvalidPropertyFetch>
184-
<code><![CDATA[$index['definition']->mappings]]></code>
185-
</PossiblyInvalidPropertyFetch>
186-
</file>
187171
<file src="src/Operation/Aggregate.php">
188172
<MixedArgument>
189173
<code><![CDATA[$this->options['typeMap']]]></code>
@@ -503,6 +487,11 @@
503487
<code><![CDATA[$options['session']]]></code>
504488
</MixedAssignment>
505489
</file>
490+
<file src="src/Operation/ListSearchIndexes.php">
491+
<RedundantConditionGivenDocblockType>
492+
<code>assert($cursor instanceof Cursor)</code>
493+
</RedundantConditionGivenDocblockType>
494+
</file>
506495
<file src="src/Operation/MapReduce.php">
507496
<MixedArgument>
508497
<code><![CDATA[$result->result->collection]]></code>

src/Collection.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -383,26 +383,27 @@ public function createIndexes(array $indexes, array $options = [])
383383

384384
/**
385385
* Create an Atlas Search index for the collection.
386+
* Only available when used against a 7.0+ Atlas cluster.
386387
*
387388
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/
388389
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/
389-
* @param string $name List of search index specifications
390-
* @param array|object $definition Atlas Search index definition
391-
* @param array $options Command options
390+
* @param array{name?: string, definition: array|object} $index Atlas Search index specification
391+
* @param array $options Command options
392392
* @return string The name of the created search index
393393
* @throws UnsupportedException if options are not supported by the selected server
394394
* @throws InvalidArgumentException for parameter/option parsing errors
395395
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
396396
*/
397-
public function createSearchIndex(string $name, $definition, array $options = []): string
397+
public function createSearchIndex(array $index, array $options = []): string
398398
{
399-
$names = $this->createSearchIndexes([['name' => $name, 'definition' => $definition]], $options);
399+
$names = $this->createSearchIndexes([$index], $options);
400400

401401
return current($names);
402402
}
403403

404404
/**
405405
* Create one or more Atlas Search indexes for the collection.
406+
* Only available when used against a 7.0+ Atlas cluster.
406407
*
407408
* Each element in the $indexes array must have a "name" and a "definition" document.
408409
* For example:
@@ -414,8 +415,8 @@ public function createSearchIndex(string $name, $definition, array $options = []
414415
*
415416
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/
416417
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/
417-
* @param array[] $indexes List of search index specifications
418-
* @param array $options Command options
418+
* @param list<array{name?: string, definition: array|object}> $indexes List of search index specifications
419+
* @param array $options Command options
419420
* @return string[] The names of the created search indexes
420421
* @throws UnsupportedException if options are not supported by the selected server
421422
* @throws InvalidArgumentException for parameter/option parsing errors
@@ -609,6 +610,7 @@ public function dropIndexes(array $options = [])
609610

610611
/**
611612
* Drop a single Atlas Search index in the collection.
613+
* Only available when used against a 7.0+ Atlas cluster.
612614
*
613615
* @param string $name Search index name
614616
* @param array $options Additional options
@@ -1001,13 +1003,13 @@ public function listIndexes(array $options = [])
10011003

10021004
/**
10031005
* Returns information for all Atlas Search indexes for the collection.
1006+
* Only available when used against a 7.0+ Atlas cluster.
10041007
*
1005-
* @see ListSearchIndexes::__construct() for supported aggregation and list options
1006-
* @return Traversable
10071008
* @throws InvalidArgumentException for parameter/option parsing errors
10081009
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
1010+
* @see ListSearchIndexes::__construct() for supported aggregation and list options
10091011
*/
1010-
public function listSearchIndexes(?string $name = null, array $options = []): Traversable
1012+
public function listSearchIndexes(?string $name = null, array $options = []): Cursor
10111013
{
10121014
$filter = [];
10131015
if ($name) {
@@ -1182,6 +1184,7 @@ public function updateOne($filter, $update, array $options = [])
11821184

11831185
/**
11841186
* Update a single Atlas Search index in the collection.
1187+
* Only available when used against a 7.0+ Atlas cluster.
11851188
*
11861189
* @param string $name Search index name
11871190
* @param array|object $definition Atlas Search index definition

src/Model/SearchIndexInput.php

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use function is_array;
2424
use function is_object;
2525
use function is_string;
26-
use function sprintf;
2726

2827
/**
2928
* Search index input model class.
@@ -41,23 +40,11 @@ class SearchIndexInput implements Serializable
4140
private $index;
4241

4342
/**
44-
* @param array{name: string, definition: array|object} $index Search index specification
43+
* @param array{name?: string, definition: array|object} $index Search index specification
4544
* @throws InvalidArgumentException
4645
*/
4746
public function __construct(array $index)
4847
{
49-
if (! isset($index['name'])) {
50-
throw new InvalidArgumentException('Required "name" string is missing from index specification');
51-
}
52-
53-
if (! is_string($index['name'])) {
54-
throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string');
55-
}
56-
57-
if ($index['name'] === '') {
58-
throw new InvalidArgumentException('Index name cannot be empty');
59-
}
60-
6148
if (! isset($index['definition'])) {
6249
throw new InvalidArgumentException('Required "definition" document is missing from search index specification');
6350
}
@@ -66,19 +53,16 @@ public function __construct(array $index)
6653
throw InvalidArgumentException::invalidType('"definition" option', $index['definition'], 'array or object');
6754
}
6855

69-
if (! isset($index['definition']['mappings']) && ! isset($index['definition']->mappings)) {
70-
throw new InvalidArgumentException(sprintf('Required "mappings" document is missing from the search index definition named "%s".', $index['name']));
56+
// Name is optional, but must be a non-empty string if provided
57+
if (isset($index['name']) && ! is_string($index['name'])) {
58+
throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string');
7159
}
7260

73-
$this->index = $index;
74-
}
61+
if (empty($index['name'])) {
62+
unset($index['name']);
63+
}
7564

76-
/**
77-
* Return the index name.
78-
*/
79-
public function __toString(): string
80-
{
81-
return $this->index['name'];
65+
$this->index = $index;
8266
}
8367

8468
/**

src/Operation/CreateSearchIndexes.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
use function array_is_list;
2828
use function array_map;
29+
use function current;
2930
use function is_array;
31+
use function is_object;
3032
use function sprintf;
3133

3234
/**
@@ -50,23 +52,21 @@ class CreateSearchIndexes implements Executable
5052
/**
5153
* Constructs a createSearchIndexes command.
5254
*
53-
* @param string $databaseName Database name
54-
* @param string $collectionName Collection name
55-
* @param array[] $indexes List of search index specifications
55+
* @param string $databaseName Database name
56+
* @param string $collectionName Collection name
57+
* @param list<array|object> $indexes List of search index specifications
5658
* @throws InvalidArgumentException for parameter parsing errors
5759
*/
5860
public function __construct(string $databaseName, string $collectionName, array $indexes)
5961
{
60-
if (empty($indexes)) {
61-
throw new InvalidArgumentException('$indexes is empty');
62-
}
63-
6462
if (! array_is_list($indexes)) {
6563
throw new InvalidArgumentException('$indexes is not a list');
6664
}
6765

6866
foreach ($indexes as $i => $index) {
69-
if (! is_array($index)) {
67+
if (is_object($index)) {
68+
$index = (array) $index;
69+
} elseif (! is_array($index)) {
7070
throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array');
7171
}
7272

@@ -85,27 +85,30 @@ public function __construct(string $databaseName, string $collectionName, array
8585
* @throws UnsupportedException if write concern is used and unsupported
8686
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
8787
*/
88-
public function execute(Server $server)
88+
public function execute(Server $server): array
8989
{
90-
$this->executeCommand($server);
90+
$cursor = $server->executeCommand($this->databaseName, $this->createCommand());
91+
92+
/** @var object{indexesCreated?: list<object{name: string}>} $result */
93+
$result = current($cursor->toArray());
9194

92-
return array_map(function (SearchIndexInput $index) {
93-
return (string) $index;
94-
}, $this->indexes);
95+
return array_map(function ($index) {
96+
return $index->name;
97+
}, $result->indexesCreated ?? []);
9598
}
9699

97100
/**
98101
* Create one or more indexes for the collection using the createSearchIndexes command.
99102
*
100103
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
101104
*/
102-
private function executeCommand(Server $server): void
105+
private function createCommand(): Command
103106
{
104107
$cmd = [
105108
'createSearchIndexes' => $this->collectionName,
106109
'indexes' => $this->indexes,
107110
];
108111

109-
$server->executeCommand($this->databaseName, new Command($cmd));
112+
return new Command($cmd);
110113
}
111114
}

src/Operation/ListSearchIndexes.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717

1818
namespace MongoDB\Operation;
1919

20-
use ArrayIterator;
2120
use MongoDB\Driver\Cursor;
2221
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
2322
use MongoDB\Driver\Server;
2423
use MongoDB\Exception\InvalidArgumentException;
2524
use MongoDB\Exception\UnexpectedValueException;
2625
use MongoDB\Exception\UnsupportedException;
2726

27+
use function assert;
2828
use function is_array;
2929
use function is_object;
3030
use function is_string;
@@ -89,14 +89,16 @@ public function __construct(string $databaseName, string $collectionName, $filte
8989
* Execute the operation.
9090
*
9191
* @see Executable::execute()
92-
* @return ArrayIterator|Cursor
9392
* @throws UnexpectedValueException if the command response was malformed
9493
* @throws UnsupportedException if collation or read concern is used and unsupported
9594
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
9695
*/
97-
public function execute(Server $server)
96+
public function execute(Server $server): Cursor
9897
{
99-
return $this->aggregate->execute($server);
98+
$cursor = $this->aggregate->execute($server);
99+
assert($cursor instanceof Cursor);
100+
101+
return $cursor;
100102
}
101103

102104
private function createAggregate(): Aggregate

tests/Collection/SearchIndexFunctionalTest.php

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
use MongoDB\Tests\FunctionalTestCase;
77

88
use function bin2hex;
9-
use function count;
10-
use function current;
119
use function getenv;
1210
use function random_bytes;
1311
use function sleep;
@@ -26,29 +24,28 @@ public function testIndexLifecycle(): void
2624
}
2725

2826
$this->manager = static::createTestManager($atlasUri);
27+
2928
$collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName());
3029

3130
$name = 'search_index_' . bin2hex(random_bytes(5));
3231

3332
// Create a search index
34-
$result = $collection->createSearchIndex($name, [
35-
'mappings' => ['dynamic' => true],
36-
]);
37-
$this->assertSame($name, $result);
33+
$createdName = $collection->createSearchIndex(['name' => $name, 'definition' => ['mappings' => ['dynamic' => true]]]);
34+
$this->assertSame($name, $createdName);
3835

3936
// Wait for the index to be ready
4037
$count = 0;
4138
do {
4239
sleep(1);
43-
$result = $collection->listSearchIndexes($name);
40+
$result = $collection->listSearchIndexes($name, ['typeMap' => ['root' => 'array', 'document' => 'array']]);
4441
$this->assertInstanceOf(Cursor::class, $result);
45-
$index = current($result->toArray());
46-
$this->assertObjectHasAttribute('queryable', $index);
42+
$result->rewind();
43+
$index = $result->current();
4744

48-
if ($count++ > 100) {
45+
if ($count++ > 90) {
4946
$this->fail('Search index did not become queryable');
5047
}
51-
} while (! $index->queryable);
48+
} while (! $index['queryable']);
5249

5350
// Update the search index
5451
$collection->updateSearchIndex($name, [
@@ -61,12 +58,13 @@ public function testIndexLifecycle(): void
6158
$count = 0;
6259
do {
6360
sleep(1);
64-
$result = $collection->listSearchIndexes($name);
61+
$result = $collection->listSearchIndexes($name, ['typeMap' => ['root' => 'array', 'document' => 'array']]);
6562
$this->assertInstanceOf(Cursor::class, $result);
63+
$result->rewind();
6664

67-
if ($count++ > 100) {
65+
if ($count++ > 90) {
6866
$this->fail('Search index was not deleted');
6967
}
70-
} while (count($result->toArray()) > 0);
68+
} while ($result->valid());
7169
}
7270
}

0 commit comments

Comments
 (0)