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
1 change: 1 addition & 0 deletions doc/01_Installation/02_Upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Following steps are necessary during updating to newer versions.
- The messenger transport DSN is now configurable via the `%pimcore.messenger.transport_dsn_prefix%` container parameter instead of being hardcoded to `doctrine://default`. This allows the installer to wire the transport DSN from environment variables (e.g. `PIMCORE_MESSENGER_TRANSPORT_DSN_PREFIX`).
- Added support to `PHP` `8.5`.
- Removed support to `PHP` `8.3` and Symfony `v6`.
- [Indexing] **BC Break**: `IndexQueueRepository::generateSelectQuery()` signature changed. The method now accepts an associative `$columnAliases` array (`alias => expression`) instead of the previous positional `$fields` array with a separate `$idField` parameter. Passing a numerically-indexed array will throw an `InvalidArgumentException`. All enqueue methods in `EnqueueService` and the element type adapters (`AssetTypeAdapter`, `DocumentTypeAdapter`, `DataObjectTypeAdapter`) have been updated accordingly. The `quoteParameters()` helper method has been removed.

## Upgrade to 2.2.0
- [Indexing] Added `id` column as new primary key to `generic_data_index_queue`. Please make sure to execute migrations.
Expand Down
119 changes: 47 additions & 72 deletions src/Repository/IndexQueueRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,26 @@ public function deleteQueueEntries(array $entries): void
{
$chunks = array_chunk($entries, self::BATCH_SIZE);
foreach ($chunks as $chunk) {
$condition = [];

/** @var IndexQueue $entry */
foreach ($chunk as $entry) {
$condition[] = sprintf(
$tuples = array_map(
fn (IndexQueue $entry) => sprintf(
'(%s, %s)',
$this->connection->quote((string)$entry->getId()),
$this->connection->quote($entry->getOperationTime())
);
}

$condition = sprintf('(%s, %s) IN (%s) ORDER BY %s ASC LIMIT %s',
$this->connection->quoteIdentifier('id'),
$this->connection->quoteIdentifier('operationTime'),
implode(',', $condition),
$this->connection->quoteIdentifier('id'),
self::BATCH_SIZE
),
$chunk
);

//delete handled entry from queue table
$this->connection->executeQuery('DELETE FROM ' . IndexQueue::TABLE . ' WHERE ' . $condition);
$this->connection->executeQuery(
sprintf(
'DELETE FROM %s WHERE (%s, %s) IN (%s) ORDER BY %s ASC LIMIT %d',
$this->connection->quoteIdentifier(IndexQueue::TABLE),
$this->connection->quoteIdentifier('id'),
$this->connection->quoteIdentifier('operationTime'),
implode(',', $tuples),
$this->connection->quoteIdentifier('id'),
self::BATCH_SIZE
)
);
}
}

Expand All @@ -161,24 +160,41 @@ public function denormalizeDatabaseEntry(array $entry): IndexQueue
return $this->denormalizer->denormalize($entry, IndexQueue::class);
}

/**
* @param array<string, string> $columnAliases Associative array mapping alias names to SQL expressions
* @param array<string, mixed> $params Query parameters for setParameters()
* @param array<int|string, string> $whereParameters Column names for WHERE clauses; keys may be numeric
* or operator constants (AND_OPERATOR, OR_OPERATOR)
*/
public function generateSelectQuery(
string $tableName,
array $fields,
string $idField = 'id',
array $columnAliases,
array $params = [],
array $whereParameters = []
): DBALQueryBuilder {
$fields = $this->quoteParameters($fields);
array_unshift($fields, $idField);
$selectExpressions = [];
foreach ($columnAliases as $alias => $expression) {
/** @phpstan-ignore function.impossibleType (runtime guard for callers without static analysis) */
if (is_int($alias)) {
throw new \InvalidArgumentException(
sprintf(
'Column aliases must be an associative array with string keys '
. '(alias => expression), got numeric key %d for expression "%s".',
$alias,
$expression
)
);
}
$selectExpressions[] = $expression . ' AS ' . $alias;
}

$qb = $this->connection->createQueryBuilder()
->addSelect(...$fields)
->addSelect(...$selectExpressions)
->from($tableName);

$this->addWhereStatements($qb, $whereParameters);

if (!empty($params)) {
$params = $this->quoteParameters($params);
$qb->setParameters($params);
}

Expand Down Expand Up @@ -315,20 +331,6 @@ private function createQueryBuilder(string $alias): QueryBuilder
->createQueryBuilder($alias);
}

