Skip to content

Commit

Permalink
Implement findAndModify command as Collection::modify()
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Dec 30, 2023
1 parent 261421e commit 6aaff17
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 2 deletions.
27 changes: 25 additions & 2 deletions src/main/php/com/mongodb/Collection.class.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php namespace com\mongodb;

use com\mongodb\io\{Commands, Protocol};
use com\mongodb\result\{Insert, Update, Delete, Cursor, Run, ChangeStream};
use com\mongodb\result\{Insert, Update, Delete, Modification, Cursor, Run, ChangeStream};
use lang\Value;
use util\Objects;

Expand Down Expand Up @@ -97,7 +97,7 @@ public function upsert($query, $arg, Options... $options): Update {
}

/**
* Updates collection with given modifications.
* Updates collection with given statements.
*
* @param string|com.mongodb.ObjectId|[:var] $query
* @param [:var] $statements Update operator expressions
Expand All @@ -118,6 +118,29 @@ public function update($query, $statements, Options... $options): Update {
return new Update($result['body']);
}

/**
* Modifies collection with given statements and returns a `Modification`
* instance with the modified document.
*
* @param string|com.mongodb.ObjectId|[:var] $query
* @param [:var] $statements Update operator expressions
* @param bool $upsert
* @param com.mongodb.Options... $options
* @return com.mongodb.result.Modification
* @throws com.mongodb.Error
*/
public function modify($query, $statements, $upsert= false, Options... $options): Modification {
$result= $this->proto->write($options, [
'findAndModify' => $this->name,
'query' => is_array($query) ? $query : ['_id' => $query],
'update' => $statements,
'new' => true,
'upsert' => $upsert,
'$db' => $this->database,
]);
return new Modification($result['body']);
}

/**
* Delete documents
*
Expand Down
33 changes: 33 additions & 0 deletions src/main/php/com/mongodb/result/Modification.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php namespace com\mongodb\result;

use com\mongodb\Document;

/** @test com.mongodb.unittest.result.ModificationTest */
class Modification extends Result {

/** Returns number of modified documents */
public function modified(): int { return $this->result['lastErrorObject']['n']; }

/** Returns whether an existing document was updated */
public function updatedExisting(): bool {
return $this->result['lastErrorObject']['updatedExisting'];
}

/**
* Returns the upserted ID, if any
*
* @return ?com.mongodb.ObjectId
*/
public function upserted() {
return $this->result['lastErrorObject']['upserted'] ?? null;
}

/**
* Returns the document
*
* @return ?com.mongodb.Document
*/
public function document() {
return isset($this->result['value']) ? new Document($this->result['value']) : null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php namespace com\mongodb\unittest\result;

use com\mongodb\result\Modification;
use com\mongodb\{Document, ObjectId};
use test\{Assert, Before, Test};

class ModificationTest {
private $objectId;

/** Creates a result */
private function result(Document $document= null, $created= false) {
if (null === $document) {
$lastErrorObject= ['n' => 0, 'updatedExisting' => false];
$value= null;
} else if ($created) {
$lastErrorObject= ['n' => 1, 'updatedExisting' => false, 'upserted' => $document->id()];
$value= $document->properties();
} else {
$lastErrorObject= ['n' => 1, 'updatedExisting' => true];
$value= $document->properties();
}
return ['lastErrorObject' => $lastErrorObject, 'value' => $value, 'ok' => 1];
}

#[Before]
public function objectId() {
$this->objectId= ObjectId::create();
}

#[Test]
public function can_create() {
new Modification($this->result());
}

#[Test]
public function none_modified() {
Assert::equals(0, (new Modification($this->result()))->modified());
}

#[Test]
public function modified() {
$doc= new Document(['test' => true]);
Assert::equals(1, (new Modification($this->result($doc)))->modified());
}

#[Test]
public function updated_existing() {
$doc= new Document(['_id' => $this->objectId, 'test' => true]);
Assert::true((new Modification($this->result($doc)))->updatedExisting());
}

#[Test]
public function created_new() {
$doc= new Document(['_id' => $this->objectId, 'test' => true]);
Assert::false((new Modification($this->result($doc, true)))->updatedExisting());
}

#[Test]
public function not_upserted() {
$doc= new Document(['_id' => $this->objectId, 'test' => true]);
Assert::null((new Modification($this->result($doc)))->upserted());
}

#[Test]
public function upserted_id() {
$doc= new Document(['_id' => $this->objectId, 'test' => true]);
Assert::equals($this->objectId, (new Modification($this->result($doc, true)))->upserted());
}

#[Test]
public function document() {
$doc= new Document(['_id' => $this->objectId, 'test' => true]);
Assert::equals($doc, (new Modification($this->result($doc)))->document());
}

#[Test]
public function no_document() {
Assert::null((new Modification($this->result()))->document());
}
}

0 comments on commit 6aaff17

Please sign in to comment.