/** * Constructor. * * @param array $index Index specification * * @throws InvalidArgumentException */ public function __construct(array $index) { if (!isset($index['key'])) { throw new InvalidArgumentException('Required "key" document is missing from index specification'); } if (!is_array($index['key']) && !is_object($index['key'])) { throw InvalidArgumentException::invalidType('"key" option', $index['key'], 'array or object'); } foreach ($index['key'] as $fieldName => $order) { if (!is_int($order) && !is_float($order) && !is_string($order)) { throw InvalidArgumentException::invalidType(sprintf('order value for "%s" field within "key" option', $fieldName), $order, 'numeric or string'); } } if (!isset($index['ns'])) { throw new InvalidArgumentException('Required "ns" option is missing from index specification'); } if (!is_string($index['ns'])) { throw InvalidArgumentException::invalidType('"ns" option', $index['ns'], 'string'); } if (!isset($index['name'])) { $index['name'] = Functions::generateIndexName($index['key']); } if (!is_string($index['name'])) { throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string'); } $this->index = $index; }
/** * Constructs a findAndModify command for updating a document. * * Supported options: * * * bypassDocumentValidation (boolean): If true, allows the write to opt * out of document level validation. * * * maxTimeMS (integer): The maximum amount of time to allow the query to * run. * * * projection (document): Limits the fields to return for the matching * document. * * * returnDocument (enum): Whether to return the document before or after * the update is applied. Must be either * FindOneAndUpdate::RETURN_DOCUMENT_BEFORE or * FindOneAndUpdate::RETURN_DOCUMENT_AFTER. The default is * FindOneAndUpdate::RETURN_DOCUMENT_BEFORE. * * * sort (document): Determines which document the operation modifies if * the query selects multiple documents. * * * upsert (boolean): When true, a new document is created if no document * matches the query. The default is false. * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. This option * is only supported for server versions >= 3.2. * * @param string $databaseName Database name * @param string $collectionName Collection name * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options * * @throws InvalidArgumentException */ public function __construct($databaseName, $collectionName, $filter, $update, array $options = []) { if (!is_array($filter) && !is_object($filter)) { throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); } if (!is_array($update) && !is_object($update)) { throw InvalidArgumentException::invalidType('$update', $update, 'array or object'); } if (!Functions::isFirstKeyOperator($update)) { throw new InvalidArgumentException('First key in $update argument is not an update operator'); } $options += ['returnDocument' => self::RETURN_DOCUMENT_BEFORE, 'upsert' => false]; if (isset($options['projection']) && !is_array($options['projection']) && !is_object($options['projection'])) { throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); } if (!is_integer($options['returnDocument'])) { throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer'); } if ($options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER && $options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE) { throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']); } if (isset($options['projection'])) { $options['fields'] = $options['projection']; } $options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER; unset($options['projection'], $options['returnDocument']); $this->findAndModify = new FindAndModify($databaseName, $collectionName, ['query' => $filter, 'update' => $update] + $options); }
/** * Constructs an update command. * * Supported options: * * * bypassDocumentValidation (boolean): If true, allows the write to opt * out of document level validation. * * * upsert (boolean): When true, a new document is created if no document * matches the query. The default is false. * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name * @param string $collectionName Collection name * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options * * @throws InvalidArgumentException */ public function __construct($databaseName, $collectionName, $filter, $update, array $options = []) { if (!is_array($update) && !is_object($update)) { throw InvalidArgumentException::invalidType('$update', $update, 'array or object'); } if (!Functions::isFirstKeyOperator($update)) { throw new InvalidArgumentException('First key in $update argument is not an update operator'); } $this->update = new Update($databaseName, $collectionName, $filter, $update, ['multi' => false] + $options); }
/** * Execute the operation. * * For servers < 2.6, this will actually perform an insert operation on the * database's "system.indexes" collection. * * @see Executable::execute() * * @param Server $server * * @return string[] The names of the created indexes */ public function execute(Server $server) { if (Functions::serverSupportsFeature($server, self::$wireVersionForCommand)) { $this->executeCommand($server); } else { $this->executeLegacy($server); } return array_map(function (IndexInput $index) { return (string) $index; }, $this->indexes); }
/** * Execute the operation. * * @see Executable::execute() * * @param Server $server * * @return InsertOneResult */ public function execute(Server $server) { $options = []; if (isset($this->options['bypassDocumentValidation']) && Functions::serverSupportsFeature($server, self::$wireVersionForDocumentLevelValidation)) { $options['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } $bulk = new Bulk($options); $insertedId = $bulk->insert($this->document); if ($insertedId === null) { $insertedId = Functions::extractIdFromInsertedDocument($this->document); } $writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null; $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern); return new InsertOneResult($writeResult, $insertedId); }
/** * Constructs a writable GridFS stream. * * Supported options: * * * _id (mixed): File document identifier. Defaults to a new ObjectId. * * * aliases (array of strings): DEPRECATED An array of aliases. * Applications wishing to store aliases should add an aliases field to * the metadata document instead. * * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to * 261120 (i.e. 255 KiB). * * * contentType (string): DEPRECATED content type to be stored with the * file. This information should now be added to the metadata. * * * metadata (document): User data for the "metadata" field of the files * collection document. * * @param CollectionWrapper $collectionWrapper GridFS collection wrapper * @param string $filename Filename * @param array $options Upload options * * @throws InvalidArgumentException */ public function __construct(CollectionWrapper $collectionWrapper, $filename, array $options = []) { $options += ['_id' => new ObjectId(), 'chunkSizeBytes' => self::$defaultChunkSizeBytes]; if (isset($options['aliases']) && !Functions::isStringArray($options['aliases'])) { throw InvalidArgumentException::invalidType('"aliases" option', $options['aliases'], 'array of strings'); } if (isset($options['chunkSizeBytes']) && !is_integer($options['chunkSizeBytes'])) { throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer'); } if (isset($options['contentType']) && !is_string($options['contentType'])) { throw InvalidArgumentException::invalidType('"contentType" option', $options['contentType'], 'string'); } if (isset($options['metadata']) && !is_array($options['metadata']) && !is_object($options['metadata'])) { throw InvalidArgumentException::invalidType('"metadata" option', $options['metadata'], 'array or object'); } $this->chunkSize = $options['chunkSizeBytes']; $this->collectionWrapper = $collectionWrapper; $this->buffer = fopen('php://temp', 'w+'); $this->ctx = hash_init('md5'); $this->file = ['_id' => $options['_id'], 'chunkSize' => $this->chunkSize, 'filename' => (string) $filename, 'uploadDate' => new UTCDateTime(floor(microtime(true) * 1000))] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]); }
/** * Execute the operation. * * @see Executable::execute() * * @param Server $server * * @return CollectionInfoIterator */ public function execute(Server $server) { return Functions::serverSupportsFeature($server, self::$wireVersionForCommand) ? $this->executeCommand($server) : $this->executeLegacy($server); }
/** * Finds a single document and updates it, returning either the original or * the updated document. * * The document to return may be null if no document matched the filter. By * default, the original document is returned. Specify * FindOneAndUpdate::RETURN_DOCUMENT_AFTER for the "returnDocument" option * to return the updated document. * * Note: BSON deserialization of the returned document does not yet support * a custom type map (depends on: https://jira.mongodb.org/browse/PHPC-314). * * @see FindOneAndReplace::__construct() for supported options * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ * * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options * * @return object|null */ public function findOneAndUpdate($filter, $update, array $options = []) { $server = $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); if (!isset($options['writeConcern']) && Functions::serverSupportsFeature($server, self::$wireVersionForFindAndModifyWriteConcern)) { $options['writeConcern'] = $this->writeConcern; } $operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options); return $operation->execute($server); }
/** * Create the distinct command. * * @param Server $server * * @return Command */ private function createCommand(Server $server) { $cmd = ['distinct' => $this->collectionName, 'key' => $this->fieldName]; if (!empty($this->filter)) { $cmd['query'] = (object) $this->filter; } if (isset($this->options['maxTimeMS'])) { $cmd['maxTimeMS'] = $this->options['maxTimeMS']; } if (isset($this->options['readConcern']) && Functions::serverSupportsFeature($server, self::$wireVersionForReadConcern)) { $cmd['readConcern'] = Functions::readConcernAsDocument($this->options['readConcern']); } return new Command($cmd); }
/** * Create the count command. * * @param Server $server * * @return Command */ private function createCommand(Server $server) { $cmd = ['count' => $this->collectionName]; if (!empty($this->filter)) { $cmd['query'] = (object) $this->filter; } foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } } if (isset($this->options['readConcern']) && Functions::serverSupportsFeature($server, self::$wireVersionForReadConcern)) { $cmd['readConcern'] = Functions::readConcernAsDocument($this->options['readConcern']); } return new Command($cmd); }
/** * Execute the operation. * * @see Executable::execute() * * @param Server $server * * @return UpdateResult */ public function execute(Server $server) { $updateOptions = ['multi' => $this->options['multi'], 'upsert' => $this->options['upsert']]; $bulkOptions = []; if (isset($this->options['bypassDocumentValidation']) && Functions::serverSupportsFeature($server, self::$wireVersionForDocumentLevelValidation)) { $bulkOptions['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } $bulk = new Bulk($bulkOptions); $bulk->update($this->filter, $this->update, $updateOptions); $writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null; $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern); return new UpdateResult($writeResult); }
/** * Execute the operation. * * @see Executable::execute() * * @param Server $server * * @return BulkWriteResult */ public function execute(Server $server) { $options = ['ordered' => $this->options['ordered']]; if (isset($this->options['bypassDocumentValidation']) && Functions::serverSupportsFeature($server, self::$wireVersionForDocumentLevelValidation)) { $options['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } $bulk = new Bulk($options); $insertedIds = []; foreach ($this->operations as $i => $operation) { $type = key($operation); $args = current($operation); switch ($type) { case self::DELETE_MANY: case self::DELETE_ONE: $bulk->delete($args[0], $args[1]); break; case self::INSERT_ONE: $insertedId = $bulk->insert($args[0]); if ($insertedId !== null) { $insertedIds[$i] = $insertedId; } else { $insertedIds[$i] = Functions::extractIdFromInsertedDocument($args[0]); } break; case self::REPLACE_ONE: case self::UPDATE_MANY: case self::UPDATE_ONE: $bulk->update($args[0], $args[1], $args[2]); } } $writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null; $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern); return new BulkWriteResult($writeResult, $insertedIds); }
/** * Create the aggregate command. * * @param Server $server * @param boolean $isCursorSupported * * @return Command */ private function createCommand(Server $server, $isCursorSupported) { $cmd = ['aggregate' => $this->collectionName, 'pipeline' => $this->pipeline]; // Servers < 2.6 do not support any command options if (!$isCursorSupported) { return new Command($cmd); } $cmd['allowDiskUse'] = $this->options['allowDiskUse']; if (isset($this->options['bypassDocumentValidation']) && Functions::serverSupportsFeature($server, self::$wireVersionForDocumentLevelValidation)) { $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } if (isset($this->options['maxTimeMS'])) { $cmd['maxTimeMS'] = $this->options['maxTimeMS']; } if (isset($this->options['readConcern']) && Functions::serverSupportsFeature($server, self::$wireVersionForReadConcern)) { $cmd['readConcern'] = Functions::readConcernAsDocument($this->options['readConcern']); } if ($this->options['useCursor']) { $cmd['cursor'] = isset($this->options["batchSize"]) ? ['batchSize' => $this->options["batchSize"]] : new stdClass(); } return new Command($cmd); }
/** * Create the findAndModify command. * * @param Server $server * * @return Command */ private function createCommand(Server $server) { $cmd = ['findAndModify' => $this->collectionName]; if ($this->options['remove']) { $cmd['remove'] = true; } else { $cmd['new'] = $this->options['new']; $cmd['upsert'] = $this->options['upsert']; } foreach (['fields', 'query', 'sort', 'update'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = (object) $this->options[$option]; } } if (isset($this->options['maxTimeMS'])) { $cmd['maxTimeMS'] = $this->options['maxTimeMS']; } if (isset($this->options['bypassDocumentValidation']) && Functions::serverSupportsFeature($server, self::$wireVersionForDocumentLevelValidation)) { $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } if (isset($this->options['writeConcern']) && Functions::serverSupportsFeature($server, self::$wireVersionForWriteConcern)) { $cmd['writeConcern'] = $this->options['writeConcern']; } return new Command($cmd); }