/** * Set up filters based on VuFind settings. * * @param ParamBag $params Parameter collection to update * * @return void */ public function createBackendFilterParameters(ParamBag $params) { // flag our non-Standard checkbox filters: $foundIncludeNewspapers = false; # includeNewspapers $foundIncludeWithoutFulltext = false; # includeWithoutFulltext $filterList = $this->getFilterList(); // Which filters should be applied to our query? if (!empty($filterList)) { // Loop through all filters and add appropriate values to request: foreach ($filterList as $filterArray) { foreach ($filterArray as $filt) { $safeValue = SummonQuery::escapeParam($filt['value']); if ($filt['field'] == 'holdingsOnly') { // Special case -- "holdings only" is a separate parameter from // other facets. $params->set('holdings', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'excludeNewspapers') { // support a checkbox for excluding newspapers: // this is now the default behaviour. } else { if ($filt['field'] == 'includeNewspapers') { // explicitly include newspaper articles $foundIncludeNewspapers = true; } else { if ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = SummonQuery::escapeParam($range['from']); $to = SummonQuery::escapeParam($range['to']); $params->add('rangeFilters', "PublicationDate,{$from}:{$to}"); } else { if ($filt['field'] == 'includeWithoutFulltext') { $foundIncludeWithoutFulltext = true; } else { // Standard case: $params->add('filters', "{$filt['field']},{$safeValue}"); } } } } } } } } // special cases (apply also when filter list is empty) // newspaper articles if (!$foundIncludeNewspapers) { // this actually means: do *not* show newspaper articles $params->add('filters', "ContentType,Newspaper Article,true"); } // combined facet "with holdings/with fulltext" if (!$foundIncludeWithoutFulltext) { $params->set('holdings', true); $params->add('filters', 'IsFullText,true'); } else { $params->set('holdings', false); } }
/** * Create search backend parameters for advanced features. * * @return ParamBag */ public function getBackendParameters() { $backendParams = new ParamBag(); // Spellcheck $backendParams->set('spellcheck', $this->getOptions()->spellcheckEnabled() ? 'true' : 'false'); // Facets $facets = $this->getFacetSettings(); if (!empty($facets)) { $backendParams->add('facet', 'true'); foreach ($facets as $key => $value) { $backendParams->add("facet.{$key}", $value); } $backendParams->add('facet.mincount', 1); } // Filters $filters = $this->getFilterSettings(); foreach ($filters as $filter) { $backendParams->add('fq', $filter); } // Shards $allShards = $this->getOptions()->getShards(); $shards = $this->getSelectedShards(); if (empty($shards)) { $shards = array_keys($allShards); } // If we have selected shards, we need to format them: if (!empty($shards)) { $selectedShards = []; foreach ($shards as $current) { $selectedShards[$current] = $allShards[$current]; } $shards = $selectedShards; $backendParams->add('shards', implode(',', $selectedShards)); } // Sort $sort = $this->getSort(); if ($sort) { $backendParams->add('sort', $this->normalizeSort($sort)); } // Highlighting -- on by default, but we should disable if necessary: if (!$this->getOptions()->highlightEnabled()) { $backendParams->add('hl', 'false'); } // Pivot facets for visual results if ($pf = $this->getPivotFacets()) { $backendParams->add('facet.pivot', $pf); } return $backendParams; }
/** * Create search backend parameters for advanced features. * * @return ParamBag */ public function getBackendParameters() { $backendParams = new ParamBag(); // Spellcheck $backendParams->set('spellcheck', $this->getOptions()->spellcheckEnabled() ? 'true' : 'false'); // Facets $facets = $this->getFacetSettings(); if (!empty($facets)) { $backendParams->add('facet', 'true'); if (isset($facets['indexSortedFacets'])) { foreach ($facets['indexSortedFacets'] as $field) { $backendParams->add("f.{$field}.facet.sort", 'index'); } unset($facets['indexSortedFacets']); } foreach ($facets as $key => $value) { if (substr($key, 0, strlen(self::PER_FIELD_PARAM)) === self::PER_FIELD_PARAM) { $backendParams->add($key, $value); } else { $backendParams->add("facet.{$key}", $value); } } $backendParams->add('facet.mincount', 1); } // Filters $filters = $this->getFilterSettings(); foreach ($filters as $filter) { $backendParams->add('fq', $filter); } // Shards $allShards = $this->getOptions()->getShards(); $shards = $this->getSelectedShards(); if (empty($shards)) { $shards = array_keys($allShards); } // If we have selected shards, we need to format them: if (!empty($shards)) { $selectedShards = []; foreach ($shards as $current) { $selectedShards[$current] = $allShards[$current]; } $shards = $selectedShards; $backendParams->add('shards', implode(',', $selectedShards)); } // Sort $sort = $this->getSort(); if ($sort) { // If we have an empty search with relevance sort, see if there is // an override configured: if ($sort == 'relevance' && $this->getQuery()->getAllTerms() == '' && ($relOv = $this->getOptions()->getEmptySearchRelevanceOverride())) { $sort = $relOv; } $backendParams->add('sort', $this->normalizeSort($sort)); } // Highlighting -- on by default, but we should disable if necessary: if (!$this->getOptions()->highlightEnabled()) { $backendParams->add('hl', 'false'); } // Pivot facets for visual results if ($pf = $this->getPivotFacets()) { $backendParams->add('facet.pivot', $pf); } if (!empty($this->boostFunctions)) { foreach ($this->boostFunctions as $func) { $backendParams->add('boost', $func); } } return $backendParams; }
/** * Check if MetaLib databases are searchable. * * @return \Zend\Http\Response */ public function metalibLinksAjax() { $this->disableSessionWrites(); // avoid session write timing bug $config = $this->getServiceLocator()->get('VuFind\\Config')->get('MetaLib'); if (!isset($config->General->enabled) || !$config->General->enabled) { throw new \Exception('MetaLib is not enabled'); } $auth = $this->serviceLocator->get('ZfcRbac\\Service\\AuthorizationService'); $authorized = $auth->isGranted('finna.authorized'); $query = new Query(); $metalib = $this->getServiceLocator()->get('VuFind\\Search'); $results = []; $ids = $this->getRequest()->getQuery()->get('id'); foreach ($ids as $id) { $backendParams = new ParamBag(); $backendParams->add('irdInfo', [$id]); $result = $metalib->search('MetaLib', $query, false, false, $backendParams); $info = $result->getIRDInfo(); $status = null; if ($info && ($authorized || strcasecmp($info['access'], 'guest') == 0)) { $status = $info['searchable'] ? 'allowed' : 'nonsearchable'; } else { $status = 'denied'; } $results = ['id' => $id, 'status' => $status]; } return $this->output($results, self::STATUS_OK); }
/** * Create search backend parameters for advanced features. * * @return ParamBag */ public function getBackendParameters() { $backendParams = new ParamBag(); // Spellcheck $backendParams->set('spellcheck', $this->getOptions()->spellcheckEnabled() ? 'true' : 'false'); // Facets $facets = $this->getFacetSettings(); if (!empty($facets)) { $backendParams->add('facet', 'true'); foreach ($facets as $key => $value) { // prefix keys with "facet" unless they already have a "f." prefix: $fullKey = substr($key, 0, 2) == 'f.' ? $key : "facet.{$key}"; $backendParams->add($fullKey, $value); } $backendParams->add('facet.mincount', 1); } // Filters $filters = $this->getFilterSettings(); foreach ($filters as $filter) { $backendParams->add('fq', $filter); } // Shards $allShards = $this->getOptions()->getShards(); $shards = $this->getSelectedShards(); if (empty($shards)) { $shards = array_keys($allShards); } // If we have selected shards, we need to format them: if (!empty($shards)) { $selectedShards = []; foreach ($shards as $current) { $selectedShards[$current] = $allShards[$current]; } $shards = $selectedShards; $backendParams->add('shards', implode(',', $selectedShards)); } // Sort $sort = $this->getSort(); if ($sort) { // If we have an empty search with relevance sort, see if there is // an override configured: if ($sort == 'relevance' && $this->getQuery()->getAllTerms() == '' && ($relOv = $this->getOptions()->getEmptySearchRelevanceOverride())) { $sort = $relOv; } $backendParams->add('sort', $this->normalizeSort($sort)); } // Highlighting -- on by default, but we should disable if necessary: if (!$this->getOptions()->highlightEnabled()) { $backendParams->add('hl', 'false'); } // Pivot facets for visual results if ($pf = $this->getPivotFacets()) { $backendParams->add('facet.pivot', $pf); } return $backendParams; }
/** * Returns browser usage statistics * * @param bool $version Include the version numbers in the list * @param int $listLength How many items to return * * @return array */ public function getBrowserStats($version, $listLength = 5) { $query = new Query('*:*'); $params = new ParamBag(); $params->add('fl', 'browser,browserVersion'); $params->add('group', 'true'); $params->add('group.field', 'session'); $start = 0; $limit = 1000; $hashes = []; do { $response = $this->solrBackend->search($query, $start, $limit, $params); $groups = $response->getGroups(); foreach ($groups['session']['groups'] as $group) { if ($version) { // Version specific $browser = $group['doclist']['docs'][0]['browser'] . ' ' . $group['doclist']['docs'][0]['browserVersion']; if (isset($hashes[$browser])) { $hashes[$browser]++; } elseif (count($hashes) < $limit) { $hashes[$browser] = 1; } } else { // Browser name if (isset($hashes[$group['doclist']['docs'][0]['browser']])) { $hashes[$group['doclist']['docs'][0]['browser']]++; } elseif (count($hashes) < $limit) { $hashes[$group['doclist']['docs'][0]['browser']] = 1; } } } $start += $limit; } while (count($groups['session']['groups']) > 0); $solrBrowsers = []; foreach ($hashes as $browser => $count) { $newBrowser = ['browserName' => $browser, 'count' => $count]; // Insert sort (limit to listLength) for ($i = 0; $i < $listLength - 1 && $i < count($solrBrowsers); $i++) { if ($count > $solrBrowsers[$i]['count']) { // Insert in order array_splice($solrBrowsers, $i, 0, [$newBrowser]); continue 2; // Skip the append after this loop } } if (count($solrBrowsers) < $listLength) { $solrBrowsers[] = $newBrowser; } } return $solrBrowsers; }
/** * Set up expanders based on VuFind settings. * * @param ParamBag $params Parameter collection to update * * @return void */ public function createBackendExpanderParameters(ParamBag $params) { // Which filters should be applied to our query? if (!empty($this->expanders)) { // Loop through all filters and add appropriate values to request: $value = ''; foreach ($this->expanders as $expander) { $value = !empty($value) ? $value . ',' . $expander : $expander; } if (!empty($value)) { $params->add('expander', $value); } } }
/** * Set up filters based on VuFind settings. * * @param ParamBag $params Parameter collection to update * * @return void */ public function createBackendFilterParameters(ParamBag $params) { // Which filters should be applied to our query? $filterList = $this->getFilterList(); if (!empty($filterList)) { $orFacets = []; // Loop through all filters and add appropriate values to request: foreach ($filterList as $filterArray) { foreach ($filterArray as $filt) { $safeValue = SummonQuery::escapeParam($filt['value']); // Special case -- "holdings only" is a separate parameter from // other facets. if ($filt['field'] == 'holdingsOnly') { $params->set('holdings', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'queryExpansion') { // Special case -- "query expansion" is a separate parameter // from other facets. $params->set('expand', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'excludeNewspapers') { // Special case -- support a checkbox for excluding // newspapers: $params->add('filters', "ContentType,Newspaper Article,true"); } else { if ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = SummonQuery::escapeParam($range['from']); $to = SummonQuery::escapeParam($range['to']); $params->add('rangeFilters', "{$filt['field']},{$from}:{$to}"); } else { if ($filt['operator'] == 'OR') { // Special case -- OR facets: $orFacets[$filt['field']] = isset($orFacets[$filt['field']]) ? $orFacets[$filt['field']] : []; $orFacets[$filt['field']][] = $safeValue; } else { // Standard case: $fq = "{$filt['field']},{$safeValue}"; if ($filt['operator'] == 'NOT') { $fq .= ',true'; } $params->add('filters', $fq); } } } } } } // Deal with OR facets: foreach ($orFacets as $field => $values) { $params->add('groupFilters', $field . ',or,' . implode(',', $values)); } } } }
/** * Gathers data for the view of the AlphaBrowser and does some initialization * * @return \Zend\View\Model\ViewModel */ public function homeAction() { $config = $this->getConfig(); // Load browse types from config file, or use defaults if unavailable: if (isset($config->AlphaBrowse_Types) && !empty($config->AlphaBrowse_Types)) { $types = []; foreach ($config->AlphaBrowse_Types as $key => $value) { $types[$key] = $value; } } else { $types = ['topic' => 'By Topic', 'author' => 'By Author', 'title' => 'By Title', 'lcc' => 'By Call Number']; } // Load any extras from config file $extras = []; if (isset($config->AlphaBrowse_Extras)) { foreach ($config->AlphaBrowse_Extras as $key => $value) { $extras[$key] = $value; } } else { $extras = ['title' => 'author:format:publishDate', 'lcc' => 'title', 'dewey' => 'title']; } // Load remaining config parameters $rows_before = isset($config->AlphaBrowse->rows_before) && is_numeric($config->AlphaBrowse->rows_before) ? (int) $config->AlphaBrowse->rows_before : 0; $highlighting = isset($config->AlphaBrowse->highlighting) ? $config->AlphaBrowse->highlighting : false; $limit = isset($config->AlphaBrowse->page_size) && is_numeric($config->AlphaBrowse->page_size) ? (int) $config->AlphaBrowse->page_size : 20; // Connect to Solr: $db = $this->getServiceLocator()->get('VuFind\\Search\\BackendManager')->get('Solr'); // Process incoming parameters: $source = $this->params()->fromQuery('source', false); $from = $this->params()->fromQuery('from', false); $page = intval($this->params()->fromQuery('page', 0)); // Special case: highlighting is pointless if there's no user input: if (empty($from)) { $highlighting = false; } // Set up any extra parameters to pass $extraParams = new ParamBag(); if (isset($extras[$source])) { $extraParams->add('extras', $extras[$source]); } // Create view model: $view = $this->createViewModel(); // If required parameters are present, load results: if ($source && $from !== false) { // Load Solr data or die trying: $result = $db->alphabeticBrowse($source, $from, $page, $limit, $extraParams, 0 - $rows_before); // No results? Try the previous page just in case we've gone past // the end of the list.... if ($result['Browse']['totalCount'] == 0) { $page--; $result = $db->alphabeticBrowse($source, $from, $page, $limit, $extraParams, 0); if ($highlighting) { $view->highlight_end = true; } } // Only display next/previous page links when applicable: if ($result['Browse']['totalCount'] > $limit) { $view->nextpage = $page + 1; } if ($result['Browse']['offset'] + $result['Browse']['startRow'] > 1) { $view->prevpage = $page - 1; } $view->result = $result; } // set up highlighting: page 0 contains match location if ($highlighting && $page == 0) { $startRow = $result['Browse']['startRow']; // solr counts rows from 1; adjust to array position style $startRow_adj = $startRow - 1; $offset = $result['Browse']['offset']; $totalRows = $result['Browse']['totalCount']; $totalRows += $startRow + $offset > 0 ? $startRow_adj + $offset : 0; // normal case: somewhere in the middle of the browse list $highlight_row = $rows_before; // special case: match row is < rows_before (i.e. at beginning of list) if ($startRow_adj < $rows_before) { $highlight_row = $startRow_adj; } // special case: we've gone past the end // only the rows_before records will have been returned if ($startRow > $totalRows) { $view->highlight_end = true; } $view->highlight_row = $highlight_row; $view->match_type = $result['Browse']['matchType']; } $view->alphaBrowseTypes = $types; $view->from = $from; $view->source = $source; // Pass information about extra columns on to theme $view->extras = isset($extras[$source]) ? explode(':', $extras[$source]) : []; return $view; }
/** * Return SOLR search parameters based on a user query and params. * * @param AbstractQuery $query User query * * @return ParamBag */ public function build(AbstractQuery $query) { $params = new ParamBag(); // Add spelling query if applicable -- note that we mus set this up before // we process the main query in order to avoid unwanted extra syntax: if ($this->createSpellingQuery) { $params->set('spellcheck.q', $query->getAllTerms()); } if ($query instanceof QueryGroup) { $query = $this->reduceQueryGroup($query); } else { $query->setString($this->getLuceneHelper()->normalizeSearchString($query->getString())); } $string = $query->getString() ?: '*:*'; if ($handler = $this->getSearchHandler($query->getHandler(), $string)) { if (!$handler->hasExtendedDismax() && $this->getLuceneHelper()->containsAdvancedLuceneSyntax($string)) { $newString = $this->createAdvancedInnerSearchString($string, $handler); if ($handler->hasDismax()) { $oldString = $newString; $newString = $handler->createBoostQueryString($newString); // If a boost was added, we don't want to highlight based on // the boost query, so we should use the non-boosted version: if ($this->createHighlightingQuery && $oldString != $string) { $params->set('hl.q', $oldString); } if ($string == '*:*') { $params->set('hl.q', '*:*'); } } $string = $newString; } else { if ($handler->hasDismax()) { $params->set('qf', implode(' ', $handler->getDismaxFields())); $params->set('qt', $handler->getDismaxHandler()); foreach ($handler->getDismaxParams() as $param) { $params->add(reset($param), next($param)); } if ($handler->hasFilterQuery()) { $params->add('fq', $handler->getFilterQuery()); } } else { $string = $handler->createSimpleQueryString($string); } } } $params->set('q', $string); return $params; }
/** * Perform a search with clean params * * @param ParamBag $paramBag The params you'd normally send to solr * * @return json */ protected function search($paramBag) { // Remove global filters from the Solr connector $map = $this->solr->getMap(); $params = $map->getParameters('select', 'appends'); $map->setParameters('select', 'appends', []); // Turn off grouping $paramBag->set('group', 'false'); $paramBag->add('wt', 'json'); // Search $response = $this->solr->search($paramBag); // Reapply the global filters $map->setParameters('select', 'appends', $params->getArrayCopy()); return $response; }
/** * Return SOLR search parameters based on a user query and params. * * @param AbstractQuery $query User query * * @return ParamBag */ public function build(AbstractQuery $query) { $params = new ParamBag(); // Add spelling query if applicable -- note that we must set this up before // we process the main query in order to avoid unwanted extra syntax: if ($this->createSpellingQuery) { $params->set('spellcheck.q', $this->getLuceneHelper()->extractSearchTerms($query->getAllTerms())); } if ($query instanceof QueryGroup) { $query = $this->reduceQueryGroup($query); } else { $query->setString($this->getLuceneHelper()->normalizeSearchString($query->getString())); } $string = $query->getString() ?: '*:*'; if ($handler = $this->getSearchHandler($query->getHandler(), $string)) { if (!$handler->hasExtendedDismax() && $this->getLuceneHelper()->containsAdvancedLuceneSyntax($string)) { $string = $this->createAdvancedInnerSearchString($string, $handler); if ($handler->hasDismax()) { $oldString = $string; $string = $handler->createBoostQueryString($string); // If a boost was added, we don't want to highlight based on // the boost query, so we should use the non-boosted version: if ($this->createHighlightingQuery && $oldString != $string) { $params->set('hl.q', $oldString); } } } else { if ($handler->hasDismax()) { // If we're using extended dismax, we'll miss out on the question // mark fix in createAdvancedInnerSearchString(), so we should // apply it here. If other query munges arise that are valuable // to both dismax and edismax, we should add a wrapper function // around them and call it from here instead of this one very // specific check. $string = $this->fixTrailingQuestionMarks($string); $params->set('qf', implode(' ', $handler->getDismaxFields())); $params->set('qt', $handler->getDismaxHandler()); foreach ($handler->getDismaxParams() as $param) { $params->add(reset($param), next($param)); } if ($handler->hasFilterQuery()) { $params->add('fq', $handler->getFilterQuery()); } } else { $string = $handler->createSimpleQueryString($string); } } } $params->set('q', $string); return $params; }
/** * Add user institutions as facet queries to backend params * * @param ParamBag $backendParams * * @return ParamBag */ protected function addUserInstitutions(ParamBag $backendParams) { /** @var Manager $favoritesManger */ $favoritesManger = $this->getServiceLocator()->get('Swissbib\\FavoriteInstitutions\\Manager'); /** @var String[] $favoriteInstitutions */ $favoriteInstitutions = $favoritesManger->getUserInstitutions(); if (sizeof($favoriteInstitutions) > 0) { //facet parameter has to be true in case it's false $backendParams->set("facet", "true"); foreach ($favoriteInstitutions as $institutionCode) { //GH 19.12.2014: use configuration for index name //more investigation for a better solution necessary $backendParams->add("facet.query", "mylibrary:" . $institutionCode); //$backendParams->add("bq", "institution:" . $institutionCode . "^5000"); } } return $backendParams; }
/** * Support function for assigning MetaLib search sets to a view. * * @param \Zend\View\Model\View\Model $view View * @param \VuFind\Search\Query\Query $query Query * * @return $view */ protected function initSets($view, $query) { $allowedSets = $this->getMetaLibSets(); $sets = []; foreach ($allowedSets as $key => $set) { $sets[$key] = $set['name']; } $view->sets = $sets; list($isIrd, $set) = $this->getMetaLibSet($this->getRequest()->getQuery()->get('set')); $view->currentSet = $set; $session = new SessionContainer('MetaLib'); if ($isIrd) { $metalib = $this->getServiceLocator()->get('VuFind\\Search'); $backendParams = new ParamBag(); $backendParams->add('irdInfo', explode(',', substr($set, 5))); $result = $metalib->search('MetaLib', $query, false, false, $backendParams); $info = $result->getIRDInfo(); $name = $info ? $info['name'] : $set; if (!isset($session->recentSets)) { $session->recentSets = []; } unset($session->recentSets[$set]); $session->recentSets[$set] = $isIrd ? $name : $sets[$set]; } $view->recentSets = isset($session->recentSets) ? array_reverse($session->recentSets) : []; return $view; }