Skip to content
This repository has been archived by the owner on Nov 11, 2020. It is now read-only.

Commit

Permalink
Merge pull request #74 from doctrine/gridfs-fixes
Browse files Browse the repository at this point in the history
Redo GridFS upserts and fix up GridFS tests
  • Loading branch information
jmikola committed Dec 5, 2012
2 parents 9ac0ba4 + 5dde920 commit b086ccf
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 164 deletions.
11 changes: 1 addition & 10 deletions lib/Doctrine/MongoDB/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,18 +286,9 @@ protected function doFindAndRemove(array $query, array $options = array())
$command['remove'] = true;
$command = array_merge($command, $options);

$document = null;

$result = $this->database->command($command);

if (isset($result['value'])) {
$document = $result['value'];
if ($this->getMongoCollection() instanceof \MongoGridFS) {
// Remove the file data from the chunks collection
$this->getMongoCollection()->chunks->remove(array('files_id' => $document['_id']), $options);
}
}
return $document;
return isset($result['value']) ? $result['value'] : null;
}

public function findAndUpdate(array $query, array $newObj, array $options = array())
Expand Down
146 changes: 106 additions & 40 deletions lib/Doctrine/MongoDB/GridFS.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,48 +56,90 @@ protected function doUpdate($query, array $newObj, array $options = array())
if (is_scalar($query)) {
$query = array('_id' => $query);
}

$file = isset($newObj[$this->cmd.'set']['file']) ? $newObj[$this->cmd.'set']['file'] : null;
unset($newObj[$this->cmd.'set']['file']);

if ($file === null) {
$file = isset($newObj['file']) ? $newObj['file'] : null;
unset($newObj['file']);
}

