Skip to content

Commit 964e7fc

Browse files
committed
Let the server throw errors and allow creation of index with default name
1 parent 01da863 commit 964e7fc

File tree

8 files changed

+70
-102
lines changed

8 files changed

+70
-102
lines changed

psalm-baseline.xml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -294,14 +294,8 @@
294294
<file src="src/Model/SearchIndexInput.php">
295295
<DocblockTypeContradiction occurrences="2">
296296
<code>! is_array($index['definition']) &amp;&amp; ! is_object($index['definition'])</code>
297-
<code>is_string($index['name'])</code>
297+
<code>isset($index['name']) &amp;&amp; ! is_string($index['name'])</code>
298298
</DocblockTypeContradiction>
299-
<MixedReturnStatement occurrences="1">
300-
<code>$this-&gt;index['name']</code>
301-
</MixedReturnStatement>
302-
<PossiblyInvalidPropertyFetch occurrences="1">
303-
<code>$index['definition']-&gt;mappings</code>
304-
</PossiblyInvalidPropertyFetch>
305299
</file>
306300
<file src="src/Operation/Aggregate.php">
307301
<MixedArgument occurrences="2">
@@ -829,13 +823,18 @@
829823
<code>$typeMap['fieldPaths'][$fieldPath]</code>
830824
<code>$value</code>
831825
</MixedAssignment>
832-
<MixedInferredReturnType occurrences="2">
826+
<MixedInferredReturnType occurrences="3">
827+
<code>array|object</code>
833828
<code>array|object|null</code>
834829
<code>array|object|null</code>
835830
</MixedInferredReturnType>
836-
<MixedReturnStatement occurrences="2">
831+
<MixedOperand occurrences="1">
832+
<code>$type</code>
833+
</MixedOperand>
834+
<MixedReturnStatement occurrences="3">
837835
<code>$collectionInfo['options']['encryptedFields'] ?? null</code>
838836
<code>$encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null</code>
837+
<code>toPHP(fromPHP($document), $typeMap)</code>
839838
</MixedReturnStatement>
840839
</file>
841840
</files>

src/Collection.php

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

397397
/**
398398
* Create an Atlas Search index for the collection.
399+
* Only available when used against a 7.0+ Atlas cluster.
399400
*
400401
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/
401402
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/
402-
* @param string $name List of search index specifications
403-
* @param array|object $definition Atlas Search index definition
404-
* @param array $options Command options
403+
* @param array{name?: string, definition: array|object} $index Atlas Search index specification
404+
* @param array $options Command options
405405
* @return string The name of the created search index
406406
* @throws UnsupportedException if options are not supported by the selected server
407407
* @throws InvalidArgumentException for parameter/option parsing errors
408408
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
409409
*/
410-
public function createSearchIndex(string $name, $definition, array $options = []): string
410+
public function createSearchIndex(array $index, array $options = []): string
411411
{
412-
$names = $this->createSearchIndexes([['name' => $name, 'definition' => $definition]], $options);
412+
$names = $this->createSearchIndexes([$index], $options);
413413

414414
return current($names);
415415
}
416416

417417
/**
418418
* Create one or more Atlas Search indexes for the collection.
419+
* Only available when used against a 7.0+ Atlas cluster.
419420
*
420421
* Each element in the $indexes array must have a "name" and a "definition" document.
421422
* For example:
@@ -427,8 +428,8 @@ public function createSearchIndex(string $name, $definition, array $options = []
427428
*
428429
* @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/
429430
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/
430-
* @param array[] $indexes List of search index specifications
431-
* @param array $options Command options
431+
* @param list<array{name?: string, definition: array|object}> $indexes List of search index specifications
432+
* @param array $options Command options
432433
* @return string[] The names of the created search indexes
433434
* @throws UnsupportedException if options are not supported by the selected server
434435
* @throws InvalidArgumentException for parameter/option parsing errors
@@ -622,6 +623,7 @@ public function dropIndexes(array $options = [])
622623

623624
/**
624625
* Drop a single Atlas Search index in the collection.
626+
* Only available when used against a 7.0+ Atlas cluster.
625627
*
626628
* @param string $name Search index name
627629
* @param array $options Additional options
@@ -1014,13 +1016,13 @@ public function listIndexes(array $options = [])
10141016

10151017
/**
10161018
* Returns information for all Atlas Search indexes for the collection.
1019+
* Only available when used against a 7.0+ Atlas cluster.
10171020
*
1018-
* @see ListSearchIndexes::__construct() for supported aggregation and list options
1019-
* @return Traversable
10201021
* @throws InvalidArgumentException for parameter/option parsing errors
10211022
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
1023+
* @see ListSearchIndexes::__construct() for supported aggregation and list options
10221024
*/
1023-
public function listSearchIndexes(?string $name = null, array $options = []): Traversable
1025+
public function listSearchIndexes(?string $name = null, array $options = []): Cursor
10241026
{
10251027
$filter = [];
10261028
if ($name) {
@@ -1195,6 +1197,7 @@ public function updateOne($filter, $update, array $options = [])
11951197

11961198
/**
11971199
* Update a single Atlas Search index in the collection.
1200+
* Only available when used against a 7.0+ Atlas cluster.
11981201
*
11991202
* @param string $name Search index name
12001203
* @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)