Skip to content
Browse files

WIP fix for #70

  • Loading branch information...
1 parent 7fb075c commit c8ab5b4b6a3f440aaefcc873fa7b41b30aba59ff @jmikola jmikola committed Oct 26, 2012
Showing with 148 additions and 31 deletions.
  1. +71 −29 lib/Doctrine/MongoDB/GridFS.php
  2. +77 −2 tests/Doctrine/MongoDB/Tests/GridFSFileTest.php
View
100 lib/Doctrine/MongoDB/GridFS.php
@@ -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
+ /* 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.
+ *
+ * Before we check, 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']);
+ }
+
+ $newObjHasModifiers = false;
- // 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");
+ 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 update a file 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,
+ 'uplodaDate' => 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 */
View
79 tests/Doctrine/MongoDB/Tests/GridFSFileTest.php
@@ -154,8 +154,83 @@ public function testUpdate()
$gridFS->update(array('_id' => $id), array('_id' => $id));
$gridFS->update(array('_id' => $id), array('_id' => $id, 'boom' => true));
$check = $gridFS->findOne(array('_id' => $id));
- $this->assertTrue(isset($check['test']));
- $this->assertTrue(isset($check['boom']));
+ $this->assertFalse(array_key_exists('test', $check));
+ $this->assertTrue($check['boom']);
+ }
+
+ public function testUpsertDocumentWithoutFile()
+ {
+ $gridFS = $this->conn->selectDatabase(self::$dbName)->getGridFS();
+
+ $gridFS->update(
+ array('id' => 123),
+ array('x' => 1),
+ array('upsert' => true, 'multiple' => false)
+ );
+
+ $document = $gridFS->findOne();
+
+ $this->assertNotNull($document);
+ $this->assertNotEquals(123, $document['_id']);
+ $this->assertEquals(1, $document['x']);
+ }
+
+ public function testUpsertDocumentWithoutFileWithId()
+ {
+ $gridFS = $this->conn->selectDatabase(self::$dbName)->getGridFS();
+
+ $gridFS->update(
+ array('x' => 1),
+ array('_id' => 123),
+ array('upsert' => true, 'multiple' => false)
+ );
+
+ $document = $gridFS->findOne(array('_id' => 123));
+
+ $this->assertNotNull($document);
+ $this->assertFalse(array_key_exists('x', $document));
+ }
+
+ public function testUpsertModifierWithoutFile()
+ {
+ $gridFS = $this->conn->selectDatabase(self::$dbName)->getGridFS();
+
+ $gridFS->update(
+ array('_id' => 123),
+ array('$set' => array('x' => 1)),
+ array('upsert' => true, 'multiple' => false)
+ );
+
+ $document = $gridFS->findOne(array('_id' => 123));
+
+ $this->assertNotNull($document);
+ $this->assertEquals(1, $document['x']);
+ }
+
+ public function testUpsert()
+ {
+ $db = $this->conn->selectDatabase(self::$dbName);
+
+ $path = __DIR__.'/file.txt';
+ $gridFS = $db->getGridFS();
+ $file = new GridFSFile($path);
+
+ $newObj = array(
+ '$set' => array(
+ 'title' => 'Test Title',
+ 'file' => $file,
+ ),
+ );
+ $gridFS->update(array('_id' => 123), $newObj, array('upsert' => true, 'multiple' => false));
+
+ $document = $gridFS->findOne(array('_id' => 123));
+
+ $file = $document['file'];
+
+ $this->assertFalse($file->isDirty());
+ $this->assertEquals($path, $file->getFilename());
+ $this->assertEquals(file_get_contents($path), $file->getBytes());
+ $this->assertEquals(22, $file->getSize());
}
private function getMockPHPGridFSFile()

0 comments on commit c8ab5b4

Please sign in to comment.
Something went wrong with that request. Please try again.