Skip to content

Commit

Permalink
implement inner join autorwrite
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Jul 15, 2023
1 parent 03ed306 commit 8409a51
Show file tree
Hide file tree
Showing 21 changed files with 98 additions and 37 deletions.
7 changes: 4 additions & 3 deletions src/Collection/DbalCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ public function orderBy($expression, string $direction = ICollection::ASC): ICol

foreach ($expression as $subExpression => $subDirection) {
$collection->ordering[] = [
$helper->processExpression($collection->queryBuilder, $subExpression, null),
$helper->processExpression($collection->queryBuilder, $subExpression, filterableJoin: false, aggregator: null),
$subDirection,
];
}
} else {
$collection->ordering[] = [
$helper->processExpression($collection->queryBuilder, $expression, null),
$helper->processExpression($collection->queryBuilder, $expression, filterableJoin: false, aggregator: null),
$direction,
];
}
Expand Down Expand Up @@ -325,7 +325,8 @@ public function getQueryBuilder(): QueryBuilder
$expression = $helper->processExpression(
$this->queryBuilder,
$args,
null,
filterableJoin: true,
aggregator: null,
);
$joins = $expression->joins;
if ($expression->isHavingClause) {
Expand Down
29 changes: 27 additions & 2 deletions src/Collection/Functions/BaseCompareFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Functions\Result\ArrayExpressionResult;
use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult;
use Nextras\Orm\Collection\Functions\Result\DbalTableJoin;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;
Expand Down Expand Up @@ -59,12 +60,13 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
assert(count($args) === 2);

$expression = $helper->processExpression($builder, $args[0], $aggregator);
$expression = $helper->processExpression($builder, $args[0], $filterableJoin, $aggregator);

if ($expression->valueNormalizer !== null) {
$cb = $expression->valueNormalizer;
Expand All @@ -73,7 +75,30 @@ public function processDbalExpression(
$value = $args[1];
}

return $this->evaluateInDb($expression, $value, $expression->dbalModifier ?? '%any');
$hasJoins = count($expression->joins) > 0;
$expression = $this->evaluateInDb($expression, $value, $expression->dbalModifier ?? '%any');

// Let's inline the condition to the join if it is allowed & if there is any join.
if (!$filterableJoin || !$hasJoins || $expression->isHavingClause) {
return $expression;
}

$joins = $expression->joins;
/** @var DbalTableJoin $lastJoin */
$lastJoin = array_pop($joins);
$joins[] = $lastJoin->withCondition($expression->expression, ...$expression->args);

return new DbalExpressionResult(
expression: "(1=1)",
args: [],
joins: $joins,
groupBy: [],
aggregator: null,
isHavingClause: false,
propertyMetadata: null,
valueNormalizer: null,
dbalModifier: null,
);
}


Expand Down
3 changes: 2 additions & 1 deletion src/Collection/Functions/BaseNumericAggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -63,6 +64,6 @@ public function processDbalExpression(
throw new InvalidStateException("Cannot apply two aggregations simultaneously.");
}

return $helper->processExpression($builder, $args[0], $this->aggregator)->applyAggregator($builder);
return $helper->processExpression($builder, $args[0], $filterableJoin, $this->aggregator)->applyAggregator($builder);
}
}
1 change: 1 addition & 0 deletions src/Collection/Functions/CollectionFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult;
}
3 changes: 2 additions & 1 deletion src/Collection/Functions/CompareLikeFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,13 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
assert(count($args) === 2);

$expression = $helper->processExpression($builder, $args[0], $aggregator);
$expression = $helper->processExpression($builder, $args[0], $filterableJoin, $aggregator);

