Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/DocumentStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,11 @@ public function getDoc(string $collectionName, string $docId): ?array;
* @throws UnknownCollection
*/
public function filterDocs(string $collectionName, Filter $filter, int $skip = null, int $limit = null, OrderBy $orderBy = null): \Traversable;

/**
* @param string $collectionName
* @param Filter $filter
* @return array
*/
public function filterDocIds(string $collectionName, Filter $filter): array;
}
60 changes: 58 additions & 2 deletions src/InMemoryDocumentStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public function updateDoc(string $collectionName, string $docId, array $docOrSub
$this->assertDocExists($collectionName, $docId);
$this->assertUniqueConstraints($collectionName, $docId, $docOrSubset);

$this->inMemoryConnection['documents'][$collectionName][$docId] = \array_replace_recursive(
$this->inMemoryConnection['documents'][$collectionName][$docId] = $this->arrayReplaceRecursiveAssocOnly(
$this->inMemoryConnection['documents'][$collectionName][$docId],
$docOrSubset
);
Expand Down Expand Up @@ -323,6 +323,27 @@ public function filterDocs(
return new \ArrayIterator($filteredDocs);
}

/**
* @param string $collectionName
* @param Filter $filter
* @return array
*/
public function filterDocIds(
string $collectionName,
Filter $filter
): array {
$this->assertHasCollection($collectionName);

$docIds = [];
foreach ($this->inMemoryConnection['documents'][$collectionName] as $docId => $doc) {
if ($filter->match($doc, (string)$docId)) {
$docIds[] = $docId;
}
}

return $docIds;
}

private function hasDoc(string $collectionName, string $docId): bool
{
if (! $this->hasCollection($collectionName)) {
Expand Down Expand Up @@ -404,7 +425,7 @@ private function assertMultiFieldUniqueConstraint(string $collectionName, string

if($this->hasDoc($collectionName, $docId)) {
$effectedDoc = $this->getDoc($collectionName, $docId);
$docOrSubset = \array_replace_recursive($effectedDoc, $docOrSubset);
$docOrSubset = $this->arrayReplaceRecursiveAssocOnly($effectedDoc, $docOrSubset);
}

$reader = new ArrayReader($docOrSubset);
Expand Down Expand Up @@ -516,4 +537,39 @@ private function sort(&$docs, OrderBy $orderBy)
return $docCmp($docA, $docB, $orderBy);
});
}

/**
* The internal array_replace_recursive function also replaces sequential arrays recursively. This method aims to
* behave identical to array_replace_recursive but only when dealing with associative arrays. Sequential arrays
* are handled as if they were scalar types instead.
*
* @param array $array1
* @param array $array2
* @return array
*/
private function arrayReplaceRecursiveAssocOnly(array $array1, array $array2): array

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsor @heiglandreas can you take a look at these changes as well? It only affects testing environment and should not cause any existing tests to fail, but at least you should know about the change ;)

{
foreach ($array2 as $key2 => $value2) {
$bothValuesArraysAndAtLeastOneAssoc = \is_array($value2) &&
isset($array1[$key2]) && \is_array($array1[$key2]) &&
!($this->isSequentialArray($value2) && $this->isSequentialArray($array1[$key2]));

if ($bothValuesArraysAndAtLeastOneAssoc) {
$array1[$key2] = $this->arrayReplaceRecursiveAssocOnly($array1[$key2], $value2);
} else {
$array1[$key2] = $value2;
}
}

return $array1;
}

private function isSequentialArray(array $array): bool
{
if (\count($array) === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would write that as if ($array === []) { as that's one function call less ;-)

return true;
}

return \array_keys($array) === \range(0, \count($array) - 1);
}
}
71 changes: 71 additions & 0 deletions tests/InMemoryDocumentStoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
namespace EventEngineTest\DocumentStore;

use EventEngine\DocumentStore\FieldIndex;
use EventEngine\DocumentStore\Filter\AndFilter;
use EventEngine\DocumentStore\Filter\AnyFilter;
use EventEngine\DocumentStore\Filter\EqFilter;
use EventEngine\DocumentStore\Filter\GtFilter;
use EventEngine\DocumentStore\Filter\LtFilter;
use EventEngine\DocumentStore\Filter\OrFilter;
use EventEngine\DocumentStore\InMemoryDocumentStore;
use EventEngine\DocumentStore\MultiFieldIndex;
use EventEngine\Persistence\InMemoryConnection;
Expand Down Expand Up @@ -105,6 +109,25 @@ public function it_updates_a_subset_of_a_doc()
$this->assertEquals(42, $filteredDocs[0]['some']['other']['nested']);
}

/**
* @test
*/
public function it_retrieves_doc_ids_by_filter()
{
$this->store->addCollection('test');

$this->store->addDoc('test', 'a', ['number' => 10]);
$this->store->addDoc('test', 'b', ['number' => 20]);
$this->store->addDoc('test', 'c', ['number' => 30]);

$result = $this->store->filterDocIds('test', new OrFilter(
new GtFilter('number', 21),
new LtFilter('number', 19)
));

$this->assertEquals(['a', 'c'], $result);
}

/**
* @test
*/
Expand Down Expand Up @@ -303,4 +326,52 @@ public function it_deletes_many()
$this->assertCount(1, $filteredDocs);
$this->assertEquals(['some' => ['prop' => 'bar']], $filteredDocs[0]);
}

/**
* @test
*/
public function it_does_not_update_numeric_arrays_recursively()
{
$this->store->addCollection('test');

$this->store->addDoc('test', 'doc', [
'a' => ['a' => 10, 'b' => 20],
'b' => [10, 20, 30],
'c' => [],
'd' => [false, true],
'e' => ['a' => 'b'],
'f' => [10, 20],
'g' => ['x' => 10, 'y' => 20],
'h' => [11],
'j' => 'foo'
]);

$this->store->updateDoc('test', 'doc', [
'a' => ['b' => 21, 'c' => 30],
'b' => [10, 30],
'c' => [true],
'd' => [],
'e' => [],
'f' => ['x' => 10, 'y' => 20],
'g' => [30, 40],
'i' => [22],
'j' => ['bar']
]);

$this->assertEquals(
[
'a' => ['a' => 10, 'b' => 21, 'c' => 30],
'b' => [10, 30],
'c' => [true],
'd' => [],
'e' => ['a' => 'b'],
'f' => [10, 20, 'x' => 10, 'y' => 20],
'g' => ['x' => 10, 'y' => 20, 30, 40],
'h' => [11],
'i' => [22],
'j' => ['bar']
],
$this->store->getDoc('test', 'doc')
);
}
}