Skip to content

Commit

Permalink
Let the server throw errors and allow creation of index with default …
Browse files Browse the repository at this point in the history
…name
  • Loading branch information
GromNaN committed Jun 19, 2023
1 parent 01da863 commit 964e7fc
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 102 deletions.
17 changes: 8 additions & 9 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -294,14 +294,8 @@
<file src="src/Model/SearchIndexInput.php">
<DocblockTypeContradiction occurrences="2">
<code>! is_array($index['definition']) &amp;&amp; ! is_object($index['definition'])</code>
<code>is_string($index['name'])</code>
<code>isset($index['name']) &amp;&amp; ! is_string($index['name'])</code>
</DocblockTypeContradiction>
<MixedReturnStatement occurrences="1">
<code>$this-&gt;index['name']</code>
</MixedReturnStatement>
<PossiblyInvalidPropertyFetch occurrences="1">
<code>$index['definition']-&gt;mappings</code>
</PossiblyInvalidPropertyFetch>
</file>
<file src="src/Operation/Aggregate.php">
<MixedArgument occurrences="2">
Expand Down Expand Up @@ -829,13 +823,18 @@
<code>$typeMap['fieldPaths'][$fieldPath]</code>
<code>$value</code>
</MixedAssignment>
<MixedInferredReturnType occurrences="2">
<MixedInferredReturnType occurrences="3">
<code>array|object</code>
<code>array|object|null</code>
<code>array|object|null</code>
</MixedInferredReturnType>
<MixedReturnStatement occurrences="2">
<MixedOperand occurrences="1">
<code>$type</code>
</MixedOperand>
<MixedReturnStatement occurrences="3">
<code>$collectionInfo['options']['encryptedFields'] ?? null</code>
<code>$encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null</code>
<code>toPHP(fromPHP($document), $typeMap)</code>
</MixedReturnStatement>
</file>
</files>
23 changes: 13 additions & 10 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,26 +396,27 @@ public function createIndexes(array $indexes, array $options = [])

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

return current($names);
}

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

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

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

/**
* Update a single Atlas Search index in the collection.
* Only available when used against a 7.0+ Atlas cluster.
*
* @param string $name Search index name
* @param array|object $definition Atlas Search index definition
Expand Down
32 changes: 8 additions & 24 deletions src/Model/SearchIndexInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
use function is_array;
use function is_object;
use function is_string;
use function sprintf;

/**
* Search index input model class.
Expand All @@ -41,23 +40,11 @@ class SearchIndexInput implements Serializable
private $index;

/**
* @param array{name: string, definition: array|object} $index Search index specification
* @param array{name?: string, definition: array|object} $index Search index specification
* @throws InvalidArgumentException
*/
public function __construct(array $index)
{
if (! isset($index['name'])) {
throw new InvalidArgumentException('Required "name" string is missing from index specification');
}

if (! is_string($index['name'])) {
throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string');
}

if ($index['name'] === '') {
throw new InvalidArgumentException('Index name cannot be empty');
}

if (! isset($index['definition'])) {
throw new InvalidArgumentException('Required "definition" document is missing from search index specification');
}
Expand All @@ -66,19 +53,16 @@ public function __construct(array $index)
throw InvalidArgumentException::invalidType('"definition" option', $index['definition'], 'array or object');
}

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

$this->index = $index;
}
if (empty($index['name'])) {
unset($index['name']);
}

/**
* Return the index name.
*/
public function __toString(): string
{
return $this->index['name'];
$this->index = $index;
}