$likeExpression = $args[1];
assert($likeExpression instanceof LikeExpression);
Expand Down
2 changes: 2 additions & 0 deletions src/Collection/Functions/ConjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -113,6 +114,7 @@ public function processDbalExpression(
helper: $helper,
builder: $builder,
args: $args,
filterableJoin: $filterableJoin,
aggregator: $aggregator,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Collection/Functions/DisjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -106,6 +107,7 @@ public function processDbalExpression(
helper: $helper,
builder: $builder,
args: $args,
filterableJoin: false,
aggregator: $aggregator,
);
}
Expand Down
1 change: 1 addition & 0 deletions src/Collection/Functions/FetchPropertyFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
Expand Down
3 changes: 2 additions & 1 deletion src/Collection/Functions/JunctionFunctionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ protected function processQueryBuilderExpressionWithModifier(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
bool $filterableJoin,
?IDbalAggregator $aggregator,
): DbalExpressionResult
{
Expand All @@ -77,7 +78,7 @@ protected function processQueryBuilderExpressionWithModifier(
}

foreach ($normalized as $collectionFunctionArgs) {
$expression = $helper->processExpression($builder, $collectionFunctionArgs, $aggregator);
$expression = $helper->processExpression($builder, $collectionFunctionArgs, $filterableJoin, $aggregator);
$expression = $expression->applyAggregator($builder);
$processedArgs[] = $expression->getArgumentsForExpansion();
$joins = array_merge($joins, $expression->joins);
Expand Down
36 changes: 30 additions & 6 deletions src/Collection/Functions/Result/DbalTableJoin.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function __construct(
string $onExpression,
array $onArgs,
IConventions $conventions,
public readonly bool $inner = false,
)
{
$this->toExpression = $toExpression;
Expand All @@ -55,13 +56,36 @@ public function __construct(
}


public function applyJoin(QueryBuilder $queryBuilder): void
public function withCondition(string $expression, ...$expresionArgs): DbalTableJoin
{
$queryBuilder->joinLeft(
"$this->toExpression AS [$this->toAlias]",
$this->onExpression,
...$this->toArgs,
...$this->onArgs,
return new self(
toExpression: $this->toExpression,
toArgs: $this->toArgs,
toAlias: $this->toAlias,
onExpression: "$this->onExpression AND $expression",
onArgs: array_merge($this->onArgs, $expresionArgs),
conventions: $this->conventions,
inner: true,
);
}


public function applyJoin(QueryBuilder $queryBuilder): void
{
if ($this->inner) {
$queryBuilder->joinInner(
"$this->toExpression AS [$this->toAlias]",
$this->onExpression,
...$this->toArgs,
...$this->onArgs,
);
} else {
$queryBuilder->joinLeft(
"$this->toExpression AS [$this->toAlias]",
$this->onExpression,
...$this->toArgs,
...$this->onArgs,
);
}
}
}
6 changes: 4 additions & 2 deletions src/Collection/Helpers/DbalQueryBuilderHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public function __construct(
public function processExpression(
QueryBuilder $builder,
array|string $expression,
bool $filterableJoin,
?IDbalAggregator $aggregator,
): DbalExpressionResult
{
Expand All @@ -85,7 +86,7 @@ public function processExpression(
$collectionFunction = $this->repository->getCollectionFunction($function);
}

return $collectionFunction->processDbalExpression($this, $builder, $expression, $aggregator);
return $collectionFunction->processDbalExpression($this, $builder, $expression, $filterableJoin, $aggregator);
}


Expand Down Expand Up @@ -143,7 +144,7 @@ public function mergeJoins(string $dbalModifier, array $joins): array
/** @var array<array<DbalTableJoin>> $aggregated */
$aggregated = [];
foreach ($joins as $join) {
$hash = md5(Json::encode([$join->onExpression, $join->onArgs]));
$hash = md5(Json::encode([$join->onExpression, $join->onArgs, $join->inner]));
/**
* We aggregate only by alias as we assume that having a different alias
* for different select-from expressions is a responsibility of the query-helper/user.
Expand All @@ -170,6 +171,7 @@ public function mergeJoins(string $dbalModifier, array $joins): array
onExpression: $dbalModifier,
onArgs: [$args],
conventions: $first->conventions,
inner: $first->inner,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ START TRANSACTION;
INSERT INTO "books" ("title", "author_id", "translator_id", "next_part", "ean_id", "publisher_id", "published_at", "printed_at", "price", "price_currency", "orig_price_cents", "orig_price_currency") VALUES ('Books 5', 1, 2, NULL, NULL, 1, '2021-12-31 23:59:59.000000'::timestamp, NULL, NULL, NULL, NULL, NULL);
SELECT CURRVAL('books_id_seq');
COMMIT;
SELECT "books".* FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") LEFT JOIN "public"."authors" AS "translator" ON ("books"."translator_id" = "translator"."id") WHERE (("author"."name" = 'Writer 1') AND ("translator"."web" = 'http://example.com/2'));
SELECT "books".* FROM "books" AS "books" INNER JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id" AND "author"."name" = 'Writer 1') INNER JOIN "public"."authors" AS "translator" ON ("books"."translator_id" = "translator"."id" AND "translator"."web" = 'http://example.com/2') WHERE (((1=1)) AND ((1=1)));
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SELECT "books".* FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") WHERE (("author"."name" = 'Writer 1') AND ("author"."web" = 'http://example.com/1'));
SELECT "books".* FROM "books" AS "books" INNER JOIN "public"."authors" AS "author" ON (("books"."author_id" = "author"."id" AND "author"."name" = 'Writer 1') AND ("books"."author_id" = "author"."id" AND "author"."web" = 'http://example.com/1')) WHERE (((1=1)) AND ((1=1)));
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SELECT COUNT(*) AS count FROM (SELECT "books"."id" FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") WHERE (("author"."name" = 'Writer 1')) ORDER BY "books"."id" ASC LIMIT 5) temp;
SELECT COUNT(*) AS count FROM (SELECT "tag_followers"."tag_id", "tag_followers"."author_id" FROM "tag_followers" AS "tag_followers" LEFT JOIN "tags" AS "tag" ON ("tag_followers"."tag_id" = "tag"."id") WHERE (("tag"."name" = 'Tag 1')) ORDER BY "tag_followers"."tag_id" ASC LIMIT 3) temp;
SELECT COUNT(*) AS count FROM (SELECT "books"."id" FROM "books" AS "books" INNER JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id" AND "author"."name" = 'Writer 1') WHERE (((1=1))) ORDER BY "books"."id" ASC LIMIT 5) temp;
SELECT COUNT(*) AS count FROM (SELECT "tag_followers"."tag_id", "tag_followers"."author_id" FROM "tag_followers" AS "tag_followers" INNER JOIN "tags" AS "tag" ON ("tag_followers"."tag_id" = "tag"."id" AND "tag"."name" = 'Tag 1') WHERE (((1=1))) ORDER BY "tag_followers"."tag_id" ASC LIMIT 3) temp;
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ SELECT CURRVAL('eans_id_seq');
INSERT INTO "books" ("title", "author_id", "translator_id", "next_part", "ean_id", "publisher_id", "published_at", "printed_at", "price", "price_currency", "orig_price_cents", "orig_price_currency") VALUES ('Book 5', 1, NULL, 4, 2, 1, '2021-12-31 23:59:59.000000'::timestamp, NULL, NULL, NULL, NULL, NULL);
SELECT CURRVAL('books_id_seq');
COMMIT;
SELECT "books".* FROM "books" AS "books" LEFT JOIN "books" AS "nextPart" ON ("books"."next_part" = "nextPart"."id") LEFT JOIN "eans" AS "nextPart_ean" ON ("nextPart"."ean_id" = "nextPart_ean"."id") LEFT JOIN "books" AS "previousPart" ON ("books"."id" = "previousPart"."next_part") LEFT JOIN "eans" AS "previousPart_ean" ON ("previousPart"."ean_id" = "previousPart_ean"."id") WHERE (("nextPart_ean"."code" = '123') AND ("previousPart_ean"."code" = '456'));
SELECT "books".* FROM "books" AS "books" LEFT JOIN "books" AS "nextPart" ON ("books"."next_part" = "nextPart"."id") INNER JOIN "eans" AS "nextPart_ean" ON ("nextPart"."ean_id" = "nextPart_ean"."id" AND "nextPart_ean"."code" = '123') LEFT JOIN "books" AS "previousPart" ON ("books"."id" = "previousPart"."next_part") INNER JOIN "eans" AS "previousPart_ean" ON ("previousPart"."ean_id" = "previousPart_ean"."id" AND "previousPart_ean"."code" = '456') WHERE (((1=1)) AND ((1=1)));
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SELECT "tags".* FROM "tags" AS "tags" WHERE (("tags"."id" = 1));
SELECT "books_x_tags"."book_id", "books_x_tags"."tag_id" FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE ((("author"."id" = 1))) AND ("books_x_tags"."tag_id" IN (1));
SELECT "books_x_tags"."book_id", "books_x_tags"."tag_id" FROM "books" AS "books" INNER JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id" AND "author"."id" = 1) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE ((((1=1)))) AND ("books_x_tags"."tag_id" IN (1));
SELECT "books".* FROM "books" AS "books" WHERE (("books"."id" IN (1)));
SELECT "books_x_tags"."tag_id", COUNT(DISTINCT "books_x_tags"."book_id") AS "count" FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE ((("author"."id" = 1))) AND ("books_x_tags"."tag_id" IN (1)) GROUP BY "books_x_tags"."tag_id";
SELECT "books_x_tags"."tag_id", COUNT(DISTINCT "books_x_tags"."book_id") AS "count" FROM "books" AS "books" INNER JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id" AND "author"."id" = 1) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE ((((1=1)))) AND ("books_x_tags"."tag_id" IN (1)) GROUP BY "books_x_tags"."tag_id";
SELECT "books_x_tags"."book_id", "books_x_tags"."tag_id" FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") LEFT JOIN "tag_followers" AS "author_tagFollowers_any" ON ("author"."id" = "author_tagFollowers_any"."author_id") LEFT JOIN "public"."authors" AS "author_tagFollowers_author_any" ON (("author_tagFollowers_any"."author_id" = "author_tagFollowers_author_any"."id") AND "author_tagFollowers_author_any"."id" = 1) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE "books_x_tags"."tag_id" IN (1) GROUP BY "books"."id", "books_x_tags"."book_id", "books_x_tags"."tag_id" HAVING ((COUNT("author_tagFollowers_author_any"."id") > 0));
SELECT "books".* FROM "books" AS "books" WHERE (("books"."id" IN (1)));
SELECT "books_x_tags"."tag_id", COUNT(DISTINCT "books_x_tags"."book_id") AS "count" FROM "books" AS "books" LEFT JOIN "public"."authors" AS "author" ON ("books"."author_id" = "author"."id") LEFT JOIN "tag_followers" AS "author_tagFollowers_any" ON ("author"."id" = "author_tagFollowers_any"."author_id") LEFT JOIN "public"."authors" AS "author_tagFollowers_author_any" ON (("author_tagFollowers_any"."author_id" = "author_tagFollowers_author_any"."id") AND "author_tagFollowers_author_any"."id" = 1) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books_x_tags"."book_id" = "books"."id") WHERE "books_x_tags"."tag_id" IN (1) GROUP BY "books"."id", "books_x_tags"."tag_id" HAVING ((COUNT("author_tagFollowers_author_any"."id") > 0));
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ INSERT INTO "users" ("id") VALUES (123);
COMMIT;
START TRANSACTION;
INSERT INTO "users" ("id") VALUES (124);
INSERT INTO "users_x_users" ("my_friends_id", "friends_with_me_id") VALUES (124, 123);
INSERT INTO "users_x_users" ("friends_with_me_id", "my_friends_id") VALUES (124, 123);
COMMIT;
SELECT "users_x_users"."my_friends_id", "users_x_users"."friends_with_me_id" FROM "users" AS "users" LEFT JOIN "users_x_users" AS "users_x_users" ON ("users_x_users"."my_friends_id" = "users"."id") WHERE "users_x_users"."friends_with_me_id" IN (123);
SELECT "users".* FROM "users" AS "users" WHERE (("users"."id" IN (124)));
SELECT "users_x_users"."friends_with_me_id", "users_x_users"."my_friends_id" FROM "users" AS "users" LEFT JOIN "users_x_users" AS "users_x_users" ON ("users_x_users"."friends_with_me_id" = "users"."id") WHERE "users_x_users"."my_friends_id" IN (123);
SELECT "users".* FROM "users" AS "users" WHERE (("users"."id" IN (124)));
SELECT "users_x_users"."my_friends_id", "users_x_users"."friends_with_me_id" FROM "users" AS "users" LEFT JOIN "users_x_users" AS "users_x_users" ON ("users_x_users"."my_friends_id" = "users"."id") WHERE "users_x_users"."friends_with_me_id" IN (123);
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SELECT "books".* FROM "books" AS "books" LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books"."id" = "books_x_tags"."book_id") LEFT JOIN "tags" AS "tags_any" ON (("books_x_tags"."tag_id" = "tags_any"."id") AND "tags_any"."id" = 1) LEFT JOIN "publishers" AS "publisher" ON ("books"."publisher_id" = "publisher"."publisher_id") GROUP BY "books"."id", "publisher"."name" HAVING ((COUNT("tags_any"."id") > 0) AND ("publisher"."name" = 'Nextras publisher A'));
SELECT "books".* FROM "books" AS "books" LEFT JOIN "books_x_tags" AS "books_x_tags" ON ("books"."id" = "books_x_tags"."book_id") LEFT JOIN "tags" AS "tags_any" ON (("books_x_tags"."tag_id" = "tags_any"."id") AND "tags_any"."id" = 1) INNER JOIN "publishers" AS "publisher" ON ("books"."publisher_id" = "publisher"."publisher_id" AND "publisher"."name" = 'Nextras publisher A') GROUP BY "books"."id" HAVING ((COUNT("tags_any"."id") > 0) AND ((1=1)));
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ SELECT CURRVAL('eans_id_seq');
INSERT INTO "books" ("title", "author_id", "translator_id", "next_part", "ean_id", "publisher_id", "published_at", "printed_at", "price", "price_currency", "orig_price_cents", "orig_price_currency") VALUES ('GoT', 1, NULL, NULL, 1, 1, '2021-12-31 23:59:59.000000'::timestamp, NULL, NULL, NULL, NULL, NULL);
SELECT CURRVAL('books_id_seq');
COMMIT;
SELECT COUNT(*) AS count FROM (SELECT "eans"."id" FROM "eans" AS "eans" LEFT JOIN "books" AS "book" ON ("eans"."id" = "book"."ean_id") WHERE (("book"."title" = 'GoT'))) temp;
SELECT "eans".* FROM "eans" AS "eans" LEFT JOIN "books" AS "book" ON ("eans"."id" = "book"."ean_id") WHERE (("book"."title" = 'GoT')) ORDER BY "book"."title" ASC;
SELECT COUNT(*) AS count FROM (SELECT "eans"."id" FROM "eans" AS "eans" INNER JOIN "books" AS "book" ON (("eans"."id" = "book"."ean_id" AND "book"."title" = 'GoT') AND ("eans"."id" = "book"."ean_id")) WHERE (((1=1)))) temp;
SELECT "eans".* FROM "eans" AS "eans" INNER JOIN "books" AS "book" ON (("eans"."id" = "book"."ean_id" AND "book"."title" = 'GoT') AND ("eans"."id" = "book"."ean_id")) WHERE (((1=1))) ORDER BY "book"."title" ASC;
Loading

0 comments on commit 8409a51

Please sign in to comment.