Skip to content

PHPLIB-232, PHPLIB-238: Bucket typeMap option and findOne() #290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ replacement:
resource: "bucket"
parent: "database"
---
source:
file: apiargs-common-option.yaml
ref: typeMap
replacement:
parent: "database"
---
source:
file: apiargs-common-option.yaml
ref: writeConcern
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ replacement:
resource: "bucket"
parent: "database"
---
source:
file: apiargs-MongoDBClient-method-construct-driverOptions.yaml
ref: typeMap
---
source:
file: apiargs-common-option.yaml
ref: writeConcern
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: projection
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: sort
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: skip
---
source:
file: apiargs-MongoDBCollection-common-option.yaml
ref: collation
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: comment
---
source:
file: apiargs-common-option.yaml
ref: maxTimeMS
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: readConcern
---
source:
file: apiargs-MongoDBGridFSBucket-method-find-option.yaml
ref: readPreference
---
source:
file: apiargs-MongoDBGridFSBucket-method-find-option.yaml
ref: typeMap
post: |
This will be used for the returned result document.
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: modifiers
...
1 change: 1 addition & 0 deletions docs/reference/class/MongoDBGridFSBucket.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Methods
/reference/method/MongoDBGridFSBucket-downloadToStreamByName
/reference/method/MongoDBGridFSBucket-drop
/reference/method/MongoDBGridFSBucket-find
/reference/method/MongoDBGridFSBucket-findOne
/reference/method/MongoDBGridFSBucket-getBucketName
/reference/method/MongoDBGridFSBucket-getDatabaseName
/reference/method/MongoDBGridFSBucket-getFileDocumentForStream
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/method/MongoDBCollection-findOne.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ Definition
Return Values
-------------

An array or object for the :term:`first document <natural order>` document that
matched the query, or ``null`` if no document matched the query. The return type
will depend on the ``typeMap`` option.
An array or object for the :term:`first document <natural order>` that matched
the query, or ``null`` if no document matched the query. The return type will
depend on the ``typeMap`` option.

Errors/Exceptions
-----------------
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ Errors/Exceptions
Behavior
--------

The selected bucket inherits options such as read preference and write concern
from the :phpclass:`Database <MongoDB\\Database>` object. Options may be
The selected bucket inherits options such as read preference and type
mapping from the :phpclass:`Database <MongoDB\\Database>` object. Options may be
overridden via the ``$options`` parameter.

Example
Expand Down
1 change: 1 addition & 0 deletions docs/reference/method/MongoDBGridFSBucket-find.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ See Also
--------

- :phpmethod:`MongoDB\\Collection::find()`
- :phpmethod:`MongoDB\\GridFS\\Bucket::findOne()`
46 changes: 46 additions & 0 deletions docs/reference/method/MongoDBGridFSBucket-findOne.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
==================================
MongoDB\\GridFS\\Bucket::findOne()
==================================

.. default-domain:: mongodb

.. contents:: On this page
:local:
:backlinks: none
:depth: 1
:class: singlecol

Definition
----------

.. phpmethod:: MongoDB\\GridFS\\Bucket::findOne()

Finds a single document from the GridFS bucket's files collection matching
the query.

.. code-block:: php

function findOne($filter = [], array $options = []): array|object|null

This method has the following parameters:

.. include:: /includes/apiargs/MongoDBCollection-method-findOne-param.rst

The ``$options`` parameter supports the following options:

.. include:: /includes/apiargs/MongoDBGridFSBucket-method-findOne-option.rst

Return Values
-------------

An array or object for the :term:`first document <natural order>` that matched
the query, or ``null`` if no document matched the query. The return type will
depend on the ``typeMap`` option.

.. todo: add examples

See Also
--------

- :phpmethod:`MongoDB\\Collection::findOne()`
- :phpmethod:`MongoDB\\GridFS\\Bucket::find()`
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Definition

.. code-block:: php

function getFileDocumentForStream($stream): object
function getFileDocumentForStream($stream): array|object

This method has the following parameters:

Expand All @@ -28,7 +28,8 @@ Definition
Return Values
-------------

The metadata document associated with the GridFS stream.
The metadata document associated with the GridFS stream. The return type will
depend on the bucket's ``typeMap`` option.

.. todo: add examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ Definition
Return Values
-------------

The ``_id`` field of the metadata document associated with the GridFS
stream.
The ``_id`` field of the metadata document associated with the GridFS stream.
The return type will depend on the bucket's ``typeMap`` option.

.. todo: add examples

Expand Down
1 change: 1 addition & 0 deletions src/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ public function selectGridFSBucket(array $options = [])
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];