private function quoteParameters(array $parameters): array
{
return array_map(
function ($parameter) {
if (is_string($parameter)) {
return $this->connection->quote($parameter);
}

return $parameter;
},
$parameters
);
}

private function addWhereStatements(DBALQueryBuilder $queryBuilder, array $whereParameters): DBALQueryBuilder
{
foreach ($whereParameters as $operator => $parameter) {
Expand Down Expand Up @@ -412,51 +414,24 @@ private function insertFromChunk(
return $this->connection->executeStatement($insertSql, $insertParams);
}

/*
* @TODO: Refactor to avoid that all values have to be in a specific order. Either use aliases or pass the keys as parameters.
* Both things can be considered breaking changes.
*/
/**
* Extracts values from SQL result rows that use named column aliases.
* Expected aliases: elementId, elementType, elementIndexName, operation, operationTime.
*/
private function getValuesFromSqlResult(array $result): array
{
if (empty($result)) {
return [];
}

$firstRow = $result[0];
$keys = array_keys($firstRow);
$columnCount = count($keys);

$ids = array_column($result, $keys[0]);
$elementType = $firstRow[$keys[1]];

$elementIndexName = match ($elementType) {
ElementType::ASSET->value, ElementType::DOCUMENT->value => $elementType,
ElementType::DATA_OBJECT->value => $firstRow['className'] ??
$firstRow[
$keys[2]
] ??
null,
default => null,
};

$operation = $firstRow[
$keys[
$columnCount > 5 ? 3 : 2
]
];

$operationTime = (int)$firstRow[
$keys[
$columnCount > 5 ? 4 : 3
]
];

return [
$ids,
$elementType,
$operation,
$operationTime,
$elementIndexName,
array_column($result, 'elementId'),
$firstRow['elementType'],
$firstRow['operation'],
(int)$firstRow['operationTime'],
$firstRow['elementIndexName'],
];
}
}
75 changes: 40 additions & 35 deletions src/Service/SearchIndex/IndexQueue/EnqueueService.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public function enqueueByTag(Tag $tag): EnqueueService
{
$tagCondition = $this->indexQueueRepository->generateSelectQuery(
'tags_assignment',
[],
'cid',
[
'elementId' => 'cid',
],
[],
['ctype', IndexQueueRepository::AND_OPERATOR => 'tagid']
);
Expand All @@ -59,13 +60,13 @@ public function enqueueByTag(Tag $tag): EnqueueService
$assetQuery = $this->indexQueueRepository->generateSelectQuery(
'assets',
[
ElementType::ASSET->value,
IndexName::ASSET->value,
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
'elementId' => 'id',
'elementType' => "'" . ElementType::ASSET->value . "'",
'elementIndexName' => "'" . IndexName::ASSET->value . "'",
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
],
'id',
['ctype' => ElementType::ASSET->value, 'tagid' => $tag->getId()],
);
$assetQuery->where($assetQuery->expr()->in('id', $tagCondition->getSQL()));
Expand All @@ -75,12 +76,13 @@ public function enqueueByTag(Tag $tag): EnqueueService
$dataObjectQuery = $this->indexQueueRepository->generateSelectQuery(
'objects',
[
ElementType::DATA_OBJECT->value,
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
'elementId' => 'id',
'elementType' => "'" . ElementType::DATA_OBJECT->value . "'",
'elementIndexName' => 'className',
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
],
'id, className',
['ctype' => ElementType::DATA_OBJECT->value, 'tagid' => $tag->getId()],
);
$dataObjectQuery->where($dataObjectQuery->expr()->in('id', $tagCondition->getSQL()));
Expand All @@ -98,13 +100,13 @@ public function enqueueByClassDefinition(ClassDefinition $classDefinition): Enqu
$selectQuery = $this->indexQueueRepository->generateSelectQuery(
$dataObjectTableName,
[
ElementType::DATA_OBJECT->value,
$classDefinition->getName(),
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
],
'oo_id'
'elementId' => 'oo_id',
'elementType' => "'" . ElementType::DATA_OBJECT->value . "'",
'elementIndexName' => "'" . $classDefinition->getName() . "'",
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
]
);
$this->indexQueueRepository->enqueueBySelectQuery(
$selectQuery
Expand All @@ -119,11 +121,12 @@ public function enqueueDataObjectFolders(): EnqueueServiceInterface
$selectQuery = $this->indexQueueRepository->generateSelectQuery(
'objects',
[
ElementType::DATA_OBJECT->value,
IndexName::DATA_OBJECT_FOLDER->value,
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
'elementId' => 'id',
'elementType' => "'" . ElementType::DATA_OBJECT->value . "'",
'elementIndexName' => "'" . IndexName::DATA_OBJECT_FOLDER->value . "'",
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
],
)->where('type = "folder"');
$this->indexQueueRepository->enqueueBySelectQuery($selectQuery);
Expand All @@ -145,11 +148,12 @@ public function enqueueAssets(): EnqueueService
$selectQuery = $this->indexQueueRepository->generateSelectQuery(
'assets',
[
ElementType::ASSET->value,
IndexName::ASSET->value,
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
'elementId' => 'id',
'elementType' => "'" . ElementType::ASSET->value . "'",
'elementIndexName' => "'" . IndexName::ASSET->value . "'",
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
]
);
$this->indexQueueRepository->enqueueBySelectQuery($selectQuery);
Expand All @@ -171,11 +175,12 @@ public function enqueueDocuments(): EnqueueService
$selectQuery = $this->indexQueueRepository->generateSelectQuery(
'documents',
[
ElementType::DOCUMENT->value,
IndexName::DOCUMENT->value,
IndexQueueOperation::UPDATE->value,
(string)$this->timeService->getCurrentMillisecondTimestamp(),
'0',
'elementId' => 'id',
'elementType' => "'" . ElementType::DOCUMENT->value . "'",
'elementIndexName' => "'" . IndexName::DOCUMENT->value . "'",
'operation' => "'" . IndexQueueOperation::UPDATE->value . "'",
'operationTime' => "'" . (string)$this->timeService->getCurrentMillisecondTimestamp() . "'",
'dispatched' => '0',
]
);
$this->indexQueueRepository->enqueueBySelectQuery($selectQuery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ public function getRelatedItemsOnUpdateQuery(
): QueryBuilder {

$selects = [
(string)$element->getId(),
"'" . ElementType::ASSET->value . "'",
"'" . IndexName::ASSET->value . "'",
"'$operation'",
"'$operationTime'",
'0',
(string)$element->getId() . ' AS elementId',
"'" . ElementType::ASSET->value . "' AS elementType",
"'" . IndexName::ASSET->value . "' AS elementIndexName",
"'$operation' AS operation",
"'$operationTime' AS operationTime",
'0 AS dispatched',
];

return $this->dbConnection->createQueryBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ public function getRelatedItemsOnUpdateQuery(
}

$selects = [
'id',
"'" . ElementType::DATA_OBJECT->value . "'",
'className',
"'$operation'",
"'$operationTime'",
'0',
'id AS elementId',
"'" . ElementType::DATA_OBJECT->value . "' AS elementType",
'className AS elementIndexName',
"'$operation' AS operation",
"'$operationTime' AS operationTime",
'0 AS dispatched',
];

$select = $this->dbConnection->createQueryBuilder()
Expand Down Expand Up @@ -188,12 +188,12 @@ private function getSelectParameters(
int $operationTime
): array {
return [
(string)$element->getId(),
"'" . ElementType::DATA_OBJECT->value . "'",
$this->getIndexName($element, $operation),
"'$operation'",
"'$operationTime'",
'0',
(string)$element->getId() . ' AS elementId',
"'" . ElementType::DATA_OBJECT->value . "' AS elementType",
$this->getIndexName($element, $operation) . ' AS elementIndexName',
"'$operation' AS operation",
"'$operationTime' AS operationTime",
'0 AS dispatched',
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ public function getRelatedItemsOnUpdateQuery(
bool $includeElement = false
): QueryBuilder {
$selects = [
(string)$element->getId(),
"'" . ElementType::DOCUMENT->value . "'",
"'" . IndexName::DOCUMENT->value . "'",
"'$operation'",
"'$operationTime'",
'0',
(string)$element->getId() . ' AS elementId',
"'" . ElementType::DOCUMENT->value . "' AS elementType",
"'" . IndexName::DOCUMENT->value . "' AS elementIndexName",
"'$operation' AS operation",
"'$operationTime' AS operationTime",
'0 AS dispatched',
];

return $this->dbConnection->createQueryBuilder()
Expand Down
Loading
Loading