Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new aggregation pipeline stages in builder #2513

Merged
merged 23 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1f289a3
Add $densify stage to aggregation pipeline builder
alcaeus Mar 14, 2023
b10aed7
Add $fill stage to aggregation pipeline builder
alcaeus Mar 14, 2023
4da931e
Add $merge stage to aggregation pipeline builder
alcaeus Mar 14, 2023
8643261
Add $replaceWith stage to aggregation pipeline builder
alcaeus Mar 14, 2023
3c1ee8d
Add $set stage to aggregation pipeline builder
alcaeus Mar 14, 2023
6a3dd48
Add $unset stage to aggregation pipeline builder
alcaeus Mar 14, 2023
f1bd9a7
Add $unionWith stage to aggregation pipeline builder
alcaeus Mar 14, 2023
e35e5d8
Use templates for Builder::addStage
alcaeus Mar 15, 2023
4fe0c0f
Add template covariance error to psalm baseline
alcaeus Mar 22, 2023
c1117f2
Remove unnecessary abstraction for $addFields and $set
alcaeus Mar 23, 2023
6f72a6a
Use array_fields for lists in variadic arguments
alcaeus Mar 23, 2023
cc4abd9
Improve wording when field name is required in Expr classes
alcaeus Mar 23, 2023
227186b
Add missing type to whenMatched option
alcaeus Mar 23, 2023
acfe974
Update type for let option
alcaeus Mar 23, 2023
0568e90
Use ? syntax for nullable type
alcaeus Mar 23, 2023
7cd1777
Rename test methods to hide copy/paste
alcaeus Mar 23, 2023
f831285
Test all range options for $densify
alcaeus Mar 23, 2023
742d27c
Test complex values and sort for $fill
alcaeus Mar 23, 2023
b8c6aff
Test reusing same builder for $merge
alcaeus Mar 23, 2023
e517015
Simplify creation of UTCDateTime instances in tests
alcaeus Mar 23, 2023
77d5827
Allow expressions as partition in $fill stage
alcaeus Mar 23, 2023
67847c5
Define psalm types for most pipeline stages
alcaeus Mar 23, 2023
3596a4c
Improve handling of required options as typed arguments
alcaeus Mar 24, 2023
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
Prev Previous commit
Next Next commit
Add $unionWith stage to aggregation pipeline builder
  • Loading branch information
alcaeus committed Mar 17, 2023
commit f1bd9a73d2f7003cd0fa85413723934e5e4d5429
15 changes: 15 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,21 @@ public function sortByCount(string $expression): Stage\SortByCount
return $stage;
}

/**
* Performs a union of two collections. $unionWith combines pipeline results
* from two collections into a single result set. The stage outputs the
* combined result set (including duplicates) to the next stage.
*
* @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unionWith/
*/
public function unionWith(string $collection): Stage\UnionWith
{
$stage = new Stage\UnionWith($this, $this->dm, $collection);
$this->addStage($stage);

return $stage;
}

/**
* Removes/excludes fields from documents.
*
Expand Down
12 changes: 12 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,18 @@ public function sort($fieldName, $order = null): Stage\Sort
return $this->builder->sort($fieldName, $order);
}

/**
* Performs a union of two collections. $unionWith combines pipeline results
* from two collections into a single result set. The stage outputs the
* combined result set (including duplicates) to the next stage.
*
* @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unionWith/
*/
public function unionWith(string $collection): Stage\UnionWith
{
return $this->builder->unionWith($collection);
}

/**
* Removes/excludes fields from documents.
*
Expand Down
75 changes: 75 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Doctrine\ODM\MongoDB\Aggregation\Stage;

use Doctrine\ODM\MongoDB\Aggregation\Builder;
use Doctrine\ODM\MongoDB\Aggregation\Stage;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\Persistence\Mapping\MappingException;
use InvalidArgumentException;

/**
* Fluent interface for adding a $unionWith stage to an aggregation pipeline.
*
* @psalm-type Pipeline = Builder|Stage|list<array<string, mixed>>
*/
class UnionWith extends Stage
{
private DocumentManager $dm;

private string $collection;

/**
* @var array|Builder|null
* @psalm-var Pipeline|null
*/
private $pipeline = null;

public function __construct(Builder $builder, DocumentManager $documentManager, string $collection)
{
parent::__construct($builder);

$this->dm = $documentManager;

try {
$class = $this->dm->getClassMetadata($collection);
$this->collection = $class->getCollection();
} catch (MappingException $e) {
$this->collection = $collection;
}
}

/**
* @param array|Builder|Stage $pipeline
* @psalm-param Pipeline $pipeline
*/
public function pipeline($pipeline): self
{
if ($pipeline instanceof Stage) {
$this->pipeline = $pipeline->builder;
} else {
$this->pipeline = $pipeline;
}

if ($this->builder === $this->pipeline) {
throw new InvalidArgumentException('Cannot use the same Builder instance for $unionWith pipeline.');
}

return $this;
}

public function getExpression(): array
{
$params = (object) ['coll' => $this->collection];

if ($this->pipeline) {
$params->pipeline = $this->pipeline instanceof Builder
? $this->pipeline->getPipeline(false)
: $this->pipeline;
}

return ['$unionWith' => $params];
}
}
1 change: 1 addition & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ parameters:
- lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php
- lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php
- lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php
- lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php

# $this->mapping['targetDocument'] is class-string<T>
-
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Doctrine\ODM\MongoDB\Tests\Aggregation\Stage;

use Doctrine\ODM\MongoDB\Tests\BaseTest;
use Documents\SimpleReferenceUser;
use Documents\User;

class UnionWithTest extends BaseTest
{
public function testUnionWithStageWithClassName(): void
{
$builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class);
$builder
->unionWith(User::class);

$expectedPipeline = [
['$unionWith' => (object) ['coll' => 'users']],
];

self::assertEquals($expectedPipeline, $builder->getPipeline());
}

public function testUnionWithStageWithCollectionName(): void
{
$unionBuilder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class);
$unionBuilder->match()
->field('foo')->equals('bar');

$builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class);
$builder
->unionWith('someRandomCollectionName')
->pipeline($unionBuilder);

$expectedPipeline = [
[
'$unionWith' => (object) [
'coll' => 'someRandomCollectionName',
'pipeline' => [
['$match' => ['foo' => 'bar']],
],
],
],
];

self::assertEquals($expectedPipeline, $builder->getPipeline());
}
}