// Has file to be persisted
if (isset($file) && $file->isDirty()) {
// It is impossible to update a file on the grid so we have to remove it and
// persist a new file with the same data
/* Before we inspect $newObj, remove an empty $set operator we may have
* left behind due to extracting the file field above.
*/
if (empty($newObj[$this->cmd.'set'])) {
unset($newObj[$this->cmd.'set']);
}

// First do a find and remove query to remove the file metadata and chunks so
// we can restore the file below
if (null === $document = $this->findAndRemove($query, $options)) {
throw new \Exception("Could not find original document containing file");
/* Determine if $newObj includes atomic modifiers, which will tell us if
* we can get by with a storeFile() in some situations or if a two-step
* storeFile() and update() is necessary.
*/
$newObjHasModifiers = false;

foreach (array_keys($newObj) as $key) {
if ($this->cmd === $key[0]) {
$newObjHasModifiers = true;
}

unset(
$document['filename'],
$document['length'],
$document['chunkSize'],
$document['uploadDate'],
$document['md5'],
$document['file']
);

// Store the file
$this->storeFile($file, $document, $options);
}

// Now send the original update bringing the file up to date
if ($newObj) {
if (isset($newObj['_id'])) {
unset($newObj['_id']);
$newObj = array($this->cmd.'set' => $newObj);
} elseif (isset($newObj[$this->cmd.'set']) && empty($newObj[$this->cmd.'set'])) {
$newObj[$this->cmd.'set'] = new \stdClass();
// Is there a file in need of persisting?
if (isset($file) && $file->isDirty()) {
/* It is impossible to overwrite a file's chunks in GridFS so we
* must remove it and re-persist a new file with the same data.
*
* First, use findAndRemove() to remove the file metadata and chunks
* prior to storing the file again below. Exclude metadata fields
* from the result, since those will be reset later.
*/
$document = $this->findAndRemove($query, array('fields' => array(
'filename' => 0,
'length' => 0,
'chunkSize' => 0,
'uploadDate' => 0,
'md5' => 0,
'file' => 0,
)));

/* If findAndRemove() returned nothing (no match or removal), create
* a new document with the query's "_id" if available.
*/
if (!isset($document)) {
/* If $newObj had modifiers, we'll need to do an update later,
* so default to an empty array for now. Otherwise, we can do
* without that update and store $newObj now.
*/
$document = $newObjHasModifiers ? array() : $newObj;

/* If the document has no "_id" but there was one in the query
* or $newObj, we can use that instead of having storeFile()
* generate one.
*/
if (!isset($document['_id']) && isset($query['_id'])) {
$document['_id'] = $query['_id'];
}

if (!isset($document['_id']) && isset($newObj['_id'])) {
$document['_id'] = $newObj['_id'];
}
}

// Document will definitely have an "_id" after storing the file.
$this->storeFile($file, $document);

if (!$newObjHasModifiers) {
/* TODO: MongoCollection::update() would return a boolean if
* $newObj was not empty, or an array describing the update
* operation. Improvise, since we only stored the file and that
* returns the "_id" field.
*/
return true;
}
$this->getMongoCollection()->update($query, $newObj, $options);
}
return $newObj;

// Now send the original update bringing the file up to date
return $this->getMongoCollection()->update($query, $newObj, $options);
}

/** @override */
Expand All @@ -111,18 +153,24 @@ protected function doBatchInsert(array &$a, array $options = array())
/** @override */
protected function doInsert(array &$a, array $options = array())
{
// If file exists and is dirty then lets persist the file and store the file path or the bytes
if (isset($a['file'])) {
$file = $a['file']; // instanceof GridFSFile
unset($a['file']);
if ($file->isDirty()) {
$this->storeFile($file, $a, $options);
} else {
parent::doInsert($a, $options);
}
// If there is no file, perform a basic insertion
if (!isset($a['file'])) {
parent::doInsert($a, $options);
return;
}

/* If the file is dirty (i.e. it must be persisted), delegate to the
* storeFile() method. Otherwise, perform a basic insertion.
*/
$file = $a['file']; // instanceof GridFSFile
unset($a['file']);

if ($file->isDirty()) {
$this->storeFile($file, $a, $options);
} else {
parent::doInsert($a, $options);
}

$a['file'] = $file;
return $a;
}
Expand All @@ -147,16 +195,34 @@ protected function doSave(array &$a, array $options = array())
*/
public function storeFile($file, array &$document, array $options = array())
{
if (is_string($file)) {
if (!$file instanceof GridFSFile) {
$file = new GridFSFile($file);
}

if ($file->hasUnpersistedFile()) {
$id = $this->getMongoCollection()->storeFile($file->getFilename(), $document, $options);
} else {
$id = $this->getMongoCollection()->storeBytes($file->getBytes(), $document, $options);
}

$document = array_merge(array('_id' => $id), $document);
$file->setMongoGridFSFile(new \MongoGridFSFile($this->getMongoCollection(), $document));
$gridFsFile = $this->getMongoCollection()->get($id);

// TODO: Consider throwing exception if file cannot be fetched
$file->setMongoGridFSFile($this->getMongoCollection()->get($id));

return $file;
}

protected function doFindAndRemove(array $query, array $options = array())
{
$document = parent::doFindAndRemove($query, $options);

if (isset($document)) {
// Remove the file data from the chunks collection
$this->getMongoCollection()->chunks->remove(array('files_id' => $document['_id']), $options);
}

return $document;
}
}
14 changes: 7 additions & 7 deletions lib/Doctrine/MongoDB/GridFSFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public function getBytes()
if ($this->isDirty && $this->bytes) {
return $this->bytes;
}
if ($this->filename) {
if ($this->isDirty && $this->filename) {
return file_get_contents($this->filename);
}
if ($this->mongoGridFSFile instanceof \MongoGridFSFile) {
Expand Down Expand Up @@ -195,13 +195,13 @@ public function write($filename)
/**
* Check if the file is dirty or set isDirty by passing a boolean argument.
*
* @param boolean $bool
* @param boolean $isDirty
* @param boolean
*/
public function isDirty($bool = null)
public function isDirty($isDirty = null)
{
if ($bool !== null) {
$this->isDirty = $bool;
if ($isDirty !== null) {
$this->isDirty = (boolean) $isDirty;
}
return $this->isDirty;
}
Expand All @@ -213,7 +213,7 @@ public function isDirty($bool = null)
*/
public function hasUnpersistedBytes()
{
return $this->isDirty && $this->bytes ? true : false;
return ($this->isDirty && $this->bytes);
}

/**
Expand All @@ -223,6 +223,6 @@ public function hasUnpersistedBytes()
*/
public function hasUnpersistedFile()
{
return $this->isDirty && $this->filename ? true : false;
return ($this->isDirty && $this->filename);
}
}
Loading

0 comments on commit b086ccf

Please sign in to comment.