diff --git a/src/index/Model/Changelog/InsertRecords.php b/src/index/Model/Changelog/InsertRecords.php
index f250287..ac8c1aa 100644
--- a/src/index/Model/Changelog/InsertRecords.php
+++ b/src/index/Model/Changelog/InsertRecords.php
@@ -6,57 +6,53 @@
use Aligent\FredhopperIndexer\Model\DataHandler;
use Aligent\FredhopperIndexer\Model\ResourceModel\Changelog as ChangelogResource;
use Magento\Framework\App\ResourceConnection;
-use Magento\Framework\Exception\LocalizedException;
class InsertRecords
{
/**
* @param ResourceConnection $resourceConnection
* @param ChangelogResource $changelogResource
- * @param TempTable $tempTable
*/
public function __construct(
private readonly ResourceConnection $resourceConnection,
private readonly ChangelogResource $changelogResource,
- private readonly TempTable $tempTable
) {
}
/**
* Insert add, update and delete records into the changelog table
*
+ * @param string $replicaId
* @return void
* @throws \Zend_Db_Select_Exception
- * @throws LocalizedException
*/
- public function execute(): void
+ public function execute(string $replicaId): void
{
- $tempTableName = $this->tempTable->getTempTableName();
$addedProductIds = $this->getAddedOrDeletedProductsByType(
true,
DataHandler::TYPE_PRODUCT,
- $tempTableName
+ $replicaId
);
$addedVariantIds = $this->getAddedOrDeletedProductsByType(
true,
DataHandler::TYPE_VARIANT,
- $tempTableName
+ $replicaId
);
$this->changelogResource->insertAdditionOperations($addedProductIds, $addedVariantIds);
- $updatedProductIds = $this->getUpdatedProductsByType(DataHandler::TYPE_PRODUCT, $tempTableName);
- $updatedVariantIds = $this->getUpdatedProductsByType(DataHandler::TYPE_VARIANT, $tempTableName);
+ $updatedProductIds = $this->getUpdatedProductsByType(DataHandler::TYPE_PRODUCT, $replicaId);
+ $updatedVariantIds = $this->getUpdatedProductsByType(DataHandler::TYPE_VARIANT, $replicaId);
$this->changelogResource->insertUpdateOperations($updatedProductIds, $updatedVariantIds);
$deletedProductIds = $this->getAddedOrDeletedProductsByType(
false,
DataHandler::TYPE_PRODUCT,
- $tempTableName
+ $replicaId
);
$deletedVariantIds = $this->getAddedOrDeletedProductsByType(
false,
DataHandler::TYPE_VARIANT,
- $tempTableName
+ $replicaId
);
$this->changelogResource->insertDeleteOperations($deletedProductIds, $deletedVariantIds);
}
@@ -66,27 +62,41 @@ public function execute(): void
*
* @param bool $isAddition
* @param string $productType
- * @param string $tempTableName
+ * @param string $replicaId
* @return array
*/
private function getAddedOrDeletedProductsByType(
bool $isAddition,
string $productType,
- string $tempTableName
+ string $replicaId
): array {
$connection = $this->resourceConnection->getConnection();
$select = $connection->select();
- $select->from(
- ['main_table' => ($isAddition ? DataHandler::INDEX_TABLE_NAME : $tempTableName)],
- ['product_id']
- );
- $select->joinLeft(
- ['temp_table' => ($isAddition ? $tempTableName : DataHandler::INDEX_TABLE_NAME)],
- 'temp_table.product_id = main_table.product_id AND '.
- 'temp_table.product_type = main_table.product_type',
- []
- );
+ if ($isAddition) {
+ $select->from(
+ ['main_table' => DataHandler::INDEX_TABLE_NAME],
+ ['product_id']
+ );
+ $select->joinLeft(
+ ['temp_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME],
+ 'temp_table.product_id = main_table.product_id AND '.
+ 'temp_table.product_type = main_table.product_type AND '.
+ 'temp_table.replica_id = ' . "'" . $replicaId . "'"
+ );
+ } else {
+ $select->from(
+ ['main_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME],
+ ['product_id']
+ );
+ $select->joinLeft(
+ ['temp_table' => DataHandler::INDEX_TABLE_NAME],
+ 'temp_table.product_id = main_table.product_id AND '.
+ 'temp_table.product_type = main_table.product_type'
+ );
+ $select->where('main_table.replica_id = ?', $replicaId);
+ }
+
$select->where('temp_table.product_id is null');
$select->where('main_table.product_type = ?', $productType);
$select->group('main_table.product_id');
@@ -95,20 +105,20 @@ private function getAddedOrDeletedProductsByType(
}
/**
- * Determine which products have been updated between the main and temporary table
+ * Determine which products have been updated between the main and replica table
*
* @param string $productType
- * @param string $tempTableName
+ * @param string $replicaId
* @return array
* @throws \Zend_Db_Select_Exception
*/
- private function getUpdatedProductsByType(string $productType, string $tempTableName): array
+ private function getUpdatedProductsByType(string $productType, string $replicaId): array
{
// get all product ids and variant ids that exist in both tables
// we do not want to consider products that are being added or deleted completely
$connection = $this->resourceConnection->getConnection();
$existingProductsSelect = $connection->select();
- $existingProductsSelect->from(['temp_table' => $tempTableName], ['product_id']);
+ $existingProductsSelect->from(['temp_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME], ['product_id']);
$existingProductsSelect->joinInner(
['main_table' => DataHandler::INDEX_TABLE_NAME],
'main_table.product_id = temp_table.product_id AND ' .
@@ -117,6 +127,7 @@ private function getUpdatedProductsByType(string $productType, string $tempTable
);
$existingProductsSelect->distinct();
$existingProductsSelect->where('temp_table.product_type = ?', $productType);
+ $existingProductsSelect->where('temp_table.replica_id = ?', $replicaId);
$existingProductIds = $connection->fetchCol($existingProductsSelect);
// records that are in the new table, but not in the old table
@@ -126,10 +137,11 @@ private function getUpdatedProductsByType(string $productType, string $tempTable
['product_id']
);
$existingProductsTempMissingSelect->joinLeft(
- ['temp_table' => $tempTableName],
+ ['temp_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME],
'main_table.product_id = temp_table.product_id AND ' .
'main_table.product_type = temp_table.product_type AND ' .
- 'main_table.store_id = temp_table.store_id',
+ 'main_table.store_id = temp_table.store_id AND ' .
+ 'temp_table.replica_id = ' . "'" . $replicaId . "'",
[]
);
$existingProductsTempMissingSelect->where('temp_table.product_id IS NULL');
@@ -139,7 +151,7 @@ private function getUpdatedProductsByType(string $productType, string $tempTable
// records that are in the old table, but not in the new table
$existingProductsMainMissingSelect = $connection->select();
$existingProductsMainMissingSelect->from(
- ['temp_table' => $tempTableName],
+ ['temp_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME],
['product_id']
);
$existingProductsMainMissingSelect->joinLeft(
@@ -152,6 +164,7 @@ private function getUpdatedProductsByType(string $productType, string $tempTable
$existingProductsMainMissingSelect->where('main_table.product_id IS NULL');
$existingProductsMainMissingSelect->where('temp_table.product_type = ?', $productType);
$existingProductsMainMissingSelect->where('temp_table.product_id in (?)', $existingProductIds);
+ $existingProductsMainMissingSelect->where('temp_table.replica_id = ?', $replicaId);
// records that differ by parent_id or attribute_data
$existingProductsDifferenceSelect = $connection->select();
@@ -160,10 +173,11 @@ private function getUpdatedProductsByType(string $productType, string $tempTable
['product_id']
);
$existingProductsDifferenceSelect->joinInner(
- ['temp_table' => $tempTableName],
+ ['temp_table' => ReplicaTableMaintainer::REPLICA_TABLE_NAME],
'main_table.product_id = temp_table.product_id AND ' .
'main_table.product_type = temp_table.product_type AND ' .
'main_table.store_id = temp_table.store_id AND '.
+ 'temp_table.replica_id = ' . "'" . $replicaId . "'" . ' AND ' .
'NOT (main_table.parent_id <=> temp_table.parent_id AND ' .
'main_table.attribute_data <=> temp_table.attribute_data)',
[]
diff --git a/src/index/Model/Changelog/ReplicaTableMaintainer.php b/src/index/Model/Changelog/ReplicaTableMaintainer.php
new file mode 100644
index 0000000..ae5b9e6
--- /dev/null
+++ b/src/index/Model/Changelog/ReplicaTableMaintainer.php
@@ -0,0 +1,83 @@
+resourceConnection->getConnection();
+ $copySelect = $connection->select();
+ $selectColumns = [
+ 'replica_id' => new \Zend_Db_Expr("'$id'"),
+ 'store_id' => 'store_id',
+ 'product_type' => 'product_type',
+ 'product_id' => 'product_id',
+ 'parent_id' => 'parent_id',
+ 'attribute_data' => 'attribute_data',
+
+ ];
+ $copySelect->from(
+ DataHandler::INDEX_TABLE_NAME,
+ $selectColumns
+ );
+
+ $copyInsert = $connection->insertFromSelect(
+ $copySelect,
+ self::REPLICA_TABLE_NAME
+ );
+ $connection->query($copyInsert);
+ }
+
+ /**
+ * Delete records from the replica table with the given ID
+ *
+ * @param string $id
+ * @return void
+ */
+ public function deleteRecords(string $id): void
+ {
+ $connection = $this->resourceConnection->getConnection();
+ $connection->delete(
+ self::REPLICA_TABLE_NAME,
+ ['replica_id = ?' => $id]
+ );
+ }
+}
diff --git a/src/index/Model/Changelog/TempTable.php b/src/index/Model/Changelog/TempTable.php
deleted file mode 100644
index b4de087..0000000
--- a/src/index/Model/Changelog/TempTable.php
+++ /dev/null
@@ -1,102 +0,0 @@
-tempTableName)) {
- throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
- }
- return $this->tempTableName;
- }
-
- /**
- * Sets the current temporary table to a unique value
- *
- * @return void
- * @throws LocalizedException
- */
- public function generateTempTableName(): void
- {
- if (isset($this->tempTableName)) {
- throw new LocalizedException(__(__METHOD__ . ': temp table name already set'));
- }
- try {
- $tempTableName = self::TEMP_TABLE_PREFIX . bin2hex(random_bytes(4));
- } catch (RandomException) {
- $tempTableName = uniqid(self::TEMP_TABLE_PREFIX, true);
- }
- $this->tempTableName = $tempTableName;
- }
-
- /**
- * Creates a temporary copy of the index table for use in generating changelog records
- *
- * @return void
- * @throws LocalizedException
- */
- public function create(): void
- {
- if (!isset($this->tempTableName)) {
- throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
- }
- $connection = $this->resourceConnection->getConnection();
- $table = $connection->createTableByDdl(DataHandler::INDEX_TABLE_NAME, $this->tempTableName);
- if ($connection->isTableExists($this->tempTableName)) {
- throw new LocalizedException(__(__METHOD__ . ': temp table already exists'));
- }
- try {
- $connection->createTable($table);
- } catch (\Exception $e) {
- throw new LocalizedException(__(__METHOD__ . ': ' . $e->getMessage()), $e);
- }
- $copySelect = $connection->select();
- $copySelect->from(DataHandler::INDEX_TABLE_NAME);
- $copyInsert = $connection->insertFromSelect(
- $copySelect,
- $this->tempTableName
- );
- $connection->query($copyInsert);
- }
-
- /**
- * Drops the temporary table if it exists
- *
- * @return void
- * @throws LocalizedException
- */
- public function drop(): void
- {
- if (!isset($this->tempTableName)) {
- throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
- }
- $this->resourceConnection->getConnection()->dropTable($this->tempTableName);
- unset($this->tempTableName);
- }
-}
diff --git a/src/index/Model/ProductIndexer.php b/src/index/Model/ProductIndexer.php
index e4181db..afb88c4 100644
--- a/src/index/Model/ProductIndexer.php
+++ b/src/index/Model/ProductIndexer.php
@@ -4,7 +4,7 @@
namespace Aligent\FredhopperIndexer\Model;
use Aligent\FredhopperIndexer\Model\Changelog\InsertRecords;
-use Aligent\FredhopperIndexer\Model\Changelog\TempTable;
+use Aligent\FredhopperIndexer\Model\Changelog\ReplicaTableMaintainer;
use Aligent\FredhopperIndexer\Model\Data\FredhopperDataProvider;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
use Magento\Framework\App\DeploymentConfig;
@@ -29,7 +29,7 @@ class ProductIndexer implements DimensionalIndexerInterface, IndexerActionInterf
* @param DataHandler $dataHandler
* @param DeploymentConfig $deploymentConfig
* @param FulltextResource $fulltextResource
- * @param TempTable $tempTable
+ * @param ReplicaTableMaintainer $replicaTableMaintainer
* @param InsertRecords $insertChangelogRecords
* @param LoggerInterface $logger
* @param int $batchSize
@@ -40,7 +40,7 @@ public function __construct(
private readonly DataHandler $dataHandler,
private readonly DeploymentConfig $deploymentConfig,
private readonly FulltextResource $fulltextResource,
- private readonly TempTable $tempTable,
+ private readonly ReplicaTableMaintainer $replicaTableMaintainer,
private readonly InsertRecords $insertChangelogRecords,
private readonly LoggerInterface $logger,
private readonly int $batchSize = 1000
@@ -49,8 +49,6 @@ public function __construct(
/**
* @inheritDoc
- *
- * @throws LocalizedException
*/
public function execute($ids): void
{
@@ -59,8 +57,6 @@ public function execute($ids): void
/**
* @inheritDoc
- *
- * @throws LocalizedException
*/
public function executeFull(): void
{
@@ -69,15 +65,14 @@ public function executeFull(): void
/**
* @inheritDoc
- *
*/
public function executeList(array $ids): void
{
try {
- // create temporary table to handle changelogs
- $this->tempTable->generateTempTableName();
- $this->tempTable->create();
- // try block here is nested to ensure that if the table was created, it gets dropped at the end
+ // create ID for temporary records to handle changelogs
+ $replicaId = $this->replicaTableMaintainer->generateUniqueId();
+ $this->replicaTableMaintainer->insertRecords($replicaId);
+ // try block here is nested to ensure that if the records were inserted, they get removed at the end
try {
foreach ($this->dimensionProvider->getIterator() as $dimension) {
try {
@@ -86,10 +81,10 @@ public function executeList(array $ids): void
continue;
}
}
- $this->insertChangelogRecords->execute();
+ $this->insertChangelogRecords->execute($replicaId);
} finally {
- // we want to ensure that the "temporary" table is always dropped
- $this->tempTable->drop();
+ // we want to ensure that the temporary records are always deleted
+ $this->replicaTableMaintainer->deleteRecords($replicaId);
}
} catch (\Exception $e) {
$this->logger->critical($e->getMessage(), ['exception' => $e]);
diff --git a/src/index/etc/db_schema.xml b/src/index/etc/db_schema.xml
index bed1488..f0accbe 100644
--- a/src/index/etc/db_schema.xml
+++ b/src/index/etc/db_schema.xml
@@ -18,6 +18,26 @@