Skip to content

Commit

Permalink
perf: improve performance of EAV querying by using WHERE instead of H…
Browse files Browse the repository at this point in the history
…AVING
  • Loading branch information
williarin committed Aug 3, 2022
1 parent a9cc6f9 commit 3903478
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 20 deletions.
91 changes: 71 additions & 20 deletions src/Bridge/Repository/AbstractEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public function persist(mixed $entity): void
}
}

public function getMappedMetaKey(mixed $fieldName, string $entityClassName = null): string
public function getMappedMetaKey(string $fieldName, string $entityClassName = null): string
{
$mappedFields = $entityClassName ? (new \ReflectionClassConstant(
$this->entityManager->getRepository($entityClassName),
Expand All @@ -238,6 +238,24 @@ public function getMappedMetaKey(mixed $fieldName, string $entityClassName = nul
return $key;
}

public function isFieldMapped(string $fieldName, string $entityClassName = null): bool
{
$mappedFields = $entityClassName ? (new \ReflectionClassConstant(
$this->entityManager->getRepository($entityClassName),
'MAPPED_FIELDS',
))->getValue() : static::MAPPED_FIELDS;

if (
!is_array($mappedFields)
|| empty($mappedFields)
|| !in_array($fieldName, $mappedFields, true)
) {
return false;
}

return true;
}

public function getTableAliasForField(string $fieldName): ?string
{
$mappedField = $this->getMappedMetaKey($fieldName);
Expand Down Expand Up @@ -282,14 +300,7 @@ protected function addSelectForExtraFields(QueryBuilder $queryBuilder): void
$extraFields = $this->getEntityExtraFields();

if (!empty($extraFields)) {
$this->tableAliases['pm_self'] = [];

$queryBuilder->leftJoin(
'p',
$this->entityManager->getTablesPrefix() . static::TABLE_META_NAME,
'pm_self',
sprintf('p.%s = pm_self.%s', static::TABLE_IDENTIFIER, static::TABLE_META_IDENTIFIER),
);
$this->joinSelfMetaTable($queryBuilder);

foreach ($extraFields as $extraField) {
$fieldName = property_to_field($extraField);
Expand Down Expand Up @@ -392,24 +403,37 @@ protected function handleRegularCriteria(
}

if (
$entityClassName !== null
&& $aliasNumber !== null
&& in_array(
in_array(
substr($field, (strpos($field, '.') ?: -1) + 1),
$this->getEntityExtraFields($entityClassName),
true,
)
) {
$exprKey = sprintf('pm_%d.meta_key = :%s_key', $aliasNumber, $snakeField);
$queryBuilder->andWhere($exprKey)
->setParameter(sprintf('%s_key', $snakeField), $this->getMappedMetaKey($field, $entityClassName))
;
if ($aliasNumber === null) {
$this->joinSelfMetaTable($queryBuilder);
}

$exprValue = sprintf('pm_%d.meta_value %s %s', $aliasNumber, $operator, $parameter);
if ($this->isFieldMapped($field, $entityClassName)) {
$exprKey = sprintf('pm_%s.meta_key = :%s_key', $aliasNumber ?? 'self', $snakeField);
$queryBuilder->andWhere($exprKey)
->setParameter(
sprintf('%s_key', $snakeField),
$this->getMappedMetaKey($field, $entityClassName),
)
;
} else {
$exprKey = sprintf(
'TRIM(LEADING \'_\' FROM pm_%s.meta_key) = :%s_key',
$aliasNumber ?? 'self',
$snakeField,
);
$queryBuilder->andWhere($exprKey)
->setParameter(sprintf('%s_key', $snakeField), $field)
;
}

$exprValue = sprintf('pm_%s.meta_value %s %s', $aliasNumber ?? 'self', $operator, $parameter);
$queryBuilder->andWhere($exprValue);
} elseif (in_array(substr($field, (strpos($field, '.') ?: -1) + 1), $this->getEntityExtraFields(), true)) {
$expr = sprintf('%s %s %s', $field, $operator, $parameter);
$queryBuilder->andHaving($expr);
} else {
if ($operator === Operand::OPERATOR_IN_ALL) {
$operator = 'IN';
Expand Down Expand Up @@ -437,6 +461,33 @@ protected function addOrderByClause(QueryBuilder $queryBuilder, ?array $orderBy)
}
}

private function joinSelfMetaTable(QueryBuilder $queryBuilder): void
{
if (!array_key_exists('pm_self', $this->tableAliases)) {
$this->tableAliases['pm_self'] = [];
}

$joinQueryPart = $queryBuilder->getQueryPart('join');

if (
array_key_exists('p', $joinQueryPart)
&& is_array($joinQueryPart['p'])
&& count(array_filter(
$joinQueryPart['p'],
static fn (array $part) => !empty($part['joinAlias']) && $part['joinAlias'] === 'pm_self',
)) > 0
) {
return;
}

$queryBuilder->leftJoin(
'p',
$this->entityManager->getTablesPrefix() . static::TABLE_META_NAME,
'pm_self',
sprintf('p.%s = pm_self.%s', static::TABLE_IDENTIFIER, static::TABLE_META_IDENTIFIER),
);
}

private function createRelationshipCriteria(
QueryBuilder $queryBuilder,
RelationshipCondition $condition
Expand Down
3 changes: 3 additions & 0 deletions src/Criteria/SelectColumns.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

final class SelectColumns
{
/**
* @param string[] $columns
*/
public function __construct(
private array $columns
) {
Expand Down

0 comments on commit 3903478

Please sign in to comment.