diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index a135c84a7972f2745af77b21a43b37299b282e8e..c098ec11aced19af4b2c6099f9c0bb7797a51cc7 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -228,6 +228,12 @@ $config = array( return $logger; }, 'VuFind\Mailer' => 'VuFind\Mailer\Factory', + 'VuFind\RecordLoader' => function ($sm) { + return new \VuFind\Record\Loader( + $sm->get('VuFind\Search'), + $sm->get('VuFind\RecordDriverPluginManager') + ); + }, 'VuFind\RecordRouter' => function ($sm) { return new \VuFind\Record\Router( $sm->get('VuFind\RecordLoader'), @@ -239,6 +245,17 @@ $config = array( $sm->get('VuFind\Config')->get('config') ); }, + 'VuFind\Search\BackendManager' => function ($sm) { + $config = $sm->get('config'); + $smConfig = new \Zend\ServiceManager\Config( + $config['vufind']['plugin_managers']['search_backend'] + ); + $registry = $sm->createScopedServiceManager(); + $smConfig->configureServiceManager($registry); + $manager = new \VuFind\Search\BackendManager($registry); + + return $manager; + }, 'VuFind\SearchSpecsReader' => function ($sm) { return new \VuFind\Config\SearchSpecsReader( $sm->get('VuFind\CacheManager') @@ -288,20 +305,8 @@ $config = array( ? $config->WorldCat->id : false; return new \VuFind\Connection\WorldCatUtils($wcId); }, - 'VuFind\Search\BackendManager' => function ($sm) { - $config = $sm->get('config'); - $smConfig = new \Zend\ServiceManager\Config( - $config['vufind']['plugin_managers']['search_backend'] - ); - $registry = $sm->createScopedServiceManager(); - $smConfig->configureServiceManager($registry); - $manager = new \VuFind\Search\BackendManager($registry); - - return $manager; - }, ), 'invokables' => array( - 'VuFind\RecordLoader' => 'VuFind\Record\Loader', 'VuFind\SessionManager' => 'Zend\Session\SessionManager', ), 'initializers' => array( @@ -792,10 +797,13 @@ $config = array( 'WorldCat' => 'VuFind\Search\Factory\WorldCatBackendFactory', ), 'aliases' => array( + // Allow Solr core names to be used as aliases for services: 'authority' => 'SolrAuth', 'biblio' => 'Solr', 'reserves' => 'SolrReserves', 'stats' => 'SolrStats', + // Legacy: + 'VuFind' => 'Solr', ) ), 'search_options' => array( diff --git a/module/VuFind/src/VuFind/Record/Loader.php b/module/VuFind/src/VuFind/Record/Loader.php index 9c6ae70f2921ce923879c43c1d1df959dbbc312a..e5a7c1cff7dd2d449ef746e9afb7113ac661de6b 100644 --- a/module/VuFind/src/VuFind/Record/Loader.php +++ b/module/VuFind/src/VuFind/Record/Loader.php @@ -26,8 +26,9 @@ * @link http://vufind.org Main Site */ namespace VuFind\Record; -use Zend\ServiceManager\ServiceLocatorAwareInterface, - Zend\ServiceManager\ServiceLocatorInterface; +use VuFind\Exception\RecordMissing as RecordMissingException, + VuFind\RecordDriver\PluginManager as RecordFactory, + VuFindSearch\Service as SearchService; /** * Record loader @@ -38,32 +39,33 @@ use Zend\ServiceManager\ServiceLocatorAwareInterface, * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org Main Site */ -class Loader implements ServiceLocatorAwareInterface +class Loader { /** - * Service locator + * Record factory * - * @var ServiceLocatorInterface + * @var RecordFactory */ - protected $serviceLocator; + protected $recordFactory; /** - * Given a record source, return the search object that can load that type of - * record. + * Search service * - * @param string $source Record source + * @var SearchService + */ + protected $searchService; + + /** + * Constructor * - * @throws \Exception - * @return \VuFind\Search\Base\Results + * @param SearchService $searchService Search service + * @param RecordFactory $recordFactory Record loader */ - protected function getClassForSource($source) - { - // Legacy hack: translate "VuFind" source from database into "Solr": - if ($source == 'VuFind') { - $source = 'Solr'; - } - return $this->getServiceLocator()->get('VuFind\SearchResultsPluginManager') - ->get($source); + public function __construct(SearchService $searchService, + RecordFactory $recordFactory + ) { + $this->searchService = $searchService; + $this->recordFactory = $recordFactory; } /** @@ -77,7 +79,13 @@ class Loader implements ServiceLocatorAwareInterface */ public function load($id, $source = 'VuFind') { - return $this->getClassForSource($source)->getRecord($id); + $results = $this->searchService->retrieve($source, $id)->getRecords(); + if (count($results) < 1) { + throw new RecordMissingException( + 'Record ' . $source . ':' . $id . ' does not exist.' + ); + } + return $results[0]; } /** @@ -92,7 +100,7 @@ class Loader implements ServiceLocatorAwareInterface */ public function loadBatchForSource($ids, $source = 'VuFind') { - return $this->getClassForSource($source)->getRecords($ids); + return $this->searchService->retrieveBatch($source, $ids)->getRecords(); } /** @@ -143,9 +151,7 @@ class Loader implements ServiceLocatorAwareInterface $fields = isset($details['extra_fields']) ? $details['extra_fields'] : array(); $fields['id'] = $details['id']; - $factory = $this->getServiceLocator() - ->get('VuFind\RecordDriverPluginManager'); - $retVal[$i] = $factory->get('Missing'); + $retVal[$i] = $this->recordFactory->get('Missing'); $retVal[$i]->setRawData($fields); $retVal[$i]->setSourceIdentifier($details['source']); } @@ -155,27 +161,4 @@ class Loader implements ServiceLocatorAwareInterface ksort($retVal); return $retVal; } - - /** - * Set the service locator. - * - * @param ServiceLocatorInterface $serviceLocator Locator to register - * - * @return Manager - */ - public function setServiceLocator(ServiceLocatorInterface $serviceLocator) - { - $this->serviceLocator = $serviceLocator; - return $this; - } - - /** - * Get the service locator. - * - * @return \Zend\ServiceManager\ServiceLocatorInterface - */ - public function getServiceLocator() - { - return $this->serviceLocator; - } } diff --git a/module/VuFind/src/VuFind/Search/Base/Results.php b/module/VuFind/src/VuFind/Search/Base/Results.php index c8e707a8398973d9b9b69ed9f5f83df754e65845..21d65f4495be8b1cf31845aaf04a3eb82c769682 100644 --- a/module/VuFind/src/VuFind/Search/Base/Results.php +++ b/module/VuFind/src/VuFind/Search/Base/Results.php @@ -196,43 +196,6 @@ abstract class Results implements ServiceLocatorAwareInterface */ abstract protected function performSearch(); - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @return \VuFind\RecordDriver\AbstractBase - */ - public function getRecord($id) - { - // This needs to be defined in subclasses: - throw new \Exception('getRecord needs to be defined.'); - } - - /** - * Method to retrieve an array of records by ID. - * - * @param array $ids Array of unique record identifiers. - * - * @return array - */ - public function getRecords($ids) - { - // This is the default, dumb behavior for retrieving multiple records -- - // just call getRecord() repeatedly. For efficiency, this method should - // be overridden in child classes when possible to reduce API calls, etc. - $retVal = array(); - foreach ($ids as $id) { - try { - $retVal[] = $this->getRecord($id); - } catch (\Exception $e) { - // Just omit missing records from the return array; calling code in - // \VuFind\Record\Loader::loadBatch() will deal with this. - } - } - return $retVal; - } - /** * Get spelling suggestion information. * diff --git a/module/VuFind/src/VuFind/Search/EmptySet/Results.php b/module/VuFind/src/VuFind/Search/EmptySet/Results.php index ac39d259255f965a0cb7220507204589d41b47f4..0fa94845a42ff4702ea1be0f170f8be3478d6aae 100644 --- a/module/VuFind/src/VuFind/Search/EmptySet/Results.php +++ b/module/VuFind/src/VuFind/Search/EmptySet/Results.php @@ -63,17 +63,4 @@ class Results extends BaseResults { return array(); } - - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @throws \Exception - * @return \VuFind\RecordDriver\AbstractBase - */ - public function getRecord($id) - { - throw new \Exception('Cannot get record from empty set.'); - } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Search/Favorites/Results.php b/module/VuFind/src/VuFind/Search/Favorites/Results.php index 9873bdfbc23789fcd89c929da95b5df1064207e2..1fa6449873363c7fc98e694675e0e84b2ad55d3d 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/Results.php +++ b/module/VuFind/src/VuFind/Search/Favorites/Results.php @@ -172,20 +172,6 @@ class Results extends BaseResults ->loadBatch($recordsToRequest); } - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - throw new \Exception( - 'getRecord not supported by VuFind\Search\Favorites\Results' - ); - } - /** * Get an array of tags being applied as filters. * diff --git a/module/VuFind/src/VuFind/Search/MixedList/Results.php b/module/VuFind/src/VuFind/Search/MixedList/Results.php index 60184a73c72d23828667913c1a55bcda0d0b2580..0b06e65d382ebb50ebbe123a7eae8a482edc8d13 100644 --- a/module/VuFind/src/VuFind/Search/MixedList/Results.php +++ b/module/VuFind/src/VuFind/Search/MixedList/Results.php @@ -66,18 +66,4 @@ class Results extends BaseResults ->loadBatch($recordsToRequest); $this->resultTotal = count($this->results); } - - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - throw new \Exception( - 'getRecord not supported by VuFind\Search\MixedList\Results' - ); - } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Search/Solr/Results.php b/module/VuFind/src/VuFind/Search/Solr/Results.php index 304a6f367f06e1e45e148327e6e720fd45797f83..eacd6b139b55f018a016485d2dcfdf02119b6955 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Results.php +++ b/module/VuFind/src/VuFind/Search/Solr/Results.php @@ -414,59 +414,6 @@ class Results extends BaseResults return $list; } - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @throws RecordMissingException - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - $collection = $this->getSearchService()->retrieve($this->backendId, $id); - - if (count($collection) == 0) { - throw new RecordMissingException( - 'Record ' . $id . ' does not exist.' - ); - } - - return current($collection->getRecords()); - } - - /** - * Method to retrieve an array of records by ID. - * - * @param array $ids Array of unique record identifiers. - * - * @return array - */ - public function getRecords($ids) - { - // Figure out how many records to retrieve at the same time -- - // we'll use either 100 or the ID request limit, whichever is smaller. - $results = $this->getServiceLocator() - ->get('VuFind\SearchResultsPluginManager')->get('Solr'); - $params = $results->getParams(); - $pageSize = $params->getQueryIDLimit(); - if ($pageSize < 1 || $pageSize > 100) { - $pageSize = 100; - } - $params->setLimit($pageSize); - - // Retrieve records a page at a time: - $retVal = array(); - while (count($ids) > 0) { - $currentPage = array_splice($ids, 0, $pageSize, array()); - $params->setQueryIDs($currentPage); - $results->performAndProcessSearch(); - $retVal = array_merge($retVal, $results->getResults()); - } - - return $retVal; - } - /** * Method to retrieve records similar to the provided ID. Returns an * array of record driver objects. diff --git a/module/VuFind/src/VuFind/Search/Summon/Results.php b/module/VuFind/src/VuFind/Search/Summon/Results.php index 7fb5ea9ffecd991578946a6124a3278c888c1c0d..87a769d115cc5f832d27ecb3a3abdc3db0d68b06 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Results.php +++ b/module/VuFind/src/VuFind/Search/Summon/Results.php @@ -107,27 +107,6 @@ class Results extends BaseResults $this->results = $collection->getRecords(); } - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @throws RecordMissingException - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - $collection = $this->getSearchService()->retrieve('Summon', $id); - - if (count($collection) == 0) { - throw new RecordMissingException( - 'Record ' . $id . ' does not exist.' - ); - } - - return current($collection->getRecords()); - } - /** * Create search backend parameters for advanced features. * diff --git a/module/VuFind/src/VuFind/Search/Tags/Results.php b/module/VuFind/src/VuFind/Search/Tags/Results.php index 0d9c5c0cb688a033cd072259977b88742a2fb001..8cbdeed777e0060a48239c445536d0caf169a277 100644 --- a/module/VuFind/src/VuFind/Search/Tags/Results.php +++ b/module/VuFind/src/VuFind/Search/Tags/Results.php @@ -77,20 +77,6 @@ class Results extends BaseResults ->loadBatch($recordsToRequest); } - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - throw new \Exception( - 'getRecord not supported by VuFind\Search\Tags\Results' - ); - } - /** * Returns the stored list of facets for the last search * diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Results.php b/module/VuFind/src/VuFind/Search/WorldCat/Results.php index 3546555e9a2dbb519534c9517a412014de8d764a..349f5403f5415fed7ac2d911bf7443040ff4d461 100644 --- a/module/VuFind/src/VuFind/Search/WorldCat/Results.php +++ b/module/VuFind/src/VuFind/Search/WorldCat/Results.php @@ -60,27 +60,6 @@ class Results extends \VuFind\Search\Base\Results $this->results = $collection->getRecords(); } - /** - * Method to retrieve a record by ID. Returns a record driver object. - * - * @param string $id Unique identifier of record - * - * @throws RecordMissingException - * @return \VuFind\RecordDriver\Base - */ - public function getRecord($id) - { - $collection = $this->getSearchService()->retrieve('WorldCat', $id); - - if (count($collection) == 0) { - throw new RecordMissingException( - 'Record ' . $id . ' does not exist.' - ); - } - - return current($collection->getRecords()); - } - /** * Returns the stored list of facets for the last search * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php index c17e15286bc257feb8e7b86b4e75446f1af5c1dd..e7930938becdae8ed17218407dd0ba7b25ae9d88 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php @@ -28,6 +28,7 @@ namespace VuFindSearch\Backend\Solr; use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Query\Query; use VuFindSearch\ParamBag; @@ -38,6 +39,7 @@ use VuFindSearch\Backend\Solr\Response\Json\Terms; use VuFindSearch\Backend\BackendInterface; use VuFindSearch\Feature\MoreLikeThis; +use VuFindSearch\Feature\RetrieveBatchInterface; use Zend\Log\LoggerInterface; @@ -55,7 +57,7 @@ use VuFindSearch\Exception\InvalidArgumentException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org */ -class Backend implements BackendInterface, MoreLikeThis +class Backend implements BackendInterface, MoreLikeThis, RetrieveBatchInterface { /** * Record collection factory. @@ -215,6 +217,44 @@ class Backend implements BackendInterface, MoreLikeThis return $collection; } + /** + * Retrieve a batch of documents. + * + * @param array $ids Array of document identifiers + * @param ParamBag $params Search backend parameters + * + * @return \VuFindSearch\Response\RecordCollectionInterface + */ + public function retrieveBatch($ids, ParamBag $params = null) + { + // Load 100 records at a time; this is a good number to avoid memory + // problems while still covering a lot of ground. + $pageSize = 100; + + // Callback function for formatting IDs: + $formatIds = function ($i) { + return '"' . addcslashes($i, '"') . '"'; + }; + + // Retrieve records a page at a time: + $results = false; + while (count($ids) > 0) { + $currentPage = array_splice($ids, 0, $pageSize, array()); + $currentPage = array_map($formatIds, $currentPage); + $query = new Query('id:(' . implode(' OR ', $currentPage) . ')'); + $next = $this->search($query, 0, $pageSize); + if (!$results) { + $results = $next; + } else { + foreach ($next->getRecords() as $record) { + $results->add($record); + } + } + } + + return $results; + } + /** * Return similar records. * diff --git a/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..75704dda17e940cf20109de8d0414b46ff2d5555 --- /dev/null +++ b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php @@ -0,0 +1,58 @@ +<?php + +/** + * Optional backend feature: retrieve batch of records. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ + +namespace VuFindSearch\Feature; + +use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Query\QueryGroup; +use VuFindSearch\Query\Query; + +use VuFindSearch\ParamBag; + +/** + * Optional backend feature: retrieve batch of records. + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ +interface RetrieveBatchInterface +{ + /** + * Retrieve a batch of documents. + * + * @param array $ids Array of document identifiers + * @param ParamBag $params Search backend parameters + * + * @return \VuFindSearch\Response\RecordCollectionInterface + */ + public function retrieveBatch($ids, ParamBag $params = null); +} \ No newline at end of file diff --git a/module/VuFindSearch/src/VuFindSearch/Service.php b/module/VuFindSearch/src/VuFindSearch/Service.php index f4ae33d5b28a6389ab6016622907990cebded323..99bd058f21804d287af1aaa50112c7fe6bdb519b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Service.php +++ b/module/VuFindSearch/src/VuFindSearch/Service.php @@ -30,6 +30,7 @@ namespace VuFindSearch; use VuFindSearch\Backend\BackendInterface; +use VuFindSearch\Feature\RetrieveBatchInterface; use Zend\Log\LoggerInterface; use Zend\EventManager\EventManagerInterface; @@ -127,6 +128,46 @@ class Service return $response; } + /** + * Retrieve a batch of records. + * + * @param string $backend Search backend identifier + * @param array $ids Record identifier + * @param ParamBag $params Search backend parameters + * + * @return ResponseInterface + */ + public function retrieveBatch($backend, $ids, ParamBag $params = null) + { + $params = $params ?: new ParamBag(); + $context = __FUNCTION__; + $args = compact('backend', 'id', 'params', 'context'); + $backend = $this->resolve($backend, $args); + $args['backend_instance'] = $backend; + + $this->triggerPre($backend, $args); + + // If the backend implements the RetrieveBatchInterface, we can load + // all the records at once; otherwise, we need to load them one at a + // time and aggregate them: + if ($backend instanceof RetrieveBatchInterface) { + $response = $backend->retrieveBatch($ids, $params); + } else { + $response = false; + foreach ($ids as $id) { + $next = $backend->retrieve($id, $params); + if (!$response) { + $response = $next; + } else { + $response->add($next->first()); + } + } + } + + $this->triggerPost($response, $args); + return $response; + } + /** * Return similar records. *