Skip to content

Commit 7e39c67

Browse files
committed
Add count/sum nesting impl
1 parent d97d181 commit 7e39c67

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

src/Database/Database.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6947,9 +6947,22 @@ public function count(string $collection, array $queries = [], ?int $max = null)
69476947
$skipAuth = true;
69486948
}
69496949

6950+
$relationships = \array_filter(
6951+
$collection->getAttribute('attributes', []),
6952+
fn (Document $attribute) => $attribute->getAttribute('type') === self::VAR_RELATIONSHIP
6953+
);
6954+
69506955
$queries = Query::groupByType($queries)['filters'];
69516956
$queries = $this->convertQueries($collection, $queries);
69526957

6958+
$queriesOrNull = $this->convertRelationshipFiltersToSubqueries($relationships, $queries);
6959+
6960+
if ($queriesOrNull === null) {
6961+
return 0;
6962+
}
6963+
6964+
$queries = $queriesOrNull;
6965+
69536966
$getCount = fn () => $this->adapter->count($collection, $queries, $max);
69546967
$count = $skipAuth ?? false ? Authorization::skip($getCount) : $getCount();
69556968

@@ -6993,8 +7006,22 @@ public function sum(string $collection, string $attribute, array $queries = [],
69937006
}
69947007
}
69957008

7009+
$relationships = \array_filter(
7010+
$collection->getAttribute('attributes', []),
7011+
fn (Document $attribute) => $attribute->getAttribute('type') === self::VAR_RELATIONSHIP
7012+
);
7013+
69967014
$queries = $this->convertQueries($collection, $queries);
69977015

7016+
$queriesOrNull = $this->convertRelationshipFiltersToSubqueries($relationships, $queries);
7017+
7018+
// If conversion returns null, it means no documents can match (relationship filter found no matches)
7019+
if ($queriesOrNull === null) {
7020+
return 0;
7021+
}
7022+
7023+
$queries = $queriesOrNull;
7024+
69987025
$sum = $this->adapter->sum($collection, $attribute, $queries, $max);
69997026

70007027
$this->trigger(self::EVENT_DOCUMENT_SUM, $sum);

