diff --git a/src/InMemoryDocumentStore.php b/src/InMemoryDocumentStore.php index d7e2c25..361eb07 100644 --- a/src/InMemoryDocumentStore.php +++ b/src/InMemoryDocumentStore.php @@ -379,9 +379,15 @@ private function assertUniqueFieldConstraint(string $collectionName, string $doc $check = new EqFilter($index->field(), $value); - $existingDocs = $this->filterDocs($collectionName, $check); + foreach ($this->inMemoryConnection['documents'][$collectionName] as $existingDocId => $existingDoc) { + if (!$check->match($existingDoc, (string)$existingDocId)) { + continue; + } + + if ((string)$existingDocId === $docId) { + continue; + } - foreach ($existingDocs as $existingDoc) { throw new RuntimeException( $errMsg ?? "Unique constraint violation. Cannot insert or update document with id $docId, because a document with same value for field: {$index->field()} exists already!" ); @@ -434,9 +440,15 @@ private function assertMultiFieldUniqueConstraint(string $collectionName, string $checkList = $checkList[0]; } - $existingDocs = $this->filterDocs($collectionName, $checkList); + foreach ($this->inMemoryConnection['documents'][$collectionName] as $existingDocId => $existingDoc) { + if (!$checkList->match($existingDoc, (string)$existingDocId)) { + continue; + } + + if ((string)$existingDocId === $docId) { + continue; + } - foreach ($existingDocs as $existingDoc) { $fieldNamesStr = implode(", ", $fieldNames); throw new RuntimeException( $errMsg ?? "Unique constraint violation. Cannot insert or update document with id $docId, because a document with same values for fields: {$fieldNamesStr} exists already!" diff --git a/tests/InMemoryDocumentStoreTest.php b/tests/InMemoryDocumentStoreTest.php index ba03c86..641e112 100644 --- a/tests/InMemoryDocumentStoreTest.php +++ b/tests/InMemoryDocumentStoreTest.php @@ -119,7 +119,32 @@ public function it_ensures_unique_constraints_for_a_field() $this->store->addDoc('test', '3', ['some' => ['prop' => 'foo']]); } + /** + * @test + */ + public function it_ensures_unique_constraints_for_a_field_for_update() + { + $this->store->addCollection('test', FieldIndex::namedIndexForField('unique_prop_idx', 'some.prop', FieldIndex::SORT_ASC, true)); + + $this->store->addDoc('test', '1', ['some' => ['prop' => 'foo']]); + $this->store->addDoc('test', '2', ['some' => ['prop' => 'bar']]); + + $this->expectExceptionMessageRegExp('/^Unique constraint violation/'); + $this->store->updateDoc('test', '2', ['some' => ['prop' => 'foo']]); + } + /** + * @test + * @doesNotPerformAssertions + */ + public function it_allows_updating_with_unique_constraints_for_a_field() + { + $this->store->addCollection('test', FieldIndex::namedIndexForField('unique_prop_idx', 'some.prop', FieldIndex::SORT_ASC, true)); + + $this->store->addDoc('test', '1', ['some' => ['prop' => 'foo']]); + + $this->store->updateDoc('test', '1', ['some' => ['prop' => 'foo', 'new' => 'prop']]); + } /** * @test @@ -130,6 +155,22 @@ public function it_ensures_unique_constraints_for_multiple_fields() $this->store->addCollection('test', $multiFieldIndex); + $this->store->addDoc('test', '1', ['some' => ['prop' => 'foo', 'other' => ['prop' => 'bat']]]); + $this->store->addDoc('test', '2', ['some' => ['prop' => 'bar', 'other' => ['prop' => 'bat']]]); + + $this->expectExceptionMessageRegExp('/^Unique constraint violation/'); + $this->store->addDoc('test', '4', ['some' => ['prop' => 'foo', 'other' => ['prop' => 'bat']]]); + } + + /** + * @test + */ + public function it_ensures_unique_constraints_for_multiple_fields_for_update() + { + $multiFieldIndex = MultiFieldIndex::forFields(['some.prop', 'some.other.prop'], true); + + $this->store->addCollection('test', $multiFieldIndex); + $this->store->addDoc('test', '1', ['some' => ['prop' => 'foo', 'other' => ['prop' => 'bat']]]); $this->store->addDoc('test', '2', ['some' => ['prop' => 'bar', 'other' => ['prop' => 'bat']]]); $this->store->addDoc('test', '3', ['some' => ['prop' => 'bar']]); @@ -138,6 +179,21 @@ public function it_ensures_unique_constraints_for_multiple_fields() $this->store->updateDoc('test', '2', ['some' => ['prop' => 'foo']]); } + /** + * @test + * @doesNotPerformAssertions + */ + public function it_allows_updating_with_unique_constraints_for_multiple_fields() + { + $multiFieldIndex = MultiFieldIndex::forFields(['some.prop', 'some.other.prop'], true); + + $this->store->addCollection('test', $multiFieldIndex); + + $this->store->addDoc('test', '1', ['some' => ['prop' => 'foo', 'other' => ['prop' => 'bat']]]); + + $this->store->updateDoc('test', '1', ['some' => ['prop' => 'foo', 'other' => ['prop' => 'bat'], 'new' => 'prop']]); + } + /** * @test */