/**
Expand Down
33 changes: 18 additions & 15 deletions src/Operation/CreateSearchIndexes.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

use function array_is_list;
use function array_map;
use function current;
use function is_array;
use function is_object;
use function sprintf;

/**
Expand All @@ -50,23 +52,21 @@ class CreateSearchIndexes implements Executable
/**
* Constructs a createSearchIndexes command.
*
* @param string $databaseName Database name
* @param string $collectionName Collection name
* @param array[] $indexes List of search index specifications
* @param string $databaseName Database name
* @param string $collectionName Collection name
* @param list<array|object> $indexes List of search index specifications
* @throws InvalidArgumentException for parameter parsing errors
*/
public function __construct(string $databaseName, string $collectionName, array $indexes)
{
if (empty($indexes)) {
throw new InvalidArgumentException('$indexes is empty');
}

if (! array_is_list($indexes)) {
throw new InvalidArgumentException('$indexes is not a list');
}

foreach ($indexes as $i => $index) {
if (! is_array($index)) {
if (is_object($index)) {
$index = (array) $index;
} elseif (! is_array($index)) {
throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array');
}

Expand All @@ -85,27 +85,30 @@ public function __construct(string $databaseName, string $collectionName, array
* @throws UnsupportedException if write concern is used and unsupported
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
public function execute(Server $server)
public function execute(Server $server): array
{
$this->executeCommand($server);
$cursor = $server->executeCommand($this->databaseName, $this->createCommand());

/** @var object{indexesCreated?: list<object{name: string}>} $result */
$result = current($cursor->toArray());

return array_map(function (SearchIndexInput $index) {
return (string) $index;
}, $this->indexes);
return array_map(function ($index) {
return $index->name;
}, $result->indexesCreated ?? []);
}

/**
* Create one or more indexes for the collection using the createSearchIndexes command.
*
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
private function executeCommand(Server $server): void
private function createCommand(): Command
{
$cmd = [
'createSearchIndexes' => $this->collectionName,
'indexes' => $this->indexes,
];

$server->executeCommand($this->databaseName, new Command($cmd));
return new Command($cmd);
}
}
10 changes: 6 additions & 4 deletions src/Operation/ListSearchIndexes.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@

namespace MongoDB\Operation;

use ArrayIterator;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;

use function assert;
use function is_array;
use function is_object;
use function is_string;
Expand Down Expand Up @@ -89,14 +89,16 @@ public function __construct(string $databaseName, string $collectionName, $filte
* Execute the operation.
*
* @see Executable::execute()
* @return ArrayIterator|Cursor
* @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation or read concern is used and unsupported
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
public function execute(Server $server)
public function execute(Server $server): Cursor
{
return $this->aggregate->execute($server);
$cursor = $this->aggregate->execute($server);
assert($cursor instanceof Cursor);

return $cursor;
}

private function createAggregate(): Aggregate
Expand Down
26 changes: 12 additions & 14 deletions tests/Collection/SearchIndexFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
use MongoDB\Tests\FunctionalTestCase;

use function bin2hex;
use function count;
use function current;
use function getenv;
use function random_bytes;
use function sleep;
Expand All @@ -26,29 +24,28 @@ public function testIndexLifecycle(): void
}

$this->manager = static::createTestManager($atlasUri);

$collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName());

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

// Create a search index
$result = $collection->createSearchIndex($name, [
'mappings' => ['dynamic' => true],
]);
$this->assertSame($name, $result);
$createdName = $collection->createSearchIndex(['name' => $name, 'definition' => ['mappings' => ['dynamic' => true]]]);
$this->assertSame($name, $createdName);

// Wait for the index to be ready
$count = 0;
do {
sleep(1);
$result = $collection->listSearchIndexes($name);
$result = $collection->listSearchIndexes($name, ['typeMap' => ['root' => 'array', 'document' => 'array']]);
$this->assertInstanceOf(Cursor::class, $result);
$index = current($result->toArray());
$this->assertObjectHasAttribute('queryable', $index);
$result->rewind();
$index = $result->current();

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

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

if ($count++ > 100) {
if ($count++ > 90) {
$this->fail('Search index was not deleted');
}
} while (count($result->toArray()) > 0);
} while ($result->valid());
}
}
Loading

0 comments on commit 964e7fc

Please sign in to comment.