tests/e2e/Adapter/Scopes/RelationshipTests.php

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4017,4 +4017,156 @@ public function testNestedRelationshipQueriesMultipleDepths(): void
40174017
$database->deleteCollection('departments_nested');
40184018
$database->deleteCollection('companies_nested');
40194019
}
4020+
4021+
public function testCountAndSumWithRelationshipQueries(): void
4022+
{
4023+
/** @var Database $database */
4024+
$database = static::getDatabase();
4025+
4026+
if (!$database->getAdapter()->getSupportForRelationships()) {
4027+
$this->expectNotToPerformAssertions();
4028+
return;
4029+
}
4030+
4031+
// Create Author -> Posts relationship with view count
4032+
$database->createCollection('authors_count');
4033+
$database->createCollection('posts_count');
4034+
4035+
$database->createAttribute('authors_count', 'name', Database::VAR_STRING, 255, true);
4036+
$database->createAttribute('authors_count', 'age', Database::VAR_INTEGER, 0, true);
4037+
$database->createAttribute('posts_count', 'title', Database::VAR_STRING, 255, true);
4038+
$database->createAttribute('posts_count', 'views', Database::VAR_INTEGER, 0, true);
4039+
$database->createAttribute('posts_count', 'published', Database::VAR_BOOLEAN, 0, true);
4040+
4041+
$database->createRelationship(
4042+
collection: 'authors_count',
4043+
relatedCollection: 'posts_count',
4044+
type: Database::RELATION_ONE_TO_MANY,
4045+
twoWay: true,
4046+
id: 'posts',
4047+
twoWayKey: 'author'
4048+
);
4049+
4050+
// Create test data
4051+
$author1 = $database->createDocument('authors_count', new Document([
4052+
'$id' => 'author1',
4053+
'$permissions' => [Permission::read(Role::any())],
4054+
'name' => 'Alice',
4055+
'age' => 30,
4056+
]));
4057+
4058+
$author2 = $database->createDocument('authors_count', new Document([
4059+
'$id' => 'author2',
4060+
'$permissions' => [Permission::read(Role::any())],
4061+
'name' => 'Bob',
4062+
'age' => 25,
4063+
]));
4064+
4065+
$author3 = $database->createDocument('authors_count', new Document([
4066+
'$id' => 'author3',
4067+
'$permissions' => [Permission::read(Role::any())],
4068+
'name' => 'Charlie',
4069+
'age' => 35,
4070+
]));
4071+
4072+
// Create posts
4073+
$database->createDocument('posts_count', new Document([
4074+
'$id' => 'post1',
4075+
'$permissions' => [Permission::read(Role::any())],
4076+
'title' => 'Alice Post 1',
4077+
'views' => 100,
4078+
'published' => true,
4079+
'author' => 'author1',
4080+
]));
4081+
4082+
$database->createDocument('posts_count', new Document([
4083+
'$id' => 'post2',
4084+
'$permissions' => [Permission::read(Role::any())],
4085+
'title' => 'Alice Post 2',
4086+
'views' => 200,
4087+
'published' => true,
4088+
'author' => 'author1',
4089+
]));
4090+
4091+
$database->createDocument('posts_count', new Document([
4092+
'$id' => 'post3',
4093+
'$permissions' => [Permission::read(Role::any())],
4094+
'title' => 'Alice Draft',
4095+
'views' => 50,
4096+
'published' => false,
4097+
'author' => 'author1',
4098+
]));
4099+
4100+
$database->createDocument('posts_count', new Document([
4101+
'$id' => 'post4',
4102+
'$permissions' => [Permission::read(Role::any())],
4103+
'title' => 'Bob Post',
4104+
'views' => 150,
4105+
'published' => true,
4106+
'author' => 'author2',
4107+
]));
4108+
4109+
$database->createDocument('posts_count', new Document([
4110+
'$id' => 'post5',
4111+
'$permissions' => [Permission::read(Role::any())],
4112+
'title' => 'Bob Draft',
4113+
'views' => 75,
4114+
'published' => false,
4115+
'author' => 'author2',
4116+
]));
4117+
4118+
// Test: Count posts by author name
4119+
$count = $database->count('posts_count', [
4120+
Query::equal('author.name', ['Alice']),
4121+
]);
4122+
$this->assertEquals(3, $count);
4123+
4124+
// Test: Count published posts by author age filter
4125+
$count = $database->count('posts_count', [
4126+
Query::lessThan('author.age', 30),
4127+
Query::equal('published', [true]),
4128+
]);
4129+
$this->assertEquals(1, $count); // Only Bob's published post
4130+
4131+
// Test: Count posts by author name (different author)
4132+
$count = $database->count('posts_count', [
4133+
Query::equal('author.name', ['Bob']),
4134+
]);
4135+
$this->assertEquals(2, $count);
4136+
4137+
// Test: Count with no matches (author with no posts)
4138+
$count = $database->count('posts_count', [
4139+
Query::equal('author.name', ['Charlie']),
4140+
]);
4141+
$this->assertEquals(0, $count);
4142+
4143+
// Test: Sum views for posts by author name
4144+
$sum = $database->sum('posts_count', 'views', [
4145+
Query::equal('author.name', ['Alice']),
4146+
]);
4147+
$this->assertEquals(350, $sum); // 100 + 200 + 50
4148+
4149+
// Test: Sum views for published posts by author age
4150+
$sum = $database->sum('posts_count', 'views', [
4151+
Query::lessThan('author.age', 30),
4152+
Query::equal('published', [true]),
4153+
]);
4154+
$this->assertEquals(150, $sum); // Only Bob's published post
4155+
4156+
// Test: Sum views for Bob's posts
4157+
$sum = $database->sum('posts_count', 'views', [
4158+
Query::equal('author.name', ['Bob']),
4159+
]);
4160+
$this->assertEquals(225, $sum); // 150 + 75
4161+
4162+
// Test: Sum with no matches
4163+
$sum = $database->sum('posts_count', 'views', [
4164+
Query::equal('author.name', ['Charlie']),
4165+
]);
4166+
$this->assertEquals(0, $sum);
4167+
4168+
// Clean up
4169+
$database->deleteCollection('authors_count');
4170+
$database->deleteCollection('posts_count');
4171+
}
40204172
}

0 commit comments

Comments
 (0)