Expand Down
80 changes: 66 additions & 14 deletions src/GridFS/Bucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class Bucket
{
private static $defaultBucketName = 'fs';
private static $defaultChunkSizeBytes = 261120;
private static $defaultTypeMap = [
'array' => 'MongoDB\Model\BSONArray',
'document' => 'MongoDB\Model\BSONDocument',
'root' => 'MongoDB\Model\BSONDocument',
];
private static $streamWrapperProtocol = 'gridfs';

private $collectionWrapper;
Expand All @@ -32,6 +37,7 @@ class Bucket
private $chunkSizeBytes;
private $readConcern;
private $readPreference;
private $typeMap;
private $writeConcern;

/**
Expand All @@ -49,6 +55,8 @@ class Bucket
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
*
* * typeMap (array): Default type map for cursors and BSON documents.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
*
* @param Manager $manager Manager instance from the driver
Expand Down Expand Up @@ -79,6 +87,10 @@ public function __construct(Manager $manager, $databaseName, array $options = []
throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
}

if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
}

if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
}
Expand All @@ -89,9 +101,10 @@ public function __construct(Manager $manager, $databaseName, array $options = []
$this->chunkSizeBytes = $options['chunkSizeBytes'];
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
$this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : self::$defaultTypeMap;
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();

$collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'writeConcern' => 1]);
$collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'typeMap' => 1, 'writeConcern' => 1]);

$this->collectionWrapper = new CollectionWrapper($manager, $databaseName, $options['bucketName'], $collectionOptions);
$this->registerStreamWrapper();
Expand All @@ -112,6 +125,7 @@ public function __debugInfo()
'chunkSizeBytes' => $this->chunkSizeBytes,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}
Expand Down Expand Up @@ -204,11 +218,25 @@ public function drop()
* @param array $options Additional options
* @return Cursor
*/
public function find($filter, array $options = [])
public function find($filter = [], array $options = [])
{
return $this->collectionWrapper->findFiles($filter, $options);
}

/**
* Finds a single document from the GridFS bucket's files collection
* matching the query.
*
* @see FindOne::__construct() for supported options
* @param array|object $filter Query by which to filter documents
* @param array $options Additional options
* @return array|object|null
*/
public function findOne($filter = [], array $options = [])
{
return $this->collectionWrapper->findOneFile($filter, $options);
}

/**
* Return the bucket name.
*
Expand All @@ -233,22 +261,15 @@ public function getDatabaseName()
* Gets the file document of the GridFS file associated with a stream.
*
* @param resource $stream GridFS stream
* @return stdClass
* @return array|object
* @throws InvalidArgumentException
*/
public function getFileDocumentForStream($stream)
{
if ( ! is_resource($stream) || get_resource_type($stream) != "stream") {
throw InvalidArgumentException::invalidType('$stream', $stream, 'resource');
}
$file = $this->getRawFileDocumentForStream($stream);

$metadata = stream_get_meta_data($stream);

if (!$metadata['wrapper_data'] instanceof StreamWrapper) {
throw InvalidArgumentException::invalidType('$stream wrapper data', $metadata['wrapper_data'], 'MongoDB\Driver\GridFS\StreamWrapper');
}

return $metadata['wrapper_data']->getFile();
// Filter the raw document through the specified type map
return \MongoDB\BSON\toPHP(\MongoDB\BSON\fromPHP($file), $this->typeMap);
}

/**
Expand All @@ -261,7 +282,13 @@ public function getFileDocumentForStream($stream)
*/
public function getFileIdForStream($stream)
{
$file = $this->getFileDocumentForStream($stream);
$file = $this->getRawFileDocumentForStream($stream);

/* Filter the raw document through the specified type map, but override
* the root type so we can reliably access the ID.
*/
$typeMap = ['root' => 'stdClass'] + $this->typeMap;
$file = \MongoDB\BSON\toPHP(\MongoDB\BSON\fromPHP($file), $typeMap);

if ( ! isset($file->_id) && ! property_exists($file, '_id')) {
throw new CorruptFileException('file._id does not exist');
Expand Down Expand Up @@ -466,6 +493,31 @@ private function getFilesNamespace()
return sprintf('%s.%s.files', $this->databaseName, $this->bucketName);
}

/**
* Gets the file document of the GridFS file associated with a stream.
*
* This returns the raw document from the StreamWrapper, which does not
* respect the Bucket's type map.
*
* @param resource $stream GridFS stream
* @return stdClass
* @throws InvalidArgumentException
*/
private function getRawFileDocumentForStream($stream)
{
if ( ! is_resource($stream) || get_resource_type($stream) != "stream") {
throw InvalidArgumentException::invalidType('$stream', $stream, 'resource');
}

$metadata = stream_get_meta_data($stream);

if (!$metadata['wrapper_data'] instanceof StreamWrapper) {
throw InvalidArgumentException::invalidType('$stream wrapper data', $metadata['wrapper_data'], 'MongoDB\Driver\GridFS\StreamWrapper');
}

return $metadata['wrapper_data']->getFile();
}

/**
* Opens a readable stream for the GridFS file.
*
Expand Down
17 changes: 15 additions & 2 deletions src/GridFS/CollectionWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public function deleteFileAndChunksById($id)
*/
public function dropCollections()
{
$this->filesCollection->drop();
$this->chunksCollection->drop();
$this->filesCollection->drop(['typeMap' => []]);
$this->chunksCollection->drop(['typeMap' => []]);
}

/**
Expand Down Expand Up @@ -140,6 +140,18 @@ public function findFiles($filter, array $options = [])
return $this->filesCollection->find($filter, $options);
}

/**
* Finds a single document from the GridFS bucket's files collection.
*
* @param array|object $filter Query by which to filter documents
* @param array $options Additional options
* @return array|object|null
*/
public function findOneFile($filter, array $options = [])
{
return $this->filesCollection->findOne($filter, $options);
}

/**
* Return the bucket name.
*
Expand Down Expand Up @@ -284,6 +296,7 @@ private function isFilesCollectionEmpty()
return null === $this->filesCollection->findOne([], [
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
'projection' => ['_id' => 1],
'typeMap' => [],
]);
}
}
Loading