*/ declare(strict_types = 1); /** * Class for keeping the source fulltext index in sync. */ final class NodaSourceFulltextSync { /** @var MDMysqli */ private MDMysqli $_mysqli_noda; /** * Removes the obsolete entries. * * @return void */ public function removeObsolete():void { $result = $this->_mysqli_noda->do_read_query("SELECT 1 FROM `source_fulltext_search` WHERE NOT EXISTS (SELECT 1 FROM `source` WHERE `source`.`source_id` = `source_fulltext_search`.`source_id`) LIMIT 1"); if ($result->num_rows === 0) { $result->close(); $result = null; return; } $result->close(); $result = null; $this->_mysqli_noda->do_update_query_large("DELETE FROM `source_fulltext_search` WHERE NOT EXISTS (SELECT 1 FROM `source` WHERE `source`.`source_id` = `source_fulltext_search`.`source_id`)"); } /** * Gets the entries to update. * * @return array */ public function getNewEntries():array { $result = $this->_mysqli_noda->do_read_query("SELECT `source_id`, `source_title`, `source_year`, `source_booktitle`, `source_venue`, `source_volume`, `source_issue`, `source_pages`, `source_url`, `source_doi`, `source_isbn`, `source_license` FROM `source` WHERE NOT EXISTS (SELECT 1 FROM `source_fulltext_search` WHERE `source`.`source_id` = `source_fulltext_search`.`source_id` AND `source`.`source_update_timestamp` <= `source_fulltext_search`.`update_timestamp`)"); if ($result->num_rows === 0) { $result->close(); $result = null; return []; } $toUpdate = []; while ($cur = $result->fetch_assoc()) { $cur['authors'] = ""; $source_id = (int)$cur['source_id']; unset($cur['source_id']); $toUpdate[$source_id] = $cur; } $result->close(); $result = null; $authorsResult = $this->_mysqli_noda->do_read_query("SELECT `source_author`.`source_id`, `persinst_anzeigename` AS `name` FROM `persinst`, `source_author` WHERE `source_author`.`author_id` = `persinst`.`persinst_id` AND `source_author`.`source_id` IN (" . implode(', ', array_keys($toUpdate)) . ")"); $authors = []; while ($cur = $authorsResult->fetch_assoc()) { $source_id = (int)$cur['source_id']; if (empty($authors[$source_id])) { $authors[$source_id] = [$cur['name']]; } else $authors[$source_id][] = $cur['name']; } $authorsResult->close(); $authorsResult = null; $newEntries = []; foreach ($toUpdate as $source_id => $values) { $cur = implode(' ', $values); if (!empty($authors[$source_id])) $cur .= ' ' . implode(' ', array_unique($authors[$source_id])); $newEntries[$source_id] = $cur; } return $newEntries; } /** * Loads the list of entries to update. * * @return void */ public function update():void { if (empty($toUpdate = $this->getNewEntries())) { return; } if (count($toUpdate) > 1) { $this->_mysqli_noda->autocommit(false); } $updateStmt = $this->_mysqli_noda->do_prepare("INSERT INTO `source_fulltext_search` (`source_id`, `content`, `update_timestamp`) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE `content` = ?, `update_timestamp` = NOW()"); foreach ($toUpdate as $key => $value) { $updateStmt->bind_param("iss", $key, $value, $value); $updateStmt->execute(); } $updateStmt->close(); $updateStmt = null; if (count($toUpdate) > 1) { $this->_mysqli_noda->commit(); $this->_mysqli_noda->autocommit(true); } } /** * Sync: Wrapper function glueing together the sync process. * * @return void */ public function sync():void { $this->removeObsolete(); $this->update(); } /** * Constructor. * * @param MDMysqli $mysqli_noda DB connection. * * @return void */ public function __construct(MDMysqli $mysqli_noda) { $this->_mysqli_noda = $mysqli_noda; } }