diff --git a/src/MockCollection.php b/src/MockCollection.php index a8790ff..7802192 100644 --- a/src/MockCollection.php +++ b/src/MockCollection.php @@ -182,8 +182,18 @@ private function updateUpsert($filter, $update, $options, $anyUpdates) { if (array_key_exists('upsert', $options)) { if ($options['upsert'] && !$anyUpdates) { - if (array_key_exists('$set', $update)) { - $documents = [array_merge($filter, $update['$set'])]; + $set = $update['$set'] ?? []; + $setOnInsert = $update['$setOnInsert'] ?? []; + $commonFields = array_intersect_key($set, $setOnInsert); + + //Emulate Mongo behaviour + //@link https://stackoverflow.com/questions/23992723/findandmodify-fails-with-error-cannot-update-field1-and-field1-at-the-same/23994425#23994425 + if(!empty($commonFields)){ + throw new Exception('Operators $setOnInsert and $set cannot have the same field(s): '. + implode(', ', array_keys($commonFields))); + } + if (!empty($set) || !empty($setOnInsert)) { + $documents = [array_merge($filter, $set, $setOnInsert)]; } else { $documents = [$update]; } @@ -196,25 +206,27 @@ private function updateCore(&$doc, $update) { // The update operators are required, as exemplified here: // http://mongodb.github.io/mongo-php-library/tutorial/crud/ - $supported = ['$set', '$unset', '$inc', '$push']; + $supported = ['$set', '$unset', '$inc', '$push', '$setOnInsert']; $unsupported = array_diff(array_keys($update), $supported); if (count($unsupported) > 0) { throw new Exception("Unsupported update operators found: " . implode(', ', $unsupported)); } - foreach ($update['$set'] ?? [] as $k => $v) { - $dot = strpos($k, "."); - if ($dot !== false) { - $tmp = &$doc; - $keys = explode(".", $k); - if ($keys !== null) { - foreach ($keys as $key) { - $tmp = &$tmp[$key]; + foreach(['$set', '$setOnInsert'] as $operator){ + foreach ($update[$operator] ?? [] as $k => $v) { + $dot = strpos($k, "."); + if ($dot !== false) { + $tmp = &$doc; + $keys = explode(".", $k); + if ($keys !== null) { + foreach ($keys as $key) { + $tmp = &$tmp[$key]; + } + $tmp = $v; } - $tmp = $v; + } else { + $doc[$k] = $v; } - } else { - $doc[$k] = $v; } } @@ -225,7 +237,11 @@ private function updateCore(&$doc, $update) } foreach ($update['$inc'] ?? [] as $k => $v) { - if (isset($doc[$k]) && is_integer($v) && is_integer($doc[$k])) { + //Mongo behaviour: it increments even a non-existing field + if(!isset($doc[$k])){ + $doc[$k] = 0; + } + if (is_integer($v) && is_integer($doc[$k])) { $doc[$k] += $v; } } diff --git a/tests/MockCollectionTest.php b/tests/MockCollectionTest.php index fcc1d73..d9f11cb 100644 --- a/tests/MockCollectionTest.php +++ b/tests/MockCollectionTest.php @@ -570,13 +570,13 @@ public function updateUpsertCore($x1, $x2, $x3) $this->col->updateOne(['bar' => 4], $x3, ['upsert' => true]); self::assertThat($this->col->count(['bar' => 1, 'foo' => 'Kekse']), self::equalTo(1)); - if (array_key_exists('$set', $x3)) { + if (isset($x3['$set']) || isset($x3['$setOnInsert'])) { self::assertThat($this->col->count(['bar' => 1, 'foo' => 'bar']), self::equalTo(1)); } else { self::assertThat($this->col->count(['bar' => 1, 'foo' => 'bar']), self::equalTo(2)); } self::assertThat($this->col->count(['bar' => 2]), self::equalTo(1)); - if (array_key_exists('$set', $x3)) { + if (isset($x3['$set']) || isset($x3['$setOnInsert'])) { self::assertThat($this->col->count(['bar' => 4]), self::equalTo(1)); } else { self::assertThat($this->col->count(['bar' => 4]), self::equalTo(0)); @@ -596,6 +596,32 @@ public function testUpdateUpsertCanUpdateAndInsert() ); } + /** + * @depends testInsertManyInsertsDocuments + */ + public function testUpdateUpsertCanSetOnInsert() + { + $this->updateUpsertCore( + ['$setOnInsert' => ['foo' => 'Kekse']], + ['$setOnInsert' => ['foo' => 'Kekse']], + ['$setOnInsert' => ['foo' => 'bar']] + ); + } + + /** + * @depends testInsertManyInsertsDocuments + */ + public function testUpdateUpsertException() + { + $this->expectException(Exception::class); + $this->updateUpsertCore( + ['$setOnInsert' => ['foo' => 'Kekse' ], '$set' => ['foo' => 'Kekse_']], + ['$setOnInsert' => ['foo' => 'Kekse' ], '$set' => ['foo' => 'Kekse_']], + ['$setOnInsert' => ['foo' => 'bar' ], '$set' => ['foo' => 'bar_']] + ); + } + + /** * @depends testInsertManyInsertsDocuments */ @@ -608,6 +634,9 @@ public function testUpdateIncrement() $this->col->updateMany([], ['$inc' => ['bar' => 1]], ['upsert' => true]); self::assertThat($this->col->count(['bar' => 1]), self::equalTo(1)); + $this->col->updateMany([], ['$inc' => ['none' => 1]], ['upsert' => true]); + self::assertThat($this->col->count(['none' => 1]), self::equalTo(1)); + $this->col->insertOne( ['foo' => 'foo', 'bar' => 1] );