Skip to content
Snippets Groups Projects
Commit ca966ebc authored by Demian Katz's avatar Demian Katz
Browse files

Refactor record loader to use helper classes/methods.

- Improves code clarity.
parent fcfe5953
No related merge requests found
...@@ -71,9 +71,9 @@ class Loader implements \Zend\Log\LoggerAwareInterface ...@@ -71,9 +71,9 @@ class Loader implements \Zend\Log\LoggerAwareInterface
/** /**
* Constructor * Constructor
* *
* @param SearchService $searchService Search service * @param SearchService $searchService Search service
* @param RecordFactory $recordFactory Record loader * @param RecordFactory $recordFactory Record loader
* @param Cache $recordCache Record Cache * @param Cache $recordCache Record Cache
*/ */
public function __construct(SearchService $searchService, public function __construct(SearchService $searchService,
RecordFactory $recordFactory, Cache $recordCache = null RecordFactory $recordFactory, Cache $recordCache = null
...@@ -145,25 +145,23 @@ class Loader implements \Zend\Log\LoggerAwareInterface ...@@ -145,25 +145,23 @@ class Loader implements \Zend\Log\LoggerAwareInterface
public function loadBatchForSource($ids, $source = DEFAULT_SEARCH_BACKEND, public function loadBatchForSource($ids, $source = DEFAULT_SEARCH_BACKEND,
$tolerateBackendExceptions = false $tolerateBackendExceptions = false
) { ) {
$list = new Checklist($ids);
$cachedRecords = []; $cachedRecords = [];
if (null !== $this->recordCache && $this->recordCache->isPrimary($source)) { if (null !== $this->recordCache && $this->recordCache->isPrimary($source)) {
// Try to load records from cache if source is cachable // Try to load records from cache if source is cachable
$cachedRecords = $this->recordCache->lookupBatch($ids, $source); $cachedRecords = $this->recordCache->lookupBatch($ids, $source);
// Check which records could not be loaded from the record cache // Check which records could not be loaded from the record cache
foreach ($cachedRecords as $cachedRecord) { foreach ($cachedRecords as $cachedRecord) {
$key = array_search($cachedRecord->getUniqueId(), $ids); $list->check($cachedRecord->getUniqueId());
if ($key !== false) {
unset($ids[$key]);
}
} }
} }
// Try to load the uncached records from the original $source // Try to load the uncached records from the original $source
$genuineRecords = []; $genuineRecords = [];
if (!empty($ids)) { if ($list->hasUnchecked()) {
try { try {
$genuineRecords = $this->searchService->retrieveBatch($source, $ids) $genuineRecords = $this->searchService
->getRecords(); ->retrieveBatch($source, $list->getUnchecked())->getRecords();
} catch (\VuFindSearch\Backend\Exception\BackendException $e) { } catch (\VuFindSearch\Backend\Exception\BackendException $e) {
if (!$tolerateBackendExceptions) { if (!$tolerateBackendExceptions) {
throw $e; throw $e;
...@@ -175,18 +173,16 @@ class Loader implements \Zend\Log\LoggerAwareInterface ...@@ -175,18 +173,16 @@ class Loader implements \Zend\Log\LoggerAwareInterface
} }
foreach ($genuineRecords as $genuineRecord) { foreach ($genuineRecords as $genuineRecord) {
$key = array_search($genuineRecord->getUniqueId(), $ids); $list->check($genuineRecord->getUniqueId());
if ($key !== false) {
unset($ids[$key]);
}
} }
} }
if (!empty($ids) && null !== $this->recordCache if ($list->hasUnchecked() && null !== $this->recordCache
&& $this->recordCache->isFallback($source) && $this->recordCache->isFallback($source)
) { ) {
// Try to load missing records from cache if source is cachable // Try to load missing records from cache if source is cachable
$cachedRecords = $this->recordCache->lookupBatch($ids, $source); $cachedRecords = $this->recordCache
->lookupBatch($list->getUnchecked(), $source);
} }
// Merge records found in cache and records loaded from original $source // Merge records found in cache and records loaded from original $source
...@@ -198,6 +194,24 @@ class Loader implements \Zend\Log\LoggerAwareInterface ...@@ -198,6 +194,24 @@ class Loader implements \Zend\Log\LoggerAwareInterface
return $retVal; return $retVal;
} }
/**
* Build a "missing record" driver.
*
* @param array $details Associative array of record details (from a
* SourceAndIdList)
*
* @return \VuFind\RecordDriver\Missing
*/
protected function buildMissingRecord($details)
{
$fields = $details['extra_fields'] ?? [];
$fields['id'] = $details['id'];
$record = $this->recordFactory->get('Missing');
$record->setRawData($fields);
$record->setSourceIdentifier($details['source']);
return $record;
}
/** /**
* Given an array of associative arrays with id and source keys (or pipe- * Given an array of associative arrays with id and source keys (or pipe-
* separated source|id strings), load all of the requested records in the * separated source|id strings), load all of the requested records in the
...@@ -217,48 +231,28 @@ class Loader implements \Zend\Log\LoggerAwareInterface ...@@ -217,48 +231,28 @@ class Loader implements \Zend\Log\LoggerAwareInterface
*/ */
public function loadBatch($ids, $tolerateBackendExceptions = false) public function loadBatch($ids, $tolerateBackendExceptions = false)
{ {
// Sort the IDs by source -- we'll create an associative array indexed by // Create a SourceAndIdList object to help sort the IDs by source:
// source and record ID which points to the desired position of the indexed $list = new SourceAndIdList($ids);
// record in the final return array:
$idBySource = [];
foreach ($ids as $i => $details) {
// Convert source|id string to array if necessary:
if (!is_array($details)) {
$parts = explode('|', $details, 2);
$ids[$i] = $details = [
'source' => $parts[0], 'id' => $parts[1]
];
}
$idBySource[$details['source']][$details['id']] = $i;
}
// Retrieve the records and put them back in order: // Retrieve the records and put them back in order:
$retVal = []; $retVal = [];
foreach ($idBySource as $source => $details) { foreach ($list->getIdsBySource() as $source => $currentIds) {
$records = $this->loadBatchForSource( $records = $this->loadBatchForSource(
array_keys($details), $source, $tolerateBackendExceptions $currentIds, $source, $tolerateBackendExceptions
); );
foreach ($records as $current) { foreach ($records as $current) {
$id = $current->getUniqueId(); $position = $list->getRecordPosition($current);
// In theory, we should be able to assume that $details[$id] is if ($position !== false) {
// set... but in practice, we can't make that assumption. In some $retVal[$position] = $current;
// cases, Summon IDs will change, and requests for an old ID value
// will return a record with a different ID.
if (isset($details[$id])) {
$retVal[$details[$id]] = $current;
} }
} }
} }
// Check for missing records and fill gaps with \VuFind\RecordDriver\Missing // Check for missing records and fill gaps with \VuFind\RecordDriver\Missing
// objects: // objects:
foreach ($ids as $i => $details) { foreach ($list->getAll() as $i => $details) {
if (!isset($retVal[$i]) || !is_object($retVal[$i])) { if (!isset($retVal[$i]) || !is_object($retVal[$i])) {
$fields = $details['extra_fields'] ?? []; $retVal[$i] = $this->buildMissingRecord($details);
$fields['id'] = $details['id'];
$retVal[$i] = $this->recordFactory->get('Missing');
$retVal[$i]->setRawData($fields);
$retVal[$i]->setSourceIdentifier($details['source']);
} }
} }
......
<?php
/**
* Record ID list (support class for Loader)
*
* PHP version 7
*
* Copyright (C) Villanova University 2018.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Record
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org Main Site
*/
namespace VuFind\Record;
use VuFind\RecordDriver\AbstractBase as Record;
/**
* Record ID list (support class for Loader)
*
* @category VuFind
* @package Record
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org Main Site
*/
class SourceAndIdList
{
/**
* Processed ID data.
*
* @var array
*/
protected $ids = [];
/**
* Record positions in the original list, indexed by source and ID.
*
* @var array
*/
protected $bySource = [];
/**
* Constructor
*
* @param array $ids Array of associative arrays with id/source keys or strings
* in source|id format. In associative array formats, there is also an optional
* "extra_fields" key which can be used to pass in data formatted as if it
* belongs to the Solr schema; this is used to create a mock driver object if
* the real data source is unavailable.
*/
public function __construct($ids)
{
// Sort the IDs by source -- we'll create an associative array indexed by
// source and record ID which points to the desired position of the indexed
// record in the final return array:
foreach ($ids as $i => $details) {
// Convert source|id string to array if necessary:
if (!is_array($details)) {
$parts = explode('|', $details, 2);
$ids[$i] = $details = [
'source' => $parts[0], 'id' => $parts[1]
];
}
$this->bySource[$details['source']][$details['id']] = $i;
}
$this->ids = $ids;
}
/**
* Get the full list of IDs sent to the constructor, normalized to array
* format.
*
* @return array
*/
public function getAll()
{
return $this->ids;
}
/**
* Get an associative source => id list array.
*
* @return array
*/
public function getIdsBySource()
{
return array_map('array_keys', $this->bySource);
}
/**
* If the provided record driver corresponds with an ID in the list, return
* the associated position in the list. Otherwise, return false.
*
* @param Record $record Record
*
* @return int|bool
*/
public function getRecordPosition(Record $record)
{
$id = $record->getUniqueId();
$source = $record->getSourceIdentifier();
// First check if the primary ID is set; in some cases (e.g. Summon),
// the ID may have changed, so also check the prior ID if available.
if (isset($this->bySource[$source][$id])) {
return $this->bySource[$source][$id];
}
$oldId = $record->tryMethod('getPreviousUniqueId');
if ($oldId !== null && isset($this->bySource[$source][$oldId])) {
return $this->bySource[$source][$oldId];
}
return false